Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Verification

Namespace Nexamas.UI.Certification

    ''' <summary>
    ''' Official internal coverage map between MASComponentRegistry descriptors and Quality/RenderVerification evidence.
    ''' </summary>
    ''' <remarks>
    ''' This map deliberately consumes existing registries instead of maintaining a parallel component list. Every descriptor
    ''' must resolve to a direct render scenario, a family render scenario, or a named contract/system proof. Unclassified
    ''' descriptors are blocking quality gaps.
    ''' </remarks>
    Friend NotInheritable Class MASQualityComponentCoverageMap

        Private Sub New()
        End Sub

        Friend Shared Function BuildCurrent() As MASQualityComponentCoverageReport
            Dim scenarioIndex As ScenarioIndex = ScenarioIndex.Build(MASRenderVerificationTargetCatalog.CreateAllTargets())
            Dim entries As New List(Of MASQualityComponentCoverageEntry)()

            For Each descriptor As MASComponentDescriptor In MASComponentRegistry.Components
                If descriptor Is Nothing Then
                    entries.Add(New MASQualityComponentCoverageEntry("<null>",
                                                                     "<null>",
                                                                     "<null>",
                                                                     MASQualityComponentCoverageKind.Unclassified,
                                                                     String.Empty,
                                                                     String.Empty,
                                                                     String.Empty,
                                                                     "Null component descriptor cannot be covered."))
                    Continue For
                End If

                entries.Add(ClassifyDescriptor(descriptor, scenarioIndex))
            Next

            Return New MASQualityComponentCoverageReport(entries)
        End Function

        Private Shared Function ClassifyDescriptor(descriptor As MASComponentDescriptor,
                                                   scenarioIndex As ScenarioIndex) As MASQualityComponentCoverageEntry
            Dim typeName As String = descriptor.ComponentType.Name
            Dim fullName As String = descriptor.ComponentType.FullName
            Dim categoryName As String = descriptor.Category.ToString()

            Dim descriptorPlan As MASRenderVerificationDescriptorScenarioPlan = MASRenderVerificationDescriptorScenarioPlanner.CreatePlan(descriptor)
            If descriptorPlan IsNot Nothing Then
                Dim descriptorScenario As ScenarioReference = scenarioIndex.FindByScenarioId(descriptorPlan.ScenarioId)
                If descriptorScenario IsNot Nothing Then
                    Return CreateEntry(descriptor,
                                       MASQualityComponentCoverageKind.DirectRenderScenario,
                                       "RenderVerification descriptor scenario: " & descriptorScenario.Scenario.ComponentKey,
                                       descriptorScenario,
                                       "Component descriptor has an official per-descriptor PNG scenario routed through the Nexamas UI capture catalog.")
                End If
            End If

            Dim direct As ScenarioReference = scenarioIndex.FindDirect(typeName)
            If direct IsNot Nothing Then
                Return CreateEntry(descriptor,
                                   MASQualityComponentCoverageKind.DirectRenderScenario,
                                   "RenderVerification direct scenario: " & direct.Scenario.ComponentKey,
                                   direct,
                                   "Component key is directly represented by the official render-verification catalog.")
            End If

            Dim family As ScenarioReference = ResolveFamilyScenario(descriptor, scenarioIndex)
            If family IsNot Nothing Then
                Return CreateEntry(descriptor,
                                   MASQualityComponentCoverageKind.FamilyRenderScenario,
                                   "RenderVerification family scenario: " & family.Scenario.ComponentKey,
                                   family,
                                   "Component is covered through a declared family render scenario and architecture descriptor contracts.")
            End If

            Dim contractKind As MASQualityComponentCoverageKind = ResolveContractCoverageKind(descriptor)
            Dim evidenceKey As String = ResolveContractEvidenceKey(descriptor, contractKind)
            If contractKind <> MASQualityComponentCoverageKind.Unclassified AndAlso Not String.IsNullOrWhiteSpace(evidenceKey) Then
                Return New MASQualityComponentCoverageEntry(typeName,
                                                            fullName,
                                                            categoryName,
                                                            contractKind,
                                                            evidenceKey,
                                                            String.Empty,
                                                            String.Empty,
                                                            "Component is covered by descriptor validation plus the named contract/system proof surface.")
            End If

            Return New MASQualityComponentCoverageEntry(typeName,
                                                        fullName,
                                                        categoryName,
                                                        MASQualityComponentCoverageKind.Unclassified,
                                                        String.Empty,
                                                        String.Empty,
                                                        String.Empty,
                                                        "No direct, family, or contract proof classification was found for this descriptor.")
        End Function

        Private Shared Function ResolveFamilyScenario(descriptor As MASComponentDescriptor,
                                                      scenarioIndex As ScenarioIndex) As ScenarioReference
            Dim typeName As String = descriptor.ComponentType.Name
            Dim fullName As String = descriptor.ComponentType.FullName
            Dim controlKind As String = If(descriptor.ControlKind.HasValue, descriptor.ControlKind.Value.ToString(), String.Empty)
            Dim surfaceFamily As String = descriptor.SurfaceFamily.ToString()

            If ContainsAny(typeName, "SplitButton", "GlyphButton") Then Return scenarioIndex.Find("MASButton", "Button")
            If ContainsAny(typeName, "CheckBox", "RadioButton", "ToggleSwitch") Then Return scenarioIndex.Find("MASChoiceControls", controlKind)
            If ContainsAny(typeName, "TextBox", "PasswordTextBox", "SearchTextBox", "NumericTextBox", "MultilineTextBox") Then Return scenarioIndex.Find("MASTextInputs", controlKind)
            If ContainsAny(typeName, "PathTextBox", "ColorPicker", "Validation") Then Return scenarioIndex.Find("MASAdvancedInputs", controlKind)
            If ContainsAny(typeName, "Segmented", "SelectorItem", "TabControl") Then Return scenarioIndex.Find("MASSelectors", controlKind)
            If ContainsAny(typeName, "ProgressBar", "OperationProgress") Then Return scenarioIndex.Find("MASProgress", controlKind)
            If ContainsAny(typeName, "Toolbar", "PathDisplay") Then Return scenarioIndex.Find("MASToolbar", controlKind)
            If ContainsAny(typeName, "TopBar") Then Return scenarioIndex.Find("MASTopBar", "TopBar")
            If ContainsAny(typeName, "ListRow", "ListView") Then Return scenarioIndex.Find("MASListView", "ListView")
            If ContainsAny(typeName, "ListBox") Then Return scenarioIndex.Find("MASListBox", "ListBox")
            If ContainsAny(typeName, "TreeRow", "TreeView") Then Return scenarioIndex.Find("MASTreeView", "TreeView")
            If ContainsAny(typeName, "TileItem", "TileBox") Then Return scenarioIndex.Find("MASTileBox", "TileBox")
            If ContainsAny(typeName, "FileExplorer", "FileBrowser") Then Return scenarioIndex.Find("MASFileExplorer", "FileExplorer")
            If ContainsAny(typeName, "FilePicker") Then Return scenarioIndex.Find("MASFilePicker", "FilePicker")
            If ContainsAny(typeName, "Tooltip") Then Return scenarioIndex.Find("MASTooltip", "Tooltip")
            If ContainsAny(typeName, "GroupBox", "Card", "PanelShell", "Surface") Then Return scenarioIndex.Find("MASSurfaces", surfaceFamily)
            If ContainsAny(typeName, "DataGrid") Then Return scenarioIndex.Find("MASDataGrid", "DataGrid")
            If ContainsAny(typeName, "Chart", "PropertyGrid", "MetricCard", "Pivot", "TreeGrid") Then Return scenarioIndex.Find("MASProductSystems", "ProductSystems")
            If ContainsAny(typeName, "Menu", "DropDown", "ContextMenu", "CommandPalette") Then Return scenarioIndex.Find("MASProductSystems", "ProductSystems")
            If ContainsAny(typeName, "DatePicker", "TimePicker", "Calendar") Then Return scenarioIndex.Find("MASAdvancedInputs", "Inputs")
            If ContainsAny(typeName, "Badge", "StatusBanner", "Notification", "Callout", "EmptyState", "Skeleton", "Avatar", "Timeline", "Rating") Then Return scenarioIndex.Find("MASInteractionStates", "States")
            If ContainsAny(typeName, "Navigation", "Breadcrumb", "Pagination", "Rail", "SplitView", "AppLayout", "Dashboard", "MasterDetail", "Carousel", "Kanban", "Agenda", "TransferList", "Filter") Then Return scenarioIndex.Find("MASProductSystems", "ProductSystems")
            If ContainsAny(typeName, "ScrollBar") OrElse ContainsAny(fullName, "Scroll") Then Return scenarioIndex.Find("MASResponsiveLayoutMatrix", "TinyBounds")

            Return Nothing
        End Function

        Private Shared Function ResolveContractCoverageKind(descriptor As MASComponentDescriptor) As MASQualityComponentCoverageKind
            Dim typeName As String = descriptor.ComponentType.Name

            If descriptor.Category = MASComponentCategory.FloatContent Then Return MASQualityComponentCoverageKind.AtomicContractProof
            If descriptor.Category = MASComponentCategory.InternalSurface Then Return MASQualityComponentCoverageKind.SurfaceMaterialProof
            If IsAllowedSystemInventoryProof(descriptor) Then Return MASQualityComponentCoverageKind.SystemInventoryProof
            If descriptor.InteractionRole <> MASInteractionRole.Passive Then Return MASQualityComponentCoverageKind.InteractionStateProof
            If descriptor.InputRole <> MASInputRole.Passive Then Return MASQualityComponentCoverageKind.InteractionStateProof
            If ContainsAny(typeName, "Layout", "Split", "Scroll") Then Return MASQualityComponentCoverageKind.SizeLayoutContractProof
            If ContainsAny(typeName, "Contract") Then Return MASQualityComponentCoverageKind.AtomicContractProof

            Return MASQualityComponentCoverageKind.Unclassified
        End Function

        Private Shared Function IsAllowedSystemInventoryProof(descriptor As MASComponentDescriptor) As Boolean
            If descriptor Is Nothing OrElse descriptor.ComponentType Is Nothing Then Return False
            Dim typeName As String = descriptor.ComponentType.Name
            Dim fullName As String = descriptor.ComponentType.FullName

            If descriptor.Category <> MASComponentCategory.CompositeContent Then Return False

            Return ContainsAny(typeName,
                               "MASTopBarComponent",
                               "MASFileBrowser",
                               "MASFilePickerNavigationBar",
                               "MASPanelShellScrollOverlay") OrElse
                   ContainsAny(fullName,
                               "Nexamas.UI.Components.MASTopBarComponent",
                               "Nexamas.UI.FileSystem.MASFileBrowser",
                               "Nexamas.UI.FileSystem.MASFilePickerNavigationBar",
                               "MASPanelShellFloatHost+MASPanelShellScrollOverlay")
        End Function

        Private Shared Function ResolveContractEvidenceKey(descriptor As MASComponentDescriptor,
                                                           kind As MASQualityComponentCoverageKind) As String
            Select Case kind
                Case MASQualityComponentCoverageKind.AtomicContractProof
                    If descriptor.Category = MASComponentCategory.FloatContent Then Return "MASFloatRuntimeGovernanceClosureManifest"
                    Return "MASArchitectureValidator"
                Case MASQualityComponentCoverageKind.SizeLayoutContractProof
                    Return "MASResponsiveLayoutMatrix / SizeLayoutSystem contracts"
                Case MASQualityComponentCoverageKind.SurfaceMaterialProof
                    Return "MASRenderVerificationSurfaceMaterialProofAudit"
                Case MASQualityComponentCoverageKind.InteractionStateProof
                    Return "MASInteractionStates render-verification proof"
                Case MASQualityComponentCoverageKind.SystemInventoryProof
                    Return "MASRenderVerificationInventorySnapshotBuilder read-model proof"
                Case Else
                    Return String.Empty
            End Select
        End Function

        Private Shared Function CreateEntry(descriptor As MASComponentDescriptor,
                                            kind As MASQualityComponentCoverageKind,
                                            evidenceKey As String,
                                            scenarioRef As ScenarioReference,
                                            summary As String) As MASQualityComponentCoverageEntry
            Return New MASQualityComponentCoverageEntry(descriptor.ComponentType.Name,
                                                        descriptor.ComponentType.FullName,
                                                        descriptor.Category.ToString(),
                                                        kind,
                                                        evidenceKey,
                                                        scenarioRef.Target.Id,
                                                        scenarioRef.Scenario.Id,
                                                        summary)
        End Function

        Private Shared Function ContainsAny(value As String, ParamArray fragments() As String) As Boolean
            Dim normalized As String = If(value, String.Empty)
            If fragments Is Nothing Then Return False

            For Each fragment As String In fragments
                If String.IsNullOrWhiteSpace(fragment) Then Continue For
                If normalized.IndexOf(fragment, StringComparison.OrdinalIgnoreCase) >= 0 Then Return True
            Next

            Return False
        End Function

        Private NotInheritable Class ScenarioReference
            Friend Sub New(target As MASRenderVerificationTarget,
                           scenario As MASRenderVerificationScenario)
                Me.Target = target
                Me.Scenario = scenario
            End Sub

            Friend ReadOnly Property Target As MASRenderVerificationTarget
            Friend ReadOnly Property Scenario As MASRenderVerificationScenario
        End Class

        Private NotInheritable Class ScenarioIndex
            Private ReadOnly _refs As List(Of ScenarioReference)

            Private Sub New(refs As IEnumerable(Of ScenarioReference))
                _refs = New List(Of ScenarioReference)()
                If refs IsNot Nothing Then
                    For Each item As ScenarioReference In refs
                        If item IsNot Nothing Then _refs.Add(item)
                    Next
                End If
            End Sub

            Friend Shared Function Build(targets As IEnumerable(Of MASRenderVerificationTarget)) As ScenarioIndex
                Dim refs As New List(Of ScenarioReference)()
                If targets IsNot Nothing Then
                    For Each target As MASRenderVerificationTarget In targets
                        If target Is Nothing OrElse target.Scenarios Is Nothing Then Continue For
                        For Each scenario As MASRenderVerificationScenario In target.Scenarios
                            If scenario IsNot Nothing Then refs.Add(New ScenarioReference(target, scenario))
                        Next
                    Next
                End If

                Return New ScenarioIndex(refs)
            End Function

            Friend Function FindByScenarioId(scenarioId As String) As ScenarioReference
                If String.IsNullOrWhiteSpace(scenarioId) Then Return Nothing

                For Each item As ScenarioReference In _refs
                    If item Is Nothing OrElse item.Scenario Is Nothing Then Continue For
                    If String.Equals(item.Scenario.Id, scenarioId, StringComparison.OrdinalIgnoreCase) Then Return item
                Next

                Return Nothing
            End Function

            Friend Function FindDirect(typeName As String) As ScenarioReference
                Dim normalized As String = NormalizeTypeKey(typeName)
                Dim withoutComponent As String = StripSuffix(normalized, "Component")
                Dim withoutControl As String = StripSuffix(normalized, "Control")
                Dim withoutContract As String = StripSuffix(normalized, "Contract")

                For Each item As ScenarioReference In _refs
                    If item Is Nothing OrElse item.Scenario Is Nothing Then Continue For
                    Dim key As String = NormalizeTypeKey(item.Scenario.ComponentKey)
                    If EqualsOrdinal(key, normalized) OrElse
                       EqualsOrdinal(key, withoutComponent) OrElse
                       EqualsOrdinal(key, withoutControl) OrElse
                       EqualsOrdinal(key, withoutContract) Then
                        Return item
                    End If
                Next

                Return Nothing
            End Function

            Friend Function Find(componentKey As String,
                                 tagOrStateHint As String) As ScenarioReference
                Dim key As String = If(componentKey, String.Empty).Trim()
                Dim hint As String = If(tagOrStateHint, String.Empty).Trim()

                For Each item As ScenarioReference In _refs
                    If item Is Nothing OrElse item.Scenario Is Nothing Then Continue For
                    If Not String.Equals(item.Scenario.ComponentKey, key, StringComparison.OrdinalIgnoreCase) Then Continue For
                    If String.IsNullOrWhiteSpace(hint) OrElse ScenarioMatchesHint(item.Scenario, hint) Then Return item
                Next

                For Each item As ScenarioReference In _refs
                    If item Is Nothing OrElse item.Scenario Is Nothing Then Continue For
                    If String.Equals(item.Scenario.ComponentKey, key, StringComparison.OrdinalIgnoreCase) Then Return item
                Next

                Return Nothing
            End Function

            Private Shared Function ScenarioMatchesHint(scenario As MASRenderVerificationScenario,
                                                        hint As String) As Boolean
                If scenario Is Nothing Then Return False
                If String.Equals(scenario.StateKey, hint, StringComparison.OrdinalIgnoreCase) Then Return True
                If String.Equals(scenario.DisplayName, hint, StringComparison.OrdinalIgnoreCase) Then Return True
                If scenario.Tags IsNot Nothing Then
                    For Each tag As String In scenario.Tags
                        If String.Equals(tag, hint, StringComparison.OrdinalIgnoreCase) Then Return True
                    Next
                End If

                Return False
            End Function

            Private Shared Function NormalizeTypeKey(value As String) As String
                Return If(value, String.Empty).Trim().Replace(".", String.Empty).Replace("+", String.Empty)
            End Function

            Private Shared Function StripSuffix(value As String,
                                                suffix As String) As String
                Dim normalized As String = If(value, String.Empty)
                If normalized.EndsWith(suffix, StringComparison.OrdinalIgnoreCase) Then
                    Return normalized.Substring(0, normalized.Length - suffix.Length)
                End If

                Return normalized
            End Function

            Private Shared Function EqualsOrdinal(left As String,
                                                  right As String) As Boolean
                Return String.Equals(left, right, StringComparison.OrdinalIgnoreCase)
            End Function
        End Class

    End Class

End Namespace
