Imports System.Globalization
Imports DevExpress.CodeParser
Imports DevExpress.Data.Controls
Imports DevExpress.XtraPrinting

Public Class ClassFormat
    Public Const CURRENCY_FORMAT = "C2"
    Public Const DECIMAL_FORMAT = "F"
    Public Const DATE_FORMAT = "d"
    Public Const DATETIME_FORMAT = "G"

    Public Shared Function GetFormatString(pFormatString As String) As String
        Select Case pFormatString
            Case ClassControlCreator.CONTROL_TYPE_CURRENCY
                Return CURRENCY_FORMAT

            Case ClassControlCreator.CONTROL_TYPE_DOUBLE
                Return DECIMAL_FORMAT

            Case ClassControlCreator.CONTROL_TYPE_DATE
                Return DATE_FORMAT

            Case ClassControlCreator.CONTROL_TYPE_DATETIME
                Return DATETIME_FORMAT

            Case Else
                Return String.Empty

        End Select
    End Function
    ''' <summary>
    ''' Normalisiert einen numerischen String für die invariante Kultur-Konvertierung.
    ''' Entfernt Tausendertrennzeichen und ersetzt Dezimaltrennzeichen durch Punkt.
    ''' </summary>
    Private Shared Function NormalizeNumericString(pValue As String) As String
        If String.IsNullOrWhiteSpace(pValue) Then
            Return pValue
        End If

        Dim normalized As String = pValue.Trim()

        LOGGER.Debug($"[NormalizeNumericString] Input: [{pValue}]")

        ' Entferne Währungssymbole und Leerzeichen
        normalized = System.Text.RegularExpressions.Regex.Replace(normalized, "[€$£¥\s]", "")

        Dim hasDot As Boolean = normalized.Contains(".")
        Dim hasComma As Boolean = normalized.Contains(",")

        LOGGER.Debug($"[NormalizeNumericString] After cleanup: [{normalized}], HasDot={hasDot}, HasComma={hasComma}")

        If hasDot AndAlso hasComma Then
            ' Beide vorhanden: Das letzte ist der Dezimaltrenner
            Dim lastDotPos As Integer = normalized.LastIndexOf(".")
            Dim lastCommaPos As Integer = normalized.LastIndexOf(",")

            LOGGER.Debug($"[NormalizeNumericString] Both separators found: LastDotPos={lastDotPos}, LastCommaPos={lastCommaPos}")

            If lastDotPos > lastCommaPos Then
                normalized = normalized.Replace(",", "")
            Else
                normalized = normalized.Replace(".", "").Replace(",", ".")
            End If

        ElseIf hasComma Then
            Dim commaCount As Integer = normalized.Count(Function(c) c = ","c)
            LOGGER.Debug($"[NormalizeNumericString] Only comma found: CommaCount={commaCount}")

            If commaCount = 1 Then
                Dim lastCommaPos As Integer = normalized.LastIndexOf(",")
                Dim digitsAfterComma As Integer = normalized.Length - lastCommaPos - 1
                LOGGER.Debug($"[NormalizeNumericString] Single comma: DigitsAfterComma={digitsAfterComma}")

                If digitsAfterComma <= 3 Then
                    normalized = normalized.Replace(",", ".")
                Else
                    normalized = normalized.Replace(",", "")
                End If
            Else
                normalized = normalized.Replace(",", "")
            End If

        ElseIf hasDot Then
            Dim dotCount As Integer = normalized.Count(Function(c) c = "."c)
            LOGGER.Debug($"[NormalizeNumericString] Only dot found: DotCount={dotCount}")

            If dotCount = 1 Then
                Dim lastDotPos As Integer = normalized.LastIndexOf(".")
                Dim digitsAfterDot As Integer = normalized.Length - lastDotPos - 1

                LOGGER.Debug($"[NormalizeNumericString] Single dot: DigitsAfterDot={digitsAfterDot}")

                ' ✅ KRITISCHE ÄNDERUNG: Prüfe auch Stellen VOR dem Punkt
                Dim digitsBeforeDot As Integer = lastDotPos

                ' Heuristik: Wenn <= 3 Stellen nach Punkt UND >= 1 Stelle davor → Dezimaltrenner
                ' Wenn > 3 Stellen davor UND <= 3 Stellen nach Punkt → unklar, vermutlich Dezimal
                ' Wenn > 3 Stellen nach Punkt → definitiv KEIN Dezimaltrenner

                If digitsAfterDot > 3 Then
                    LOGGER.Warn($"⚠️ [NormalizeNumericString] Dot with {digitsAfterDot} digits after → treating as THOUSAND separator!")
                    normalized = normalized.Replace(".", "")
                ElseIf digitsAfterDot >= 1 AndAlso digitsAfterDot <= 3 Then
                    ' Wahrscheinlich Dezimaltrenner (z.B. 5464.17 oder 120.5)
                    LOGGER.Debug($"[NormalizeNumericString] Dot treated as decimal separator ({digitsBeforeDot} digits before, {digitsAfterDot} after)")
                Else
                    ' digitsAfterDot = 0 → Punkt am Ende, vermutlich Fehler
                    LOGGER.Warn($"⚠️ [NormalizeNumericString] Dot at end of string → removing")
                    normalized = normalized.Replace(".", "")
                End If
            Else
                ' Mehrere Punkte → Tausendertrenner
                LOGGER.Debug($"[NormalizeNumericString] Multiple dots → removing all")
                normalized = normalized.Replace(".", "")
            End If
        Else
            LOGGER.Debug($"[NormalizeNumericString] No separators found → integer or already normalized")
        End If

        LOGGER.Debug($"[NormalizeNumericString] Output: [{normalized}]")

        Return normalized
    End Function

    ''' <summary>
    ''' Converts a string according to the type information, using the invariant culture
    ''' </summary>
    ''' <param name="pValue"></param>
    ''' <param name="pType"></param>
    ''' <returns></returns>
    Public Shared Function GetConvertedValue(pValue As Object, pType As String) As Object
        Dim oConvertedValue
        LOGGER.Debug($"GetConvertedValue: {pType}")

        Select Case pType
            Case ClassControlCreator.CONTROL_TYPE_DOUBLE
                Try
                    ' ✅ IMMER normalisieren – auch für DB-Werte!
                    Dim normalizedValue As String = NormalizeNumericString(pValue?.ToString())
                    If Double.TryParse(normalizedValue, NumberStyles.Float, CultureInfo.InvariantCulture, oConvertedValue) Then
                        Return oConvertedValue
                    End If
                Catch ex As Exception
                    LOGGER.Error(ex)
                End Try

            Case ClassControlCreator.CONTROL_TYPE_CURRENCY
                Try
                    ' ✅ KRITISCH: Normalisierung VOR Konvertierung
                    Dim normalizedValue As String = NormalizeNumericString(pValue?.ToString())
                    LOGGER.Debug($"GetConvertedValue CURRENCY: Original=[{pValue}], Normalized=[{normalizedValue}]")

                    If Double.TryParse(normalizedValue, NumberStyles.Float, CultureInfo.InvariantCulture, oConvertedValue) Then
                        Return oConvertedValue
                    End If
                Catch ex As Exception
                    LOGGER.Error($"Currency conversion failed for [{pValue}]: {ex.Message}")
                    LOGGER.Error(ex)
                End Try

            Case ClassControlCreator.CONTROL_TYPE_INTEGER
                Try
                    Dim normalizedValue As String = NormalizeNumericString(pValue?.ToString())
                    If Integer.TryParse(normalizedValue, NumberStyles.Integer, CultureInfo.InvariantCulture, oConvertedValue) Then
                        Return oConvertedValue
                    End If
                Catch ex As Exception
                    LOGGER.Error(ex)
                End Try

            Case Else
                LOGGER.Debug($"GetConvertedValue - Case ELSE - pType is {pType}")
                Try
                    oConvertedValue = pValue.ToString
                Catch ex As Exception
                    LOGGER.Warn($"Error in GetConvertedValue:  pType is {pType} - converting value to String")
                    oConvertedValue = ""
                End Try
        End Select

        Return oConvertedValue
    End Function

    ''' <summary>
    ''' Converts values to their respective data type and then back to string 
    ''' using INVARIANT culture for consistency across systems.
    ''' </summary>
    ''' <param name="pValue"></param>
    ''' <returns></returns>
    Public Shared Function GetStringValue(pValue As Object) As String
        ' ✅ FIX: Immer InvariantCulture verwenden für DB-Speicherung
        Select Case pValue.GetType
            Case GetType(Single)
                ' ✅ NEU: InvariantCulture statt CurrentCulture
                Return DirectCast(pValue, Single).ToString(CultureInfo.InvariantCulture)

            Case GetType(Double)
                ' ✅ NEU: InvariantCulture statt CurrentCulture
                Return DirectCast(pValue, Double).ToString(CultureInfo.InvariantCulture)

            Case GetType(Decimal)
                ' ✅ NEU: InvariantCulture statt CurrentCulture
                Return DirectCast(pValue, Decimal).ToString(CultureInfo.InvariantCulture)

            Case GetType(Date)
                ' Datum: ISO 8601 Format für Culture-Unabhängigkeit
                Return DirectCast(pValue, Date).ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)

            Case GetType(DateTime)
                ' DateTime: ISO 8601 Format
                Return DirectCast(pValue, DateTime).ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)

            Case Else
                Return pValue.ToString
        End Select
    End Function
End Class
