Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.IO
Imports Nexamas.UI.Application
Imports Nexamas.UI.Diagnostics

Namespace Nexamas.UI.Quality

    ''' <summary>
    ''' Commercial-readiness proof that failures inside the diagnostics machinery itself do not
    ''' escape only through Debug/Trace and do not recursively re-enter failing subscribers.
    ''' </summary>
    ''' <remarks>
    ''' The gate exercises the real diagnostics subscriber boundary and the release diagnostics
    ''' write-failure path. Both failures must be retained in the official runtime-fault channel
    ''' and visible through the public MASApplication.Diagnostics snapshot.
    ''' </remarks>
    Friend NotInheritable Class MASRuntimeDiagnosticsFailureChannelGate

        Private Const SubscriberTriggerContext As String = "MASRuntimeDiagnosticsFailureChannelGate.SubscriberTrigger"
        Private Const LoggingFailureProbeMessage As String = "Simulated release diagnostics write failure."
        Private Const SubscriberFailureProbeMessage As String = "Simulated diagnostics subscriber failure."

        Private Sub New()
        End Sub

        Friend Shared Function Passes() As Boolean
            Return EvaluateFindings().Count = 0
        End Function

        Friend Shared Function EvaluateFindings() As IReadOnlyList(Of MASCommercialReadinessFinding)
            Dim findings As New List(Of MASCommercialReadinessFinding)()

            ValidateLoggingFailureVisibility(findings)
            ValidateSubscriberFailureVisibility(findings)

            Return findings
        End Function

        Private Shared Sub ValidateLoggingFailureVisibility(findings As List(Of MASCommercialReadinessFinding))
            Try
                MASExceptionSilencer.RecordReleaseDiagnosticWriteFailureForProof(
                    New IOException(LoggingFailureProbeMessage))
            Catch ex As Exception
                findings.Add(New MASCommercialReadinessFinding("runtime-faults", "DiagnosticsLoggingFailureProbe", MASCommercialReadinessFindingSeverity.Error, "Diagnostics logging-failure proof threw unexpectedly: " & ex.GetType().FullName))
                Return
            End Try

            Dim expectedContext As String = MASRuntimeFaultChannel.DiagnosticsLoggingFailureContext & ".ReleaseDiagnosticWrite"
            AssertRuntimeRecordVisible(expectedContext, LoggingFailureProbeMessage, findings)
            AssertPublicSnapshotVisible(expectedContext, LoggingFailureProbeMessage, findings)
        End Sub

        Private Shared Sub ValidateSubscriberFailureVisibility(findings As List(Of MASCommercialReadinessFinding))
            Dim throwingHandler As EventHandler(Of MASRuntimeFaultEventArgs) =
                Sub(sender As Object, args As MASRuntimeFaultEventArgs)
                    Throw New InvalidOperationException(SubscriberFailureProbeMessage)
                End Sub

            AddHandler MASRuntimeFaultChannel.FaultRecorded, throwingHandler
            Try
                MASRuntimeFaultChannel.Publish(New MASRuntimeFaultRecord(
                    timestampUtc:=Nexamas.UI.Timing.MASTimeProvider.UtcNowOffset(),
                    severity:=MASExceptionSeverity.Recoverable,
                    category:=MASExceptionHandlingCategory.DiagnosticOnly,
                    context:=SubscriberTriggerContext,
                    sourceFile:="<Nexamas.UI.Diagnostics.Proof>",
                    sourceLine:=0,
                    exceptionTypeName:=GetType(ApplicationException).FullName,
                    message:="Triggering diagnostics subscriber failure proof.",
                    occurrenceCount:=1,
                    isFirstOccurrence:=True))
            Catch ex As Exception
                findings.Add(New MASCommercialReadinessFinding("runtime-faults", "DiagnosticsSubscriberFailureProbe", MASCommercialReadinessFindingSeverity.Error, "Diagnostics subscriber-failure proof escaped the diagnostics boundary: " & ex.GetType().FullName))
                Return
            Finally
                RemoveHandler MASRuntimeFaultChannel.FaultRecorded, throwingHandler
            End Try

            AssertRuntimeRecordVisible(MASRuntimeFaultChannel.DiagnosticsSubscriberFailureContext, SubscriberFailureProbeMessage, findings)
            AssertPublicSnapshotVisible(MASRuntimeFaultChannel.DiagnosticsSubscriberFailureContext, SubscriberFailureProbeMessage, findings)
        End Sub

        Private Shared Sub AssertRuntimeRecordVisible(expectedContext As String,
                                                     expectedMessageFragment As String,
                                                     findings As List(Of MASCommercialReadinessFinding))
            Dim records As IReadOnlyList(Of MASRuntimeFaultRecord) = MASRuntimeFaultChannel.GetRecentFaultsSnapshot()
            If Not ContainsRuntimeRecord(records, expectedContext, expectedMessageFragment) Then
                findings.Add(New MASCommercialReadinessFinding("runtime-faults", "DiagnosticsFailureRuntimeVisibility", MASCommercialReadinessFindingSeverity.Error, "Diagnostics failure context " & expectedContext & " did not reach the retained runtime-fault channel."))
            End If
        End Sub

        Private Shared Sub AssertPublicSnapshotVisible(expectedContext As String,
                                                       expectedMessageFragment As String,
                                                       findings As List(Of MASCommercialReadinessFinding))
            Dim snapshot As MASApplicationDiagnosticsSnapshot = MASApplication.Diagnostics.GetSnapshot(256)
            If snapshot Is Nothing OrElse snapshot.RecentFaults Is Nothing Then
                findings.Add(New MASCommercialReadinessFinding("runtime-faults", "DiagnosticsFailurePublicSnapshot", MASCommercialReadinessFindingSeverity.Error, "Public diagnostics snapshot was not available while validating diagnostics failure visibility."))
                Return
            End If

            For Each summary As MASApplicationDiagnosticsFaultSummary In snapshot.RecentFaults
                If summary Is Nothing Then Continue For
                If Not String.Equals(summary.Context, expectedContext, StringComparison.Ordinal) Then Continue For
                If summary.Message Is Nothing OrElse summary.Message.IndexOf(expectedMessageFragment, StringComparison.Ordinal) < 0 Then Continue For
                If Not String.Equals(summary.Category, MASExceptionHandlingCategory.DiagnosticOnly.ToString(), StringComparison.Ordinal) Then
                    findings.Add(New MASCommercialReadinessFinding("runtime-faults", "DiagnosticsFailurePublicClassification", MASCommercialReadinessFindingSeverity.Error, "Public diagnostics snapshot exposed diagnostics failure " & expectedContext & " with category " & summary.Category & " instead of DiagnosticOnly."))
                End If
                Return
            Next

            findings.Add(New MASCommercialReadinessFinding("runtime-faults", "DiagnosticsFailurePublicSnapshot", MASCommercialReadinessFindingSeverity.Error, "Diagnostics failure context " & expectedContext & " was not visible through MASApplication.Diagnostics.GetSnapshot."))
        End Sub

        Private Shared Function ContainsRuntimeRecord(records As IReadOnlyList(Of MASRuntimeFaultRecord),
                                                      expectedContext As String,
                                                      expectedMessageFragment As String) As Boolean
            If records Is Nothing Then Return False

            For index As Integer = records.Count - 1 To 0 Step -1
                Dim record As MASRuntimeFaultRecord = records(index)
                If record Is Nothing Then Continue For
                If Not String.Equals(record.Context, expectedContext, StringComparison.Ordinal) Then Continue For
                If record.Message Is Nothing OrElse record.Message.IndexOf(expectedMessageFragment, StringComparison.Ordinal) < 0 Then Continue For
                If record.Category <> MASExceptionHandlingCategory.DiagnosticOnly Then Return False
                Return True
            Next

            Return False
        End Function

    End Class

End Namespace
