Option Strict On
Option Explicit On

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

Namespace Nexamas.UI.Localization

    ''' <summary>
    ''' Severity for the Phase 4 Localization / RTL runtime ownership closure audit.
    ''' </summary>
    Friend Enum MASLocalizationRuntimeOwnershipFindingSeverity
        Info = 0
        Warning = 1
        [Error] = 2
    End Enum

    ''' <summary>
    ''' Immutable finding emitted by the Localization / RTL runtime ownership audit.
    ''' </summary>
    Friend NotInheritable Class MASLocalizationRuntimeOwnershipFinding
        Private Sub New(severity As MASLocalizationRuntimeOwnershipFindingSeverity,
                        code As String,
                        subject As String,
                        message As String)
            Me.Severity = severity
            Me.Code = NormalizeText(code)
            Me.Subject = NormalizeText(subject)
            Me.Message = NormalizeText(message)
        End Sub

        Friend ReadOnly Property Severity As MASLocalizationRuntimeOwnershipFindingSeverity
        Friend ReadOnly Property Code As String
        Friend ReadOnly Property Subject As String
        Friend ReadOnly Property Message As String

        Friend Shared Function Info(code As String,
                                    subject As String,
                                    message As String) As MASLocalizationRuntimeOwnershipFinding
            Return New MASLocalizationRuntimeOwnershipFinding(MASLocalizationRuntimeOwnershipFindingSeverity.Info, code, subject, message)
        End Function

        Friend Shared Function Warning(code As String,
                                       subject As String,
                                       message As String) As MASLocalizationRuntimeOwnershipFinding
            Return New MASLocalizationRuntimeOwnershipFinding(MASLocalizationRuntimeOwnershipFindingSeverity.Warning, code, subject, message)
        End Function

        Friend Shared Function [Error](code As String,
                                       subject As String,
                                       message As String) As MASLocalizationRuntimeOwnershipFinding
            Return New MASLocalizationRuntimeOwnershipFinding(MASLocalizationRuntimeOwnershipFindingSeverity.Error, code, subject, message)
        End Function

        Public Overrides Function ToString() As String
            Return Severity.ToString() & " | " & Code & " | " & Subject & " | " & Message
        End Function

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

    ''' <summary>
    ''' Immutable report for Localization / RTL runtime ownership closure.
    ''' </summary>
    Friend NotInheritable Class MASLocalizationRuntimeOwnershipReport
        Private ReadOnly _findings As ReadOnlyCollection(Of MASLocalizationRuntimeOwnershipFinding)

        Friend Sub New(cultureCount As Integer,
                       stringCount As Integer,
                       probeCount As Integer,
                       publicGatewayMethodCount As Integer,
                       findings As IEnumerable(Of MASLocalizationRuntimeOwnershipFinding))
            Me.CultureCount = Math.Max(0, cultureCount)
            Me.StringCount = Math.Max(0, stringCount)
            Me.ProbeCount = Math.Max(0, probeCount)
            Me.PublicGatewayMethodCount = Math.Max(0, publicGatewayMethodCount)

            Dim normalized As New List(Of MASLocalizationRuntimeOwnershipFinding)()
            If findings IsNot Nothing Then
                For Each finding As MASLocalizationRuntimeOwnershipFinding In findings
                    If finding IsNot Nothing Then normalized.Add(finding)
                Next
            End If
            _findings = New ReadOnlyCollection(Of MASLocalizationRuntimeOwnershipFinding)(normalized)
        End Sub

        Friend ReadOnly Property CultureCount As Integer
        Friend ReadOnly Property StringCount As Integer
        Friend ReadOnly Property ProbeCount As Integer
        Friend ReadOnly Property PublicGatewayMethodCount As Integer

        Friend ReadOnly Property Findings As ReadOnlyCollection(Of MASLocalizationRuntimeOwnershipFinding)
            Get
                Return _findings
            End Get
        End Property

        Friend ReadOnly Property ErrorCount As Integer
            Get
                Return CountBySeverity(MASLocalizationRuntimeOwnershipFindingSeverity.Error)
            End Get
        End Property

        Friend ReadOnly Property WarningCount As Integer
            Get
                Return CountBySeverity(MASLocalizationRuntimeOwnershipFindingSeverity.Warning)
            End Get
        End Property

        Friend ReadOnly Property IsClosed As Boolean
            Get
                Return ProbeCount > 0 AndAlso CultureCount >= 3 AndAlso StringCount >= 3 AndAlso ErrorCount = 0
            End Get
        End Property

        Friend ReadOnly Property Summary As String
            Get
                Return "Localization / RTL runtime ownership: " &
                       CultureCount.ToString(CultureInfo.InvariantCulture) & " culture(s), " &
                       StringCount.ToString(CultureInfo.InvariantCulture) & " localizable string(s), " &
                       ProbeCount.ToString(CultureInfo.InvariantCulture) & " probe(s), " &
                       ErrorCount.ToString(CultureInfo.InvariantCulture) & " error(s), " &
                       WarningCount.ToString(CultureInfo.InvariantCulture) & " warning(s)."
            End Get
        End Property

        Friend Function CreateFindingDetails(maxItems As Integer) As IReadOnlyList(Of String)
            Dim details As New List(Of String)()
            Dim limit As Integer = Math.Max(1, maxItems)
            For Each finding As MASLocalizationRuntimeOwnershipFinding In _findings
                If finding Is Nothing Then Continue For
                details.Add(finding.ToString())
                If details.Count >= limit Then Exit For
            Next
            Return New ReadOnlyCollection(Of String)(details)
        End Function

        Friend Function CreateSummaryDetails() As IReadOnlyList(Of String)
            Dim details As New List(Of String) From {
                Summary,
                "Runtime text route: MASLocalization.ResolveText is the Friend-owned resolver for product strings.",
                "Catalog route: MASLocalization.GetStrings exposes the current Friend-owned catalog without opening a public translation API.",
                "RTL route: MASLocalization.ResolveCulture owns direction and mirrored-layout facts; MASLocalizationRtl only attaches the product page.",
                "Fallback route: unknown keys return a caller fallback or the key without throwing or mutating thread culture."
            }

            For Each finding As MASLocalizationRuntimeOwnershipFinding In _findings
                If finding Is Nothing Then Continue For
                If finding.Severity <> MASLocalizationRuntimeOwnershipFindingSeverity.Info AndAlso details.Count < 18 Then details.Add(finding.ToString())
            Next

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

        Private Function CountBySeverity(severity As MASLocalizationRuntimeOwnershipFindingSeverity) As Integer
            Dim count As Integer = 0
            For Each finding As MASLocalizationRuntimeOwnershipFinding In _findings
                If finding IsNot Nothing AndAlso finding.Severity = severity Then count += 1
            Next
            Return count
        End Function
    End Class

    ''' <summary>
    ''' Phase 4 closure audit for Localization / RTL runtime ownership. It does not introduce a new
    ''' resource system, public translation API, or thread-culture mutation path. It proves that the
    ''' existing LocalizationSystem owns culture facts, runtime text lookup, RTL/mirroring facts, and
    ''' the public host route boundary.
    ''' </summary>
    Friend NotInheritable Class MASLocalizationRuntimeOwnershipAudit
        Private Sub New()
        End Sub

        Friend Shared Function VerifyCurrent() As MASLocalizationRuntimeOwnershipReport
            Dim findings As New List(Of MASLocalizationRuntimeOwnershipFinding)()
            Dim probeCount As Integer = 0

            Dim snapshot As MASLocalizationSnapshot = MASLocalization.CreateSnapshot()
            Dim cultureCount As Integer = If(snapshot Is Nothing, 0, snapshot.CultureCount)
            Dim stringCount As Integer = If(snapshot Is Nothing, 0, snapshot.LocalizedStringCount)

            VerifyReadiness(findings, probeCount)
            VerifyRuntimeTextResolver(snapshot, findings, probeCount)
            VerifyRtlCultureFacts(findings, probeCount)
            Dim publicMethodCount As Integer = VerifyPublicBoundary(findings, probeCount)

            If findings.Count = 0 Then
                findings.Add(MASLocalizationRuntimeOwnershipFinding.Info(
                    "MASLocalizationRuntimeOwnership.NoFindings",
                    "LocalizationSystem",
                    "Localization / RTL runtime ownership probes completed without warning or error findings."))
            End If

            Return New MASLocalizationRuntimeOwnershipReport(cultureCount, stringCount, probeCount, publicMethodCount, findings)
        End Function

        Private Shared Sub VerifyReadiness(findings As IList(Of MASLocalizationRuntimeOwnershipFinding),
                                           ByRef probeCount As Integer)
            probeCount += 1
            Dim readiness As MASLocalizationReadinessReport = MASLocalization.EvaluateReadiness()
            If readiness Is Nothing Then
                findings.Add(MASLocalizationRuntimeOwnershipFinding.[Error](
                    "MASLocalizationRuntimeOwnership.ReadinessMissing",
                    "MASLocalization.EvaluateReadiness",
                    "Localization readiness returned no report, so runtime ownership cannot be trusted."))
                Return
            End If

            If Not readiness.ReadyForPhase6Foundation Then
                findings.Add(MASLocalizationRuntimeOwnershipFinding.[Error](
                    "MASLocalizationRuntimeOwnership.ReadinessBlocked",
                    "MASLocalizationReadinessGate",
                    "Localization readiness is not complete enough for runtime text and RTL ownership."))
            End If

            For Each readinessFinding As MASLocalizationReadinessFinding In readiness.Findings
                If readinessFinding Is Nothing Then Continue For
                If readinessFinding.Severity = MASLocalizationReadinessSeverity.[Error] Then
                    findings.Add(MASLocalizationRuntimeOwnershipFinding.[Error](readinessFinding.Code, "MASLocalizationReadinessGate", readinessFinding.Message))
                ElseIf readinessFinding.Severity = MASLocalizationReadinessSeverity.Warning Then
                    findings.Add(MASLocalizationRuntimeOwnershipFinding.Warning(readinessFinding.Code, "MASLocalizationReadinessGate", readinessFinding.Message))
                End If
            Next
        End Sub

        Private Shared Sub VerifyRuntimeTextResolver(snapshot As MASLocalizationSnapshot,
                                                     findings As IList(Of MASLocalizationRuntimeOwnershipFinding),
                                                     ByRef probeCount As Integer)
            probeCount += 1
            If snapshot Is Nothing Then
                findings.Add(MASLocalizationRuntimeOwnershipFinding.[Error](
                    "MASLocalizationRuntimeOwnership.SnapshotMissing",
                    "MASLocalization.CreateSnapshot",
                    "Localization snapshot is required before runtime text lookup can be verified."))
                Return
            End If

            Dim seenKeys As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)
            For Each entry As MASLocalizationStringEntry In snapshot.LocalizedStrings
                If entry Is Nothing Then Continue For
                If Not seenKeys.Add(entry.Key) Then
                    findings.Add(MASLocalizationRuntimeOwnershipFinding.[Error](
                        "MASLocalizationRuntimeOwnership.DuplicateKey",
                        entry.Key,
                        "Localization string keys must be unique so runtime lookup has one owner."))
                End If

                AssertResolvedText(findings, entry.Key, "en-US", entry.EnglishText)
                AssertResolvedText(findings, entry.Key, "de-DE", entry.GermanText)
                AssertResolvedText(findings, entry.Key, "ar-SY", entry.ArabicText)
                AssertResolvedText(findings, entry.Key, "ar-SA", entry.ArabicText)
            Next

            Dim fallback As String = MASLocalization.ResolveText("Product.Unknown.Key", "de-DE", "Fallback text")
            If Not String.Equals(fallback, "Fallback text", StringComparison.Ordinal) Then
                findings.Add(MASLocalizationRuntimeOwnershipFinding.[Error](
                    "MASLocalizationRuntimeOwnership.FallbackBroken",
                    "MASLocalization.ResolveText",
                    "Unknown localization keys must return caller fallback text before returning the key."))
            End If
        End Sub

        Private Shared Sub AssertResolvedText(findings As IList(Of MASLocalizationRuntimeOwnershipFinding),
                                              key As String,
                                              cultureName As String,
                                              expectedText As String)
            Dim actual As String = MASLocalization.ResolveText(key, cultureName, String.Empty)
            If Not String.Equals(actual, expectedText, StringComparison.Ordinal) Then
                findings.Add(MASLocalizationRuntimeOwnershipFinding.[Error](
                    "MASLocalizationRuntimeOwnership.TextMismatch",
                    key & " @ " & cultureName,
                    "Runtime text resolver returned a value different from the catalog-owned localized string."))
            End If
        End Sub

        Private Shared Sub VerifyRtlCultureFacts(findings As IList(Of MASLocalizationRuntimeOwnershipFinding),
                                                 ByRef probeCount As Integer)
            probeCount += 1
            Dim arabicCulture As MASLocalizationCultureEntry = MASLocalization.ResolveCulture("ar-SY")
            If arabicCulture Is Nothing OrElse Not arabicCulture.IsRightToLeft Then
                findings.Add(MASLocalizationRuntimeOwnershipFinding.[Error](
                    "MASLocalizationRuntimeOwnership.RtlCultureMissing",
                    "ar-SY",
                    "Arabic product culture must resolve as right-to-left."))
            End If

            If arabicCulture Is Nothing OrElse Not arabicCulture.UsesMirroredLayout Then
                findings.Add(MASLocalizationRuntimeOwnershipFinding.[Error](
                    "MASLocalizationRuntimeOwnership.MirroredLayoutMissing",
                    "ar-SY",
                    "Arabic product culture must request mirrored layout facts."))
            End If

            Dim fallbackCulture As MASLocalizationCultureEntry = MASLocalization.ResolveCulture("unknown-culture")
            If fallbackCulture Is Nothing OrElse Not String.Equals(fallbackCulture.CultureName, "en-US", StringComparison.OrdinalIgnoreCase) Then
                findings.Add(MASLocalizationRuntimeOwnershipFinding.[Error](
                    "MASLocalizationRuntimeOwnership.CultureFallbackBroken",
                    "MASLocalization.ResolveCulture",
                    "Unknown cultures must resolve to the English product profile without throwing."))
            End If
        End Sub

        Private Shared Function VerifyPublicBoundary(findings As IList(Of MASLocalizationRuntimeOwnershipFinding),
                                                     ByRef probeCount As Integer) As Integer
            probeCount += 1
            Dim publicMethods As MethodInfo() = GetType(MASLocalizationRtl).GetMethods(BindingFlags.Public Or BindingFlags.Static)
            Dim publicMethodCount As Integer = 0
            For Each method As MethodInfo In publicMethods
                If method Is Nothing OrElse method.IsSpecialName Then Continue For
                If method.DeclaringType IsNot GetType(MASLocalizationRtl) Then Continue For
                publicMethodCount += 1

                Dim name As String = method.Name
                If Not String.Equals(name, "AttachPage", StringComparison.Ordinal) Then
                    findings.Add(MASLocalizationRuntimeOwnershipFinding.[Error](
                        "MASLocalizationRuntimeOwnership.PublicBoundaryLeak",
                        "MASLocalizationRtl." & name,
                        "The public Localization / RTL gateway may expose host attachment only; runtime text lookup and catalogs must remain Friend-owned."))
                End If
            Next

            Return publicMethodCount
        End Function
    End Class

End Namespace
