Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports SkiaSharp

Namespace Nexamas.UI.Theming

    ' ThemeValidator partial implementation extracted for maintainability.
    Partial Friend NotInheritable Class ThemeValidator

#Region "Low-level Validation Helpers"

        Private Shared Sub ValidateRequiredColor(color As SKColor, name As String, errors As List(Of String))
            If IsClearlyInvalidColor(color) Then
                errors.Add($"Color '{name}' is invalid or empty.")
            End If
        End Sub

        Private Shared Sub ValidateRequiredTextColor(color As SKColor, name As String, errors As List(Of String))
            ValidateRequiredColor(color, name, errors)
            ValidateMinimumAlpha(color, name, MinTextAlphaByte, errors)
        End Sub

        Private Shared Sub ValidateMinimumAlpha(color As SKColor,
                                                name As String,
                                                minimumAlpha As Byte,
                                                errors As List(Of String))
            If color.Alpha < minimumAlpha Then
                errors.Add($"Color '{name}' alpha is too low. Alpha={color.Alpha}, required>={minimumAlpha}.")
            End If
        End Sub

        Private Shared Function IsClearlyInvalidColor(color As SKColor) As Boolean
            Return color.Alpha = 0 AndAlso
                   color.Red = 0 AndAlso
                   color.Green = 0 AndAlso
                   color.Blue = 0
        End Function

        Private Shared Sub ValidateDistinctEnough(a As SKColor,
                                                  b As SKColor,
                                                  pairName As String,
                                                  minDistance As Double,
                                                  errors As List(Of String))
            Dim distance As Double = ColorDistance(a, b)

            If distance < minDistance Then
                errors.Add($"Colors '{pairName}' are too similar. Distance={distance:F2}, required>={minDistance:F2}.")
            End If
        End Sub

        Private Shared Sub ValidatePerceptualDifference(a As SKColor,
                                                        b As SKColor,
                                                        pairName As String,
                                                        minLuminanceDelta As Double,
                                                        errors As List(Of String))
            Dim delta As Double = Math.Abs(RelativeLuminance(a) - RelativeLuminance(b))

            If delta < minLuminanceDelta Then
                errors.Add($"Colors '{pairName}' are too close perceptually. LuminanceDelta={delta:F4}, required>={minLuminanceDelta:F4}.")
            End If
        End Sub

        Private Shared Sub ValidateContrast(foreground As SKColor,
                                            background As SKColor,
                                            pairName As String,
                                            minContrast As Double,
                                            errors As List(Of String))
            Dim effectiveForeground As SKColor = CompositeOver(foreground, background)
            Dim ratio As Double = ContrastRatio(effectiveForeground, background)

            If ratio < minContrast Then
                errors.Add($"Contrast for '{pairName}' is too low. Ratio={ratio:F2}, required>={minContrast:F2}.")
            End If
        End Sub

        Private Shared Sub ValidateCompositeSeparation(foreground As SKColor,
                                                       background As SKColor,
                                                       pairName As String,
                                                       minDistance As Double,
                                                       minLuminanceDelta As Double,
                                                       errors As List(Of String))
            Dim composite As SKColor = CompositeOver(foreground, background)
            Dim distance As Double = ColorDistance(composite, background)
            Dim luminanceDelta As Double = Math.Abs(RelativeLuminance(composite) - RelativeLuminance(background))

            If distance < minDistance Then
                errors.Add($"Composite separation for '{pairName}' is too weak. Distance={distance:F2}, required>={minDistance:F2}.")
            End If

            If luminanceDelta < minLuminanceDelta Then
                errors.Add($"Composite luminance separation for '{pairName}' is too weak. LuminanceDelta={luminanceDelta:F4}, required>={minLuminanceDelta:F4}.")
            End If
        End Sub

        Private Shared Sub ValidateSurfaceOrderingMonotonic(c1 As SKColor,
                                                            c2 As SKColor,
                                                            c3 As SKColor,
                                                            c4 As SKColor?,
                                                            label As String,
                                                            errors As List(Of String))
            Dim values As New List(Of Double) From {
                RelativeLuminance(c1),
                RelativeLuminance(c2),
                RelativeLuminance(c3)
            }

            If c4.HasValue Then
                values.Add(RelativeLuminance(c4.Value))
            End If

            If Not IsStrictlyMonotonic(values) Then
                errors.Add($"'{label}' must have a clear luminance progression. Current sequence is not monotonic.")
            End If
        End Sub

        Private Shared Function IsStrictlyMonotonic(values As IList(Of Double)) As Boolean
            If values Is Nothing Then
                Throw New ArgumentNullException(NameOf(values))
            End If

            If values.Count < 2 Then
                Return True
            End If

            Dim ascending As Boolean = True
            Dim descending As Boolean = True

            For i As Integer = 1 To values.Count - 1
                If values(i) <= values(i - 1) Then
                    ascending = False
                End If

                If values(i) >= values(i - 1) Then
                    descending = False
                End If
            Next

            Return ascending OrElse descending
        End Function

        Private Shared Sub ValidateShadowSanity(shadow As SKColor,
                                                name As String,
                                                errors As List(Of String))
            If shadow.Alpha = 0 Then
                errors.Add($"'{name}' cannot be fully transparent.")
            End If

            Dim luminance As Double = RelativeLuminance(shadow)
            If luminance > 0.35 Then
                errors.Add($"'{name}' is too bright for a shadow base. Luminance={luminance:F4}, recommended<=0.35.")
            End If
        End Sub

        Private Shared Function ColorDistance(a As SKColor, b As SKColor) As Double
            Dim dr As Double = CDbl(a.Red) - CDbl(b.Red)
            Dim dg As Double = CDbl(a.Green) - CDbl(b.Green)
            Dim db As Double = CDbl(a.Blue) - CDbl(b.Blue)

            Dim rgbDistance As Double = Math.Sqrt((dr * dr) + (dg * dg) + (db * db))
            Dim luminanceWeight As Double = Math.Abs(RelativeLuminance(a) - RelativeLuminance(b)) * 255.0

            Return rgbDistance + (luminanceWeight * 0.5)
        End Function

        Private Shared Function ContrastRatio(a As SKColor, b As SKColor) As Double
            Dim l1 As Double = RelativeLuminance(a)
            Dim l2 As Double = RelativeLuminance(b)

            Dim lighter As Double = Math.Max(l1, l2)
            Dim darker As Double = Math.Min(l1, l2)

            Return (lighter + 0.05) / (darker + 0.05)
        End Function

        Private Shared Function RelativeLuminance(color As SKColor) As Double
            Dim r As Double = Linearize(color.Red / 255.0)
            Dim g As Double = Linearize(color.Green / 255.0)
            Dim b As Double = Linearize(color.Blue / 255.0)

            Return (0.2126 * r) + (0.7152 * g) + (0.0722 * b)
        End Function

        Private Shared Function Linearize(channel As Double) As Double
            If channel <= 0.04045 Then
                Return channel / 12.92
            End If

            Return Math.Pow((channel + 0.055) / 1.055, 2.4)
        End Function

        Private Shared Function CompositeOver(foreground As SKColor, background As SKColor) As SKColor
            Dim fa As Double = foreground.Alpha / 255.0
            Dim ba As Double = background.Alpha / 255.0

            Dim outA As Double = fa + (ba * (1.0 - fa))
            If outA <= 0.0 Then
                Return New SKColor(0, 0, 0, 0)
            End If

            Dim fr As Double = foreground.Red / 255.0
            Dim fg As Double = foreground.Green / 255.0
            Dim fb As Double = foreground.Blue / 255.0

            Dim br As Double = background.Red / 255.0
            Dim bg As Double = background.Green / 255.0
            Dim bb As Double = background.Blue / 255.0

            Dim outR As Double = ((fr * fa) + (br * ba * (1.0 - fa))) / outA
            Dim outG As Double = ((fg * fa) + (bg * ba * (1.0 - fa))) / outA
            Dim outB As Double = ((fb * fa) + (bb * ba * (1.0 - fa))) / outA

            Return New SKColor(
                ToByte(outR * 255.0),
                ToByte(outG * 255.0),
                ToByte(outB * 255.0),
                ToByte(outA * 255.0))
        End Function

        Private Shared Function ToByte(value As Double) As Byte
            If value <= 0.0 Then Return 0
            If value >= 255.0 Then Return 255
            Return CByte(Math.Round(value, MidpointRounding.AwayFromZero))
        End Function

        Private Shared Sub ThrowIfAny(errors As List(Of String), header As String)
            If errors Is Nothing Then
                Throw New ArgumentNullException(NameOf(errors))
            End If

            If errors.Count = 0 Then
                Return
            End If

            Throw New InvalidOperationException(BuildMessage(header, errors))
        End Sub

        Private Shared Function BuildMessage(header As String, errors As List(Of String)) As String
            Dim lines As New List(Of String)()

            lines.Add(header)

            For i As Integer = 0 To errors.Count - 1
                lines.Add($" - {errors(i)}")
            Next

            Return String.Join(Environment.NewLine, lines)
        End Function

#End Region

    End Class

End Namespace
