Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.Globalization

Namespace Nexamas.UI.Certification

    ''' <summary>
    ''' Verifies that the Certification Manifest is closed against the Quality Orchestrator gate graph.
    ''' </summary>
    ''' <remarks>
    ''' This audit is intentionally read-only and manifest-only. It does not execute build scripts, read reports,
    ''' capture render baselines, allocate UI controls, or inspect external files. Its only job is to prevent
    ''' Certification from silently ignoring a Quality Orchestrator gate or accepting duplicate manifest ownership keys.
    ''' </remarks>
    Friend NotInheritable Class MASCertificationOrchestrationClosureAudit
        Private Sub New()
        End Sub

        Friend Shared Function Verify(gateResults As IEnumerable(Of MASQualityGateResult)) As MASCertificationOrchestrationClosureReport
            Dim normalizedGateResults As IReadOnlyList(Of MASQualityGateResult) = NormalizeGateResults(gateResults)
            Dim findings As New List(Of String)()

            If normalizedGateResults.Count = 0 Then
                findings.Add("Quality Orchestrator produced no gate results for Certification closure validation.")
            End If

            Dim declaredManifest As NexamasUICertificationManifest = NexamasUICertificationManifest.CreateDeclaredCurrent()
            Dim declaredEntries As IReadOnlyList(Of NexamasUICertificationEntry)
            If declaredManifest Is Nothing Then
                declaredEntries = New ReadOnlyCollection(Of NexamasUICertificationEntry)(New List(Of NexamasUICertificationEntry)())
            Else
                declaredEntries = declaredManifest.Entries
            End If

            Dim declaredSystemKeys As List(Of String) = CreateDeclaredSystemKeys(declaredEntries, findings)
            If Not ContainsKey(declaredSystemKeys, "certification-orchestration-closure") Then
                findings.Add("Declared Certification Manifest has no entry for the Certification Orchestration Closure Gate itself.")
            End If

            If declaredManifest Is Nothing Then
                findings.Add("Declared Certification Manifest is not available.")
            Else
                If Not declaredManifest.HasCompleteCommercialEvidenceSurface Then
                    findings.Add("Declared Certification Manifest does not have a complete commercial evidence surface.")
                End If

                If declaredManifest.HasCertificationBlocker Then
                    findings.Add("Declared Certification Manifest contains at least one blocker before Quality Orchestrator results are applied.")
                End If
            End If

            For Each result As MASQualityGateResult In normalizedGateResults
                If result Is Nothing Then Continue For

                Dim systemKey As String = Normalize(result.CertificationSystemKey)
                If systemKey.Length = 0 Then
                    findings.Add("Gate '" & Normalize(result.GateKey) & "' has no Certification system key.")
                    Continue For
                End If

                If Not ContainsKey(declaredSystemKeys, systemKey) Then
                    findings.Add("Gate '" & Normalize(result.GateKey) & "' maps to Certification system key '" & systemKey & "', but no declared Certification entry owns that key.")
                End If
            Next

            Return New MASCertificationOrchestrationClosureReport(normalizedGateResults.Count,
                                                                  declaredEntries.Count,
                                                                  findings)
        End Function

        Private Shared Function NormalizeGateResults(gateResults As IEnumerable(Of MASQualityGateResult)) As IReadOnlyList(Of MASQualityGateResult)
            Dim results As New List(Of MASQualityGateResult)()
            If gateResults IsNot Nothing Then
                For Each result As MASQualityGateResult In gateResults
                    If result IsNot Nothing Then results.Add(result)
                Next
            End If

            Return New ReadOnlyCollection(Of MASQualityGateResult)(results)
        End Function

        Private Shared Function CreateDeclaredSystemKeys(entries As IEnumerable(Of NexamasUICertificationEntry), findings As List(Of String)) As List(Of String)
            Dim keys As New List(Of String)()
            If entries Is Nothing Then Return keys

            For Each entry As NexamasUICertificationEntry In entries
                If entry Is Nothing Then
                    findings.Add("Declared Certification Manifest contains a null entry.")
                    Continue For
                End If

                Dim key As String = Normalize(entry.SystemKey)
                If key.Length = 0 Then
                    findings.Add("Declared Certification entry '" & Normalize(entry.DisplayName) & "' has no system key.")
                    Continue For
                End If

                If ContainsKey(keys, key) Then
                    findings.Add("Declared Certification system key '" & key & "' is registered more than once.")
                Else
                    keys.Add(key)
                End If
            Next

            Return keys
        End Function

        Private Shared Function ContainsKey(keys As IEnumerable(Of String), key As String) As Boolean
            Dim normalizedKey As String = Normalize(key)
            If normalizedKey.Length = 0 OrElse keys Is Nothing Then Return False

            For Each candidate As String In keys
                If String.Equals(Normalize(candidate), normalizedKey, StringComparison.OrdinalIgnoreCase) Then Return True
            Next

            Return False
        End Function

        Private Shared Function Normalize(value As String) As String
            Return If(value, String.Empty).Trim()
        End Function
    End Class

    ''' <summary>
    ''' Immutable result for Certification Manifest / Quality Orchestrator closure validation.
    ''' </summary>
    Friend NotInheritable Class MASCertificationOrchestrationClosureReport
        Private ReadOnly _findings As IReadOnlyList(Of String)

        Friend Sub New(orchestratedGateCount As Integer,
                       declaredCertificationEntryCount As Integer,
                       findings As IEnumerable(Of String))
            Me.OrchestratedGateCount = Math.Max(0, orchestratedGateCount)
            Me.DeclaredCertificationEntryCount = Math.Max(0, declaredCertificationEntryCount)
            _findings = New ReadOnlyCollection(Of String)(NormalizeFindings(findings))
        End Sub

        Friend ReadOnly Property OrchestratedGateCount As Integer
        Friend ReadOnly Property DeclaredCertificationEntryCount As Integer

        Friend ReadOnly Property Findings As IReadOnlyList(Of String)
            Get
                Return _findings
            End Get
        End Property

        Friend ReadOnly Property FindingCount As Integer
            Get
                Return Findings.Count
            End Get
        End Property

        Friend ReadOnly Property IsClosed As Boolean
            Get
                Return OrchestratedGateCount > 0 AndAlso FindingCount = 0
            End Get
        End Property

        Friend ReadOnly Property Summary As String
            Get
                Return OrchestratedGateCount.ToString(CultureInfo.InvariantCulture) &
                       " orchestrated gate(s), " &
                       DeclaredCertificationEntryCount.ToString(CultureInfo.InvariantCulture) &
                       " declared Certification entrie(s), " &
                       FindingCount.ToString(CultureInfo.InvariantCulture) &
                       " closure finding(s)."
            End Get
        End Property

        Friend Function CreateFindingDetails(maxCount As Integer) As IReadOnlyList(Of String)
            Dim details As New List(Of String)()
            Dim limit As Integer = Math.Max(1, maxCount)

            For Each finding As String In Findings
                details.Add(finding)
                If details.Count >= limit Then Exit For
            Next

            Return New ReadOnlyCollection(Of String)(details)
        End Function

        Friend Function CreateSummaryDetails() As IReadOnlyList(Of String)
            Return New ReadOnlyCollection(Of String)(New List(Of String) From {
                "Orchestrated gate count: " & OrchestratedGateCount.ToString(CultureInfo.InvariantCulture),
                "Declared Certification entry count: " & DeclaredCertificationEntryCount.ToString(CultureInfo.InvariantCulture),
                "Closure finding count: " & FindingCount.ToString(CultureInfo.InvariantCulture)
            })
        End Function

        Private Shared Function NormalizeFindings(findings As IEnumerable(Of String)) As List(Of String)
            Dim result As New List(Of String)()
            If findings Is Nothing Then Return result

            For Each finding As String In findings
                Dim normalized As String = If(finding, String.Empty).Trim()
                If normalized.Length > 0 Then result.Add(normalized)
            Next

            Return result
        End Function
    End Class

End Namespace
