Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports SkiaSharp

Namespace Nexamas.UI.Theming

    ''' <summary>
    ''' Central validation authority for theme identity, foundation, family completeness,
    ''' and visual safety rules such as contrast, alpha, and surface differentiation.
    ''' </summary>
    ''' <remarks>
    ''' Validation should fail loudly. ThemeValidator must not silently repair invalid
    ''' themes or provide fallback colors.
    ''' </remarks>
    Partial Friend NotInheritable Class ThemeValidator

        Private Sub New()
        End Sub

#Region "Thresholds"

        ' WCAG-oriented text thresholds
        Private Const MinTextContrast As Double = 4.5
        Private Const MinLargeTextContrast As Double = 3.0
        Private Const MinAccentTextContrast As Double = 4.5
        Private Const MinDisabledTextContrast As Double = 2.2

        ' General visual differentiation
        Private Const MinDifferentiationDistance As Double = 12.0
        Private Const MinSurfaceStepDistance As Double = 8.0
        Private Const MinBorderDistance As Double = 6.0
        Private Const MinStrongStateDistance As Double = 14.0

        ' Luminance step thresholds
        Private Const MinSurfaceStepLuminanceDelta As Double = 0.018
        Private Const MinBorderLuminanceDelta As Double = 0.012
        Private Const MinTextLuminanceDelta As Double = 0.2

        ' Alpha thresholds
        Private Const MinVisibleAlphaByte As Byte = 24
        Private Const MinBorderAlphaByte As Byte = 28
        Private Const MinTextAlphaByte As Byte = 170
        Private Const MinSolidSurfaceAlphaByte As Byte = 200
        Private Const MinShadowAlphaByte As Byte = 18

#End Region

#Region "Public Entry Points"

        Friend Shared Sub ValidateFoundation(bundle As IMASThemeFoundation)
            If bundle Is Nothing Then
                Throw New ArgumentNullException(NameOf(bundle))
            End If

            Dim errors As New List(Of String)()

            ValidateBrand(bundle.Brand, errors)
            ValidateNeutrals(bundle.Neutrals, errors)
            ValidateSemantics(bundle.Semantics, errors)
            ValidateTextColors(bundle.TextColors, errors)
            ValidateInteraction(bundle.Interaction, errors)
            ValidateSurface(bundle.Surface, errors)
            ValidateFoundationCrossConsistency(bundle, errors)

            ThrowIfAny(errors, "Theme foundation validation failed.")
        End Sub

        Friend Shared Sub ValidateFamilies(bundle As IMASThemeFamilies)
            If bundle Is Nothing Then
                Throw New ArgumentNullException(NameOf(bundle))
            End If

            Dim errors As New List(Of String)()

            If bundle.SelectorItem Is Nothing Then errors.Add("Family 'SelectorItem' is Nothing.")
            If bundle.SegmentedControl Is Nothing Then errors.Add("Family 'SegmentedControl' is Nothing.")

            If bundle.List Is Nothing Then errors.Add("Family 'List' is Nothing.")
            If bundle.Input Is Nothing Then errors.Add("Family 'Input' is Nothing.")
            If bundle.Tile Is Nothing Then errors.Add("Family 'Tile' is Nothing.")
            If bundle.Buttons Is Nothing Then errors.Add("Family 'Buttons' is Nothing.")
            If bundle.Cards Is Nothing Then errors.Add("Family 'Cards' is Nothing.")
            If bundle.Media Is Nothing Then errors.Add("Family 'Media' is Nothing.")
            If bundle.Menu Is Nothing Then errors.Add("Family 'Menu' is Nothing.")
            If bundle.Scroll Is Nothing Then errors.Add("Family 'Scroll' is Nothing.")
            If bundle.TopBar Is Nothing Then errors.Add("Family 'TopBar' is Nothing.")
            If bundle.Progress Is Nothing Then errors.Add("Family 'Progress' is Nothing.")
            If bundle.Toast Is Nothing Then errors.Add("Family 'Toast' is Nothing.")
            If bundle.Tooltip Is Nothing Then errors.Add("Family 'Tooltip' is Nothing.")

            If bundle.CheckBox Is Nothing Then errors.Add("Family 'CheckBox' is Nothing.")
            If bundle.RadioButton Is Nothing Then errors.Add("Family 'RadioButton' is Nothing.")
            If bundle.ToggleSwitch Is Nothing Then errors.Add("Family 'ToggleSwitch' is Nothing.")
            If bundle.ColorPicker Is Nothing Then errors.Add("Family 'ColorPicker' is Nothing.")

            If bundle.Tabs Is Nothing Then errors.Add("Family 'Tabs' is Nothing.")
            If bundle.PanelShell Is Nothing Then errors.Add("Family 'PanelShell' is Nothing.")
            If bundle.GroupBox Is Nothing Then errors.Add("Family 'GroupBox' is Nothing.")

            ThrowIfAny(errors, "Theme family validation failed.")
        End Sub

        Friend Shared Sub ValidateComposition(id As String,
                                              displayName As String,
                                              version As Integer,
                                              foundation As IMASThemeFoundation,
                                              families As IMASThemeFamilies)

            Dim errors As New List(Of String)()

            If String.IsNullOrWhiteSpace(id) Then
                errors.Add("Theme id cannot be null, empty, or whitespace.")
            End If

            If String.IsNullOrWhiteSpace(displayName) Then
                errors.Add("Theme display name cannot be null, empty, or whitespace.")
            End If

            If version < 1 Then
                errors.Add("Theme version must be greater than or equal to 1.")
            End If

            If foundation Is Nothing Then
                errors.Add("Foundation bundle is Nothing.")
            End If

            If families Is Nothing Then
                errors.Add("Families bundle is Nothing.")
            End If

            ThrowIfAny(errors, "Theme composition validation failed.")

            ValidateFoundation(foundation)
            ValidateFamilies(families)
        End Sub

#End Region

#Region "Foundation Sections"
        Private Shared Sub ValidateButtonSurface(surface As MASButtonSurfaceTheme,
                                         source As String,
                                         errors As List(Of String))

            ValidateRequiredColor(surface.FillTop, $"{source}.FillTop", errors)
            ValidateRequiredColor(surface.FillBottom, $"{source}.FillBottom", errors)

            ValidateRequiredColor(surface.BorderOuter, $"{source}.BorderOuter", errors)
            ValidateRequiredColor(surface.BorderInner, $"{source}.BorderInner", errors)

            ValidateRequiredColor(surface.InnerHighlight, $"{source}.InnerHighlight", errors)
            ValidateRequiredColor(surface.InnerShade, $"{source}.InnerShade", errors)

            ValidateRequiredColor(surface.MaterialWash, $"{source}.MaterialWash", errors)
            ValidateRequiredColor(surface.BodyGlow, $"{source}.BodyGlow", errors)
            ValidateRequiredColor(surface.TopSheen, $"{source}.TopSheen", errors)

            ValidateMinimumAlpha(surface.FillTop, $"{source}.FillTop", MinVisibleAlphaByte, errors)
            ValidateMinimumAlpha(surface.FillBottom, $"{source}.FillBottom", MinVisibleAlphaByte, errors)

            ValidateMinimumAlpha(surface.BorderOuter, $"{source}.BorderOuter", MinBorderAlphaByte, errors)
            ValidateMinimumAlpha(surface.BorderInner, $"{source}.BorderInner", MinBorderAlphaByte, errors)

            ValidateMinimumAlpha(surface.MaterialWash, $"{source}.MaterialWash", MinVisibleAlphaByte, errors)

            ValidateDistinctEnough(surface.FillTop,
                           surface.FillBottom,
                           $"{source}.FillTop vs FillBottom",
                           MinSurfaceStepDistance,
                           errors)

            ValidateDistinctEnough(surface.BorderOuter,
                           surface.BorderInner,
                           $"{source}.BorderOuter vs BorderInner",
                           MinBorderDistance,
                           errors)

            ValidatePerceptualDifference(surface.FillTop,
                                 surface.FillBottom,
                                 $"{source}.FillTop vs FillBottom",
                                 MinSurfaceStepLuminanceDelta,
                                 errors)

            ValidatePerceptualDifference(surface.BorderOuter,
                                 surface.BorderInner,
                                 $"{source}.BorderOuter vs BorderInner",
                                 MinBorderLuminanceDelta,
                                 errors)
        End Sub
        Private Shared Sub ValidateBrand(brand As IMASBrandTheme, errors As List(Of String))
            If brand Is Nothing Then
                errors.Add("Brand theme is Nothing.")
                Return
            End If

            ValidateRequiredColor(brand.BrandPrimary, "Brand.BrandPrimary", errors)
            ValidateRequiredColor(brand.BrandPrimaryStrong, "Brand.BrandPrimaryStrong", errors)
            ValidateRequiredColor(brand.BrandPrimarySoft, "Brand.BrandPrimarySoft", errors)
            ValidateRequiredColor(brand.BrandSecondary, "Brand.BrandSecondary", errors)
            ValidateRequiredColor(brand.BrandSecondarySoft, "Brand.BrandSecondarySoft", errors)
            ValidateRequiredColor(brand.BrandSilver, "Brand.BrandSilver", errors)
            ValidateRequiredColor(brand.BrandSilverSoft, "Brand.BrandSilverSoft", errors)
            ValidateRequiredColor(brand.BrandHighlight, "Brand.BrandHighlight", errors)

            ValidateDistinctEnough(brand.BrandPrimary, brand.BrandPrimaryStrong, "BrandPrimary vs BrandPrimaryStrong", MinDifferentiationDistance, errors)
            ValidateDistinctEnough(brand.BrandPrimary, brand.BrandPrimarySoft, "BrandPrimary vs BrandPrimarySoft", MinDifferentiationDistance, errors)
            ValidateDistinctEnough(brand.BrandSecondary, brand.BrandSecondarySoft, "BrandSecondary vs BrandSecondarySoft", MinDifferentiationDistance, errors)
            ValidateDistinctEnough(brand.BrandSilver, brand.BrandSilverSoft, "BrandSilver vs BrandSilverSoft", MinDifferentiationDistance, errors)

            ValidatePerceptualDifference(brand.BrandPrimary, brand.BrandPrimarySoft, "BrandPrimary vs BrandPrimarySoft", 0.03, errors)
            ValidatePerceptualDifference(brand.BrandSecondary, brand.BrandSecondarySoft, "BrandSecondary vs BrandSecondarySoft", 0.03, errors)
        End Sub

        Private Shared Sub ValidateNeutrals(neutrals As IMASNeutralTheme, errors As List(Of String))
            If neutrals Is Nothing Then
                errors.Add("Neutral theme is Nothing.")
                Return
            End If

            ValidateRequiredColor(neutrals.Surface0, "Neutrals.Surface0", errors)
            ValidateRequiredColor(neutrals.Surface1, "Neutrals.Surface1", errors)
            ValidateRequiredColor(neutrals.Surface2, "Neutrals.Surface2", errors)
            ValidateRequiredColor(neutrals.Surface3, "Neutrals.Surface3", errors)

            ValidateRequiredColor(neutrals.BorderSubtle, "Neutrals.BorderSubtle", errors)
            ValidateRequiredColor(neutrals.BorderDefault, "Neutrals.BorderDefault", errors)
            ValidateRequiredColor(neutrals.BorderStrong, "Neutrals.BorderStrong", errors)

            ValidateRequiredColor(neutrals.MetallicSoft, "Neutrals.MetallicSoft", errors)
            ValidateRequiredColor(neutrals.MetallicEdge, "Neutrals.MetallicEdge", errors)

            ValidateMinimumAlpha(neutrals.Surface0, "Neutrals.Surface0", MinSolidSurfaceAlphaByte, errors)
            ValidateMinimumAlpha(neutrals.Surface1, "Neutrals.Surface1", MinSolidSurfaceAlphaByte, errors)
            ValidateMinimumAlpha(neutrals.Surface2, "Neutrals.Surface2", MinSolidSurfaceAlphaByte, errors)
            ValidateMinimumAlpha(neutrals.Surface3, "Neutrals.Surface3", MinSolidSurfaceAlphaByte, errors)

            ValidateDistinctEnough(neutrals.Surface0, neutrals.Surface1, "Surface0 vs Surface1", MinSurfaceStepDistance, errors)
            ValidateDistinctEnough(neutrals.Surface1, neutrals.Surface2, "Surface1 vs Surface2", MinSurfaceStepDistance, errors)
            ValidateDistinctEnough(neutrals.Surface2, neutrals.Surface3, "Surface2 vs Surface3", MinSurfaceStepDistance, errors)

            ValidatePerceptualDifference(neutrals.Surface0, neutrals.Surface1, "Surface0 vs Surface1", MinSurfaceStepLuminanceDelta, errors)
            ValidatePerceptualDifference(neutrals.Surface1, neutrals.Surface2, "Surface1 vs Surface2", MinSurfaceStepLuminanceDelta, errors)
            ValidatePerceptualDifference(neutrals.Surface2, neutrals.Surface3, "Surface2 vs Surface3", MinSurfaceStepLuminanceDelta, errors)

            ValidateDistinctEnough(neutrals.BorderSubtle, neutrals.BorderDefault, "BorderSubtle vs BorderDefault", MinBorderDistance, errors)
            ValidateDistinctEnough(neutrals.BorderDefault, neutrals.BorderStrong, "BorderDefault vs BorderStrong", MinBorderDistance, errors)

            ValidatePerceptualDifference(neutrals.BorderSubtle, neutrals.BorderDefault, "BorderSubtle vs BorderDefault", MinBorderLuminanceDelta, errors)
            ValidatePerceptualDifference(neutrals.BorderDefault, neutrals.BorderStrong, "BorderDefault vs BorderStrong", MinBorderLuminanceDelta, errors)

            ValidateSurfaceOrderingMonotonic(
                neutrals.Surface0,
                neutrals.Surface1,
                neutrals.Surface2,
                neutrals.Surface3,
                "Neutrals surfaces",
                errors)

            ValidateMinimumAlpha(neutrals.BorderSubtle, "Neutrals.BorderSubtle", MinBorderAlphaByte, errors)
            ValidateMinimumAlpha(neutrals.BorderDefault, "Neutrals.BorderDefault", MinBorderAlphaByte, errors)
            ValidateMinimumAlpha(neutrals.BorderStrong, "Neutrals.BorderStrong", MinBorderAlphaByte, errors)
        End Sub

        Private Shared Sub ValidateSemantics(semantics As IMASSemanticTheme, errors As List(Of String))
            If semantics Is Nothing Then
                errors.Add("Semantic theme is Nothing.")
                Return
            End If

            ValidateRequiredColor(semantics.Success, "Semantics.Success", errors)
            ValidateRequiredColor(semantics.Warning, "Semantics.Warning", errors)
            ValidateRequiredColor(semantics.Danger, "Semantics.Danger", errors)
            ValidateRequiredColor(semantics.Info, "Semantics.Info", errors)
            ValidateRequiredColor(semantics.Muted, "Semantics.Muted", errors)
            ValidateRequiredColor(semantics.Disabled, "Semantics.Disabled", errors)
            ValidateRequiredColor(semantics.FocusStrong, "Semantics.FocusStrong", errors)
            ValidateRequiredColor(semantics.SelectionSoft, "Semantics.SelectionSoft", errors)

            ValidateDistinctEnough(semantics.Success, semantics.Danger, "Semantic Success vs Danger", MinDifferentiationDistance, errors)
            ValidateDistinctEnough(semantics.Warning, semantics.Danger, "Semantic Warning vs Danger", MinDifferentiationDistance, errors)
            ValidateDistinctEnough(semantics.FocusStrong, semantics.SelectionSoft, "Semantic FocusStrong vs SelectionSoft", MinDifferentiationDistance, errors)

            ValidatePerceptualDifference(semantics.FocusStrong, semantics.SelectionSoft, "Semantic FocusStrong vs SelectionSoft", 0.03, errors)
        End Sub

        Private Shared Sub ValidateTextColors(textColors As IMASTextColorTheme, errors As List(Of String))
            If textColors Is Nothing Then
                errors.Add("Text color theme is Nothing.")
                Return
            End If

            ValidateRequiredTextColor(textColors.PrimaryText, "TextColors.PrimaryText", errors)
            ValidateRequiredTextColor(textColors.SecondaryText, "TextColors.SecondaryText", errors)
            ValidateRequiredTextColor(textColors.MutedText, "TextColors.MutedText", errors)
            ValidateRequiredTextColor(textColors.DisabledText, "TextColors.DisabledText", errors)
            ValidateRequiredTextColor(textColors.InverseText, "TextColors.InverseText", errors)
            ValidateRequiredTextColor(textColors.OnBrandPrimaryText, "TextColors.OnBrandPrimaryText", errors)
            ValidateRequiredTextColor(textColors.OnBrandPrimaryStrongText, "TextColors.OnBrandPrimaryStrongText", errors)

            ValidateDistinctEnough(textColors.PrimaryText, textColors.SecondaryText, "PrimaryText vs SecondaryText", 8.0, errors)
            ValidateDistinctEnough(textColors.SecondaryText, textColors.MutedText, "SecondaryText vs MutedText", 8.0, errors)
            ValidateDistinctEnough(textColors.MutedText, textColors.DisabledText, "MutedText vs DisabledText", 8.0, errors)

            ValidatePerceptualDifference(textColors.PrimaryText, textColors.SecondaryText, "PrimaryText vs SecondaryText", 0.05, errors)
            ValidatePerceptualDifference(textColors.SecondaryText, textColors.MutedText, "SecondaryText vs MutedText", 0.04, errors)
            ValidatePerceptualDifference(textColors.MutedText, textColors.DisabledText, "MutedText vs DisabledText", 0.03, errors)
        End Sub

        Private Shared Sub ValidateInteraction(interaction As IMASInteractionTheme, errors As List(Of String))
            If interaction Is Nothing Then
                errors.Add("Interaction theme is Nothing.")
                Return
            End If

            ValidateInteractionState(interaction.Hover, "Interaction.Hover", errors)
            ValidateInteractionState(interaction.Pressed, "Interaction.Pressed", errors)
            ValidateInteractionState(interaction.Selected, "Interaction.Selected", errors)
            ValidateInteractionState(interaction.Focused, "Interaction.Focused", errors)
            ValidateInteractionLayer(interaction.DisabledFill, "Interaction.DisabledFill", errors)

            If interaction.Hover.Fill.Enabled AndAlso interaction.Pressed.Fill.Enabled Then
                ValidateDistinctEnough(
                    interaction.Hover.Fill.Color,
                    interaction.Pressed.Fill.Color,
                    "Interaction Hover.Fill vs Pressed.Fill",
                    MinStrongStateDistance,
                    errors)
            End If

            If interaction.Hover.Ring.Enabled AndAlso interaction.Selected.Ring.Enabled Then
                ValidateDistinctEnough(
                    interaction.Hover.Ring.Color,
                    interaction.Selected.Ring.Color,
                    "Interaction Hover.Ring vs Selected.Ring",
                    MinDifferentiationDistance,
                    errors)
            End If

            If interaction.Selected.Ring.Enabled AndAlso interaction.Focused.Ring.Enabled Then
                ValidateDistinctEnough(
                    interaction.Selected.Ring.Color,
                    interaction.Focused.Ring.Color,
                    "Interaction Selected.Ring vs Focused.Ring",
                    MinDifferentiationDistance,
                    errors)
            End If

            If interaction.Selected.Fill.Enabled AndAlso interaction.DisabledFill.Enabled Then
                ValidateDistinctEnough(
                    interaction.Selected.Fill.Color,
                    interaction.DisabledFill.Color,
                    "Interaction Selected.Fill vs DisabledFill",
                    MinStrongStateDistance,
                    errors)
            End If
        End Sub

        Private Shared Sub ValidateInteractionState(state As MASInteractionThemeState,
                                                    source As String,
                                                    errors As List(Of String))
            ValidateInteractionLayer(state.Fill, $"{source}.Fill", errors)
            ValidateInteractionLayer(state.Ring, $"{source}.Ring", errors)

            If state.Fill.Enabled AndAlso state.Ring.Enabled Then
                ValidateDistinctEnough(state.Fill.Color, state.Ring.Color, $"{source}.Fill vs Ring", 6.0, errors)
            End If
        End Sub

        Private Shared Sub ValidateInteractionLayer(layer As MASInteractionThemeLayer,
                                                    source As String,
                                                    errors As List(Of String))
            If Not layer.Enabled Then
                Return
            End If

            ValidateRequiredColor(layer.Color, source, errors)
            ValidateMinimumAlpha(layer.Color, source, MinVisibleAlphaByte, errors)
        End Sub

        Private Shared Sub ValidateSurface(surface As IMASFoundationSurfaceTheme, errors As List(Of String))
            If surface Is Nothing Then
                errors.Add("Foundation surface theme is Nothing.")
                Return
            End If

            ValidateRequiredColor(surface.SurfaceTop, "Surface.SurfaceTop", errors)
            ValidateRequiredColor(surface.SurfaceMid, "Surface.SurfaceMid", errors)
            ValidateRequiredColor(surface.SurfaceBot, "Surface.SurfaceBot", errors)
            ValidateRequiredColor(surface.MaterialWash, "Surface.MaterialWash", errors)
            ValidateRequiredColor(surface.BorderOuter, "Surface.BorderOuter", errors)
            ValidateRequiredColor(surface.BorderInner, "Surface.BorderInner", errors)
            ValidateRequiredColor(surface.ShadowBase, "Surface.ShadowBase", errors)
            ValidateRequiredColor(surface.BackgroundColdTint, "Surface.BackgroundColdTint", errors)

            ValidateDistinctEnough(surface.SurfaceTop, surface.SurfaceMid, "SurfaceTop vs SurfaceMid", MinSurfaceStepDistance, errors)
            ValidateDistinctEnough(surface.SurfaceMid, surface.SurfaceBot, "SurfaceMid vs SurfaceBot", MinSurfaceStepDistance, errors)
            ValidateDistinctEnough(surface.BorderOuter, surface.BorderInner, "BorderOuter vs BorderInner", MinBorderDistance, errors)

            ValidatePerceptualDifference(surface.SurfaceTop, surface.SurfaceMid, "SurfaceTop vs SurfaceMid", MinSurfaceStepLuminanceDelta, errors)
            ValidatePerceptualDifference(surface.SurfaceMid, surface.SurfaceBot, "SurfaceMid vs SurfaceBot", MinSurfaceStepLuminanceDelta, errors)
            ValidatePerceptualDifference(surface.BorderOuter, surface.BorderInner, "BorderOuter vs BorderInner", MinBorderLuminanceDelta, errors)

            ValidateSurfaceOrderingMonotonic(
                surface.SurfaceTop,
                surface.SurfaceMid,
                surface.SurfaceBot,
                Nothing,
                "Acrylic surface gradient",
                errors)

            ValidateMinimumAlpha(surface.MaterialWash, "Surface.MaterialWash", MinVisibleAlphaByte, errors)
            ValidateMinimumAlpha(surface.BorderOuter, "Surface.BorderOuter", MinBorderAlphaByte, errors)
            ValidateMinimumAlpha(surface.BorderInner, "Surface.BorderInner", MinBorderAlphaByte, errors)
            ValidateMinimumAlpha(surface.ShadowBase, "Surface.ShadowBase", MinShadowAlphaByte, errors)

            ValidateShadowSanity(surface.ShadowBase, "Surface.ShadowBase", errors)
        End Sub

#End Region


    End Class

End Namespace
