Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.Globalization
Imports System.IO
Imports System.Reflection
Imports Nexamas.UI.Application
Imports Nexamas.UI.Reporting
Imports Nexamas.UI.Visualization

Namespace Nexamas.UI.Output

    ''' <summary>
    ''' Severity for the Phase 5 Output product route closure audit.
    ''' </summary>
    Friend Enum MASOutputApplicationRouteFindingSeverity
        Info = 0
        Warning = 1
        [Error] = 2
    End Enum

    ''' <summary>
    ''' Immutable finding emitted by the Output product route audit.
    ''' </summary>
    Friend NotInheritable Class MASOutputApplicationRouteFinding
        Private Sub New(severity As MASOutputApplicationRouteFindingSeverity,
                        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 MASOutputApplicationRouteFindingSeverity
        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 MASOutputApplicationRouteFinding
            Return New MASOutputApplicationRouteFinding(MASOutputApplicationRouteFindingSeverity.Info, code, subject, message)
        End Function

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

        Friend Shared Function [Error](code As String,
                                       subject As String,
                                       message As String) As MASOutputApplicationRouteFinding
            Return New MASOutputApplicationRouteFinding(MASOutputApplicationRouteFindingSeverity.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 Application-owned Output product closure.
    ''' </summary>
    Friend NotInheritable Class MASOutputApplicationRouteReport
        Private ReadOnly _findings As ReadOnlyCollection(Of MASOutputApplicationRouteFinding)

        Friend Sub New(chartPlanCount As Integer,
                       chartPrimitiveCount As Integer,
                       reportPageCount As Integer,
                       reportElementCount As Integer,
                       visualCaptureByteCount As Integer,
                       directoryArtifactCount As Integer,
                       productBundleManifestCount As Integer,
                       overwriteProtectionProofCount As Integer,
                       probeCount As Integer,
                       findings As IEnumerable(Of MASOutputApplicationRouteFinding))
            Me.ChartPlanCount = Math.Max(0, chartPlanCount)
            Me.ChartPrimitiveCount = Math.Max(0, chartPrimitiveCount)
            Me.ReportPageCount = Math.Max(0, reportPageCount)
            Me.ReportElementCount = Math.Max(0, reportElementCount)
            Me.VisualCaptureByteCount = Math.Max(0, visualCaptureByteCount)
            Me.DirectoryArtifactCount = Math.Max(0, directoryArtifactCount)
            Me.ProductBundleManifestCount = Math.Max(0, productBundleManifestCount)
            Me.OverwriteProtectionProofCount = Math.Max(0, overwriteProtectionProofCount)
            Me.ProbeCount = Math.Max(0, probeCount)

            Dim normalized As New List(Of MASOutputApplicationRouteFinding)()
            If findings IsNot Nothing Then
                For Each finding As MASOutputApplicationRouteFinding In findings
                    If finding IsNot Nothing Then normalized.Add(finding)
                Next
            End If

            _findings = New ReadOnlyCollection(Of MASOutputApplicationRouteFinding)(normalized)
        End Sub

        Friend ReadOnly Property ChartPlanCount As Integer
        Friend ReadOnly Property ChartPrimitiveCount As Integer
        Friend ReadOnly Property ReportPageCount As Integer
        Friend ReadOnly Property ReportElementCount As Integer
        Friend ReadOnly Property VisualCaptureByteCount As Integer
        Friend ReadOnly Property DirectoryArtifactCount As Integer
        Friend ReadOnly Property ProductBundleManifestCount As Integer
        Friend ReadOnly Property OverwriteProtectionProofCount As Integer
        Friend ReadOnly Property ProbeCount As Integer

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

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

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

        Friend ReadOnly Property IsClosed As Boolean
            Get
                Return ProbeCount > 0 AndAlso
                       ChartPlanCount > 0 AndAlso
                       ChartPrimitiveCount > 0 AndAlso
                       ReportPageCount > 0 AndAlso
                       ReportElementCount > 0 AndAlso
                       VisualCaptureByteCount > 0 AndAlso
                       DirectoryArtifactCount >= 4 AndAlso
                       ProductBundleManifestCount > 0 AndAlso
                       OverwriteProtectionProofCount > 0 AndAlso
                       ErrorCount = 0
            End Get
        End Property

        Friend ReadOnly Property Summary As String
            Get
                Return "Output product route closure: " &
                       ChartPlanCount.ToString(CultureInfo.InvariantCulture) & " chart plan(s), " &
                       ChartPrimitiveCount.ToString(CultureInfo.InvariantCulture) & " chart primitive(s), " &
                       ReportPageCount.ToString(CultureInfo.InvariantCulture) & " report page(s), " &
                       ReportElementCount.ToString(CultureInfo.InvariantCulture) & " report element(s), " &
                       VisualCaptureByteCount.ToString(CultureInfo.InvariantCulture) & " visual-capture byte(s), " &
                       DirectoryArtifactCount.ToString(CultureInfo.InvariantCulture) & " directory artifact(s), " &
                       ProductBundleManifestCount.ToString(CultureInfo.InvariantCulture) & " product-bundle manifest(s), " &
                       OverwriteProtectionProofCount.ToString(CultureInfo.InvariantCulture) & " overwrite-protection proof(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 MASOutputApplicationRouteFinding 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,
                "Application route: MASApplication exposes a public MASApplicationOutput facade and retains a Friend MASApplicationOutputGateway runtime route.",
                "Chart route: MASApplication.Output exports chart foundation manifests without exposing MASChartFoundationBuilder.",
                "Report route: MASApplication.Output exports report preview manifests without invoking OS printing or claiming PDF support.",
                "Visual route: MASApplication.Output exports PNG visual capture through the official Render Verification capture catalog without exposing capture hosts or scenario internals.",
                "Catalog route: MASApplication.Output exposes a formal capability catalog with stable ids, default file names, supported targets, and supported/not-available statuses.",
                "Destination route: MASApplication.Output supports memory, direct file targets, and directory targets with stable product file names and overwrite protection.",
                "Bundle route: MASApplication.Output exports a product evidence bundle that materializes capability catalog, readiness, chart, report, visual capture, and a central JSON manifest together.",
                "Boundary route: public Output contracts are explicit; foundation builders and render capture internals remain Friend."
            }

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

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

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

    ''' <summary>
    ''' Product closure audit for Output. It proves that Chart/Visualization, Report/Print, and Render Verification
    ''' foundations are consumed through one Application-owned public facade, while internal builders/capture hosts remain
    ''' governed and unsupported routes such as PDF/print are reported honestly.
    ''' </summary>
    Friend NotInheritable Class MASOutputApplicationRouteAudit
        Private Sub New()
        End Sub

        Friend Shared Function VerifyCurrent() As MASOutputApplicationRouteReport
            Dim findings As New List(Of MASOutputApplicationRouteFinding)()
            Dim probeCount As Integer = 0
            Dim visualCaptureByteCount As Integer = 0
            Dim directoryArtifactCount As Integer = 0
            Dim productBundleManifestCount As Integer = 0
            Dim overwriteProtectionProofCount As Integer = 0
            Dim snapshot As MASApplicationOutputSnapshot = VerifyApplicationGateway(findings, probeCount, visualCaptureByteCount, directoryArtifactCount, productBundleManifestCount, overwriteProtectionProofCount)
            VerifyFoundationBuilders(findings, probeCount)
            VerifyPublicBoundary(findings, probeCount)

            If findings.Count = 0 Then
                findings.Add(MASOutputApplicationRouteFinding.Info(
                    "MASOutput.ApplicationRoute.NoFindings",
                    "OutputSystem",
                    "Output product route probes completed without warning or error findings."))
            End If

            Return New MASOutputApplicationRouteReport(
                chartPlanCount:=If(snapshot Is Nothing, 0, snapshot.ChartPlanCount),
                chartPrimitiveCount:=If(snapshot Is Nothing, 0, snapshot.ChartPrimitiveCount),
                reportPageCount:=If(snapshot Is Nothing, 0, snapshot.ReportPageCount),
                reportElementCount:=If(snapshot Is Nothing, 0, snapshot.ReportElementCount),
                visualCaptureByteCount:=visualCaptureByteCount,
                directoryArtifactCount:=directoryArtifactCount,
                productBundleManifestCount:=productBundleManifestCount,
                overwriteProtectionProofCount:=overwriteProtectionProofCount,
                probeCount:=probeCount,
                findings:=findings)
        End Function

        Private Shared Function VerifyApplicationGateway(findings As IList(Of MASOutputApplicationRouteFinding),
                                                         ByRef probeCount As Integer,
                                                         ByRef visualCaptureByteCount As Integer,
                                                         ByRef directoryArtifactCount As Integer,
                                                         ByRef productBundleManifestCount As Integer,
                                                         ByRef overwriteProtectionProofCount As Integer) As MASApplicationOutputSnapshot
            probeCount += 1

            Dim publicOutputProperty As PropertyInfo = GetType(MASApplication).GetProperty("Output", BindingFlags.Instance Or BindingFlags.Public)
            If publicOutputProperty Is Nothing OrElse publicOutputProperty.PropertyType IsNot GetType(MASApplicationOutput) Then
                findings.Add(MASOutputApplicationRouteFinding.[Error](
                    "MASOutput.ApplicationRoute.MissingPublicFacadeProperty",
                    "MASApplication.Output",
                    "MASApplication must expose a public MASApplicationOutput facade as the official SDK Output product."))
            End If

            Dim internalGatewayProperty As PropertyInfo = GetType(MASApplication).GetProperty("OutputGateway", BindingFlags.Instance Or BindingFlags.NonPublic)
            If internalGatewayProperty Is Nothing OrElse internalGatewayProperty.PropertyType IsNot GetType(MASApplicationOutputGateway) Then
                findings.Add(MASOutputApplicationRouteFinding.[Error](
                    "MASOutput.ApplicationRoute.MissingInternalGatewayProperty",
                    "MASApplication.OutputGateway",
                    "MASApplication must retain a Friend MASApplicationOutputGateway so the public facade does not bypass Application ownership."))
            End If

            Dim application As MASApplication = Nothing
            Try
                application = MASApplication.Create()

                If application.Output Is Nothing Then
                    findings.Add(MASOutputApplicationRouteFinding.[Error](
                        "MASOutput.ApplicationRoute.NullPublicFacade",
                        "MASApplication.Output",
                        "The public Output facade returned Nothing."))
                    Return Nothing
                End If

                Dim productSnapshot As MASOutputReadinessSnapshot = application.Output.GetReadinessSnapshot()
                If productSnapshot Is Nothing Then
                    findings.Add(MASOutputApplicationRouteFinding.[Error](
                        "MASOutput.ApplicationRoute.NullProductSnapshot",
                        "MASApplication.Output.GetReadinessSnapshot",
                        "The public Output facade returned no product readiness snapshot."))
                ElseIf Not productSnapshot.IsProductReady Then
                    findings.Add(MASOutputApplicationRouteFinding.[Error](
                        "MASOutput.ApplicationRoute.ProductNotReady",
                        "MASOutputReadinessSnapshot",
                        productSnapshot.Summary))
                End If

                Dim productCatalog As MASOutputCapabilityCatalog = application.Output.GetCapabilityCatalog()
                If productCatalog Is Nothing Then
                    findings.Add(MASOutputApplicationRouteFinding.[Error](
                        "MASOutput.ApplicationRoute.NullCapabilityCatalog",
                        "MASApplication.Output.GetCapabilityCatalog",
                        "The public Output facade returned no capability catalog."))
                ElseIf Not productCatalog.IsProductCatalogClosed Then
                    findings.Add(MASOutputApplicationRouteFinding.[Error](
                        "MASOutput.ApplicationRoute.CapabilityCatalogNotClosed",
                        "MASOutputCapabilityCatalog",
                        productCatalog.Summary))
                End If

                If Not application.Output.CanExport(MASOutputArtifactKind.OutputCapabilityCatalog, MASOutputFormat.Json) Then
                    findings.Add(MASOutputApplicationRouteFinding.[Error](
                        "MASOutput.ApplicationRoute.CapabilityCatalogQueryFailed",
                        "MASApplication.Output.CanExport",
                        "The public Output facade must report the capability-catalog JSON route as exportable."))
                End If

                If application.Output.CanExport(MASOutputArtifactKind.PdfDocument, MASOutputFormat.Pdf) Then
                    findings.Add(MASOutputApplicationRouteFinding.[Error](
                        "MASOutput.ApplicationRoute.PdfCatalogFalsePositive",
                        "MASApplication.Output.CanExport",
                        "PDF must not be reported as exportable until a real PDF encoder is connected."))
                End If

                VerifyPublicExport(application.Output,
                                   MASOutputRequest.CapabilityCatalogManifest(MASOutputFormat.Json),
                                   "MASOutput.ApplicationRoute.CapabilityCatalogExport",
                                   findings,
                                   probeCount)

                VerifyPublicExport(application.Output,
                                   MASOutputRequest.ReadinessManifest(MASOutputFormat.Json),
                                   "MASOutput.ApplicationRoute.ReadinessExport",
                                   findings,
                                   probeCount)
                VerifyPublicExport(application.Output,
                                   MASOutputRequest.ChartFoundationManifest(MASOutputFormat.Json),
                                   "MASOutput.ApplicationRoute.ChartExport",
                                   findings,
                                   probeCount)
                VerifyPublicExport(application.Output,
                                   MASOutputRequest.ReportPreviewManifest(MASOutputFormat.Json),
                                   "MASOutput.ApplicationRoute.ReportExport",
                                   findings,
                                   probeCount)

                Dim visualCaptureResult As MASOutputResult = VerifyPublicExport(application.Output,
                                                                                MASOutputRequest.VisualCapturePng(MASOutputTarget.Memory(), MASOutputOptions.ForVisualCapture("showcase-real-buttons-light-1x")),
                                                                                "MASOutput.ApplicationRoute.VisualCaptureExport",
                                                                                findings,
                                                                                probeCount)
                If visualCaptureResult IsNot Nothing Then visualCaptureByteCount = Math.Max(visualCaptureByteCount, visualCaptureResult.ContentByteCount)

                VerifyDirectoryAndOverwriteTargets(application.Output,
                                                   findings,
                                                   probeCount,
                                                   directoryArtifactCount,
                                                   productBundleManifestCount,
                                                   overwriteProtectionProofCount)

                Dim unsupportedPdf As MASOutputResult = application.Output.Export(New MASOutputRequest(MASOutputArtifactKind.PdfDocument, MASOutputFormat.Pdf, MASOutputTarget.Memory()))
                If unsupportedPdf Is Nothing OrElse unsupportedPdf.Status <> MASOutputOperationStatus.Unsupported Then
                    findings.Add(MASOutputApplicationRouteFinding.[Error](
                        "MASOutput.ApplicationRoute.UnsupportedPdfClaim",
                        "MASApplication.Output.Export(PDF)",
                        "PDF must return an explicit Unsupported result until a real PDF encoder is connected."))
                End If

                If application.OutputGateway Is Nothing Then
                    findings.Add(MASOutputApplicationRouteFinding.[Error](
                        "MASOutput.ApplicationRoute.NullInternalGateway",
                        "MASApplication.OutputGateway",
                        "The Application-owned internal Output gateway returned Nothing."))
                    Return Nothing
                End If

                Dim snapshot As MASApplicationOutputSnapshot = application.OutputGateway.CreateReadinessSnapshot()
                If snapshot Is Nothing Then
                    findings.Add(MASOutputApplicationRouteFinding.[Error](
                        "MASOutput.ApplicationRoute.NullSnapshot",
                        "MASApplicationOutputGateway.CreateReadinessSnapshot",
                        "The Application-owned Output gateway returned no readiness snapshot."))
                    Return Nothing
                End If

                If Not snapshot.IsReady Then
                    findings.Add(MASOutputApplicationRouteFinding.[Error](
                        "MASOutput.ApplicationRoute.NotReady",
                        "MASApplicationOutputSnapshot",
                        snapshot.Summary))
                End If

                Return snapshot
            Finally
                If application IsNot Nothing Then application.Dispose()
            End Try
        End Function

        Private Shared Sub VerifyDirectoryAndOverwriteTargets(output As MASApplicationOutput,
                                                                   findings As IList(Of MASOutputApplicationRouteFinding),
                                                                   ByRef probeCount As Integer,
                                                                   ByRef directoryArtifactCount As Integer,
                                                                   ByRef productBundleManifestCount As Integer,
                                                                   ByRef overwriteProtectionProofCount As Integer)
            probeCount += 1
            If output Is Nothing Then
                findings.Add(MASOutputApplicationRouteFinding.[Error](
                    "MASOutput.ApplicationRoute.DirectoryTargetProbeSkipped",
                    "MASApplication.Output",
                    "Directory/file destination probes could not run because the public Output facade was Nothing."))
                Return
            End If

            Dim tempRoot As String = Path.Combine(Path.GetTempPath(), "NexamasUI_OutputAudit_" & Guid.NewGuid().ToString("N"))
            Try
                System.IO.Directory.CreateDirectory(tempRoot)

                Dim catalogDirectoryResult As MASOutputResult = VerifyPublicExport(output,
                                                                                   MASOutputRequest.CapabilityCatalogManifest(MASOutputFormat.Json, MASOutputTarget.Directory(tempRoot)),
                                                                                   "MASOutput.ApplicationRoute.DirectoryCapabilityCatalogExport",
                                                                                   findings,
                                                                                   probeCount)
                If VerifyWrittenArtifact(catalogDirectoryResult, "mas-output-capabilities.json", findings, "MASOutput.ApplicationRoute.DirectoryCapabilityCatalogFile") Then
                    directoryArtifactCount += 1
                End If

                Dim readinessDirectoryResult As MASOutputResult = VerifyPublicExport(output,
                                                                                     MASOutputRequest.ReadinessManifest(MASOutputFormat.Json, MASOutputTarget.Directory(tempRoot)),
                                                                                     "MASOutput.ApplicationRoute.DirectoryReadinessExport",
                                                                                     findings,
                                                                                     probeCount)
                If VerifyWrittenArtifact(readinessDirectoryResult, "mas-output-readiness.json", findings, "MASOutput.ApplicationRoute.DirectoryReadinessFile") Then
                    directoryArtifactCount += 1
                End If

                Dim visualDirectory As String = Path.Combine(tempRoot, "visual")
                Dim visualDirectoryResult As MASOutputResult = VerifyPublicExport(output,
                                                                                  MASOutputRequest.VisualCapturePng(MASOutputTarget.Directory(visualDirectory), MASOutputOptions.ForVisualCapture("showcase-real-buttons-light-1x")),
                                                                                  "MASOutput.ApplicationRoute.DirectoryVisualCaptureExport",
                                                                                  findings,
                                                                                  probeCount)
                If VerifyWrittenArtifact(visualDirectoryResult, "mas-visual-capture.png", findings, "MASOutput.ApplicationRoute.DirectoryVisualCaptureFile") Then
                    directoryArtifactCount += 1
                End If

                Dim bundleDirectory As String = Path.Combine(tempRoot, "bundle")
                Dim bundleResult As MASOutputResult = VerifyPublicExport(output,
                                                                         MASOutputRequest.ProductEvidenceBundle(MASOutputTarget.Directory(bundleDirectory), MASOutputOptions.ForVisualCapture("showcase-real-buttons-light-1x")),
                                                                         "MASOutput.ApplicationRoute.ProductEvidenceBundleExport",
                                                                         findings,
                                                                         probeCount)
                If VerifyWrittenArtifact(bundleResult, "mas-output-product-bundle.json", findings, "MASOutput.ApplicationRoute.ProductEvidenceBundleFile") Then
                    directoryArtifactCount += 1
                    productBundleManifestCount += 1
                    VerifyBundleSupportingArtifacts(bundleDirectory, findings)
                End If

                Dim overwritePath As String = Path.Combine(tempRoot, "overwrite-proof.json")
                Dim noOverwriteOptions As New MASOutputOptions(overwriteExisting:=False)
                Dim firstOverwriteProbe As MASOutputResult = output.Export(MASOutputRequest.ReadinessManifest(MASOutputFormat.Json, MASOutputTarget.File(overwritePath), noOverwriteOptions))
                Dim secondOverwriteProbe As MASOutputResult = output.Export(MASOutputRequest.ReadinessManifest(MASOutputFormat.Json, MASOutputTarget.File(overwritePath), noOverwriteOptions))
                probeCount += 2

                If firstOverwriteProbe IsNot Nothing AndAlso firstOverwriteProbe.Succeeded AndAlso
                   secondOverwriteProbe IsNot Nothing AndAlso
                   secondOverwriteProbe.Status = MASOutputOperationStatus.Failed AndAlso
                   secondOverwriteProbe.FailureReason = MASOutputFailureReason.DestinationWriteFailed Then
                    overwriteProtectionProofCount += 1
                Else
                    findings.Add(MASOutputApplicationRouteFinding.[Error](
                        "MASOutput.ApplicationRoute.OverwriteProtectionNotEnforced",
                        "MASOutputOptions.OverwriteExisting",
                        "File-backed Output targets must honor overwriteExisting:=False and return DestinationWriteFailed on a second write."))
                End If
            Catch ex As IOException
                findings.Add(MASOutputApplicationRouteFinding.[Error](
                    "MASOutput.ApplicationRoute.DestinationProbeIoFailure",
                    "MASOutputTarget.File/Directory",
                    If(ex.Message, String.Empty)))
            Catch ex As UnauthorizedAccessException
                findings.Add(MASOutputApplicationRouteFinding.[Error](
                    "MASOutput.ApplicationRoute.DestinationProbeAccessFailure",
                    "MASOutputTarget.File/Directory",
                    If(ex.Message, String.Empty)))
            Finally
                Try
                    If System.IO.Directory.Exists(tempRoot) Then System.IO.Directory.Delete(tempRoot, True)
                Catch masCaughtExceptionCleanup As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowFileSystemBoundary(
                        masCaughtExceptionCleanup,
                        "MASOutputApplicationRouteAudit.TempRootCleanup")
                End Try
            End Try
        End Sub

        Private Shared Function VerifyWrittenArtifact(result As MASOutputResult,
                                                      expectedFileName As String,
                                                      findings As IList(Of MASOutputApplicationRouteFinding),
                                                      code As String) As Boolean
            If result Is Nothing OrElse Not result.Succeeded OrElse result.ArtifactPath.Length = 0 Then
                findings.Add(MASOutputApplicationRouteFinding.[Error](code, "MASOutputResult.ArtifactPath", "File-backed Output export did not return a successful artifact path."))
                Return False
            End If

            If Not File.Exists(result.ArtifactPath) Then
                findings.Add(MASOutputApplicationRouteFinding.[Error](code, result.ArtifactPath, "File-backed Output export reported a path that does not exist."))
                Return False
            End If

            If Not String.Equals(Path.GetFileName(result.ArtifactPath), expectedFileName, StringComparison.OrdinalIgnoreCase) Then
                findings.Add(MASOutputApplicationRouteFinding.[Error](code, result.ArtifactPath, "Directory target did not use the expected stable product file name: " & expectedFileName))
                Return False
            End If

            Dim length As Long = New FileInfo(result.ArtifactPath).Length
            If length <= 0 Then
                findings.Add(MASOutputApplicationRouteFinding.[Error](code, result.ArtifactPath, "File-backed Output export wrote an empty artifact."))
                Return False
            End If

            Return True
        End Function

        Private Shared Sub VerifyBundleSupportingArtifacts(bundleDirectory As String,
                                                               findings As IList(Of MASOutputApplicationRouteFinding))
            Dim normalizedDirectory As String = If(bundleDirectory, String.Empty).Trim()
            If normalizedDirectory.Length = 0 Then
                findings.Add(MASOutputApplicationRouteFinding.[Error](
                    "MASOutput.ApplicationRoute.ProductEvidenceBundleInvalidDirectory",
                    "MASOutputTarget.Directory",
                    "Product evidence bundle verification received an empty directory path."))
                Return
            End If

            Dim expectedFiles As String() = {
                "mas-output-capabilities.json",
                "mas-output-readiness.json",
                "mas-chart-foundation.json",
                "mas-report-preview.txt",
                "mas-visual-capture.png",
                "mas-output-product-bundle.json"
            }

            For Each expectedFile As String In expectedFiles
                Dim fullPath As String = Path.Combine(normalizedDirectory, expectedFile)
                If Not File.Exists(fullPath) Then
                    findings.Add(MASOutputApplicationRouteFinding.[Error](
                        "MASOutput.ApplicationRoute.ProductEvidenceBundleMissingArtifact",
                        fullPath,
                        "The product evidence bundle did not materialize the expected supporting artifact."))
                ElseIf New FileInfo(fullPath).Length <= 0 Then
                    findings.Add(MASOutputApplicationRouteFinding.[Error](
                        "MASOutput.ApplicationRoute.ProductEvidenceBundleEmptyArtifact",
                        fullPath,
                        "The product evidence bundle wrote an empty supporting artifact."))
                End If
            Next
        End Sub

        Private Shared Function VerifyPublicExport(output As MASApplicationOutput,
                                                   request As MASOutputRequest,
                                                   code As String,
                                                   findings As IList(Of MASOutputApplicationRouteFinding),
                                                   ByRef probeCount As Integer) As MASOutputResult
            probeCount += 1
            If output Is Nothing OrElse request Is Nothing Then
                findings.Add(MASOutputApplicationRouteFinding.[Error](code, "MASApplication.Output.Export", "Public Output export probe could not run."))
                Return Nothing
            End If

            Dim result As MASOutputResult = output.Export(request)
            If result Is Nothing OrElse Not result.Succeeded OrElse Not result.HasArtifactPayload Then
                Dim message As String = "Public Output export did not return a successful non-empty artifact."
                If result IsNot Nothing Then message = result.Summary
                findings.Add(MASOutputApplicationRouteFinding.[Error](code, "MASApplication.Output.Export", message))
            End If

            Return result
        End Function

        Private Shared Sub VerifyFoundationBuilders(findings As IList(Of MASOutputApplicationRouteFinding),
                                                    ByRef probeCount As Integer)
            probeCount += 1

            Dim chartSnapshot As MASChartFoundationSnapshot = MASChartFoundationBuilder.BuildBasicDashboardFoundationSnapshot()
            If chartSnapshot Is Nothing OrElse Not chartSnapshot.ReadyForDashboardVisualizationFoundation Then
                findings.Add(MASOutputApplicationRouteFinding.[Error](
                    "MASOutput.ApplicationRoute.ChartFoundationNotReady",
                    "MASChartFoundationBuilder",
                    "The Chart / Visualization foundation did not produce a ready dashboard foundation snapshot."))
            End If

            Dim reportSnapshot As MASReportPreviewSnapshot = MASReportPrintPlanBuilder.BuildSampleBusinessReportPreview()
            If reportSnapshot Is Nothing OrElse Not reportSnapshot.ReadyForSimplePrintableReports Then
                findings.Add(MASOutputApplicationRouteFinding.[Error](
                    "MASOutput.ApplicationRoute.ReportFoundationNotReady",
                    "MASReportPrintPlanBuilder",
                    "The Report / Print foundation did not produce a ready sample printable-report preview."))
            End If
        End Sub

        Private Shared Sub VerifyPublicBoundary(findings As IList(Of MASOutputApplicationRouteFinding),
                                                ByRef probeCount As Integer)
            probeCount += 1

            AssertPublic(GetType(MASApplicationOutput), "MASApplicationOutput", findings)
            AssertPublic(GetType(MASOutputRequest), "MASOutputRequest", findings)
            AssertPublic(GetType(MASOutputResult), "MASOutputResult", findings)
            AssertPublic(GetType(MASOutputReadinessSnapshot), "MASOutputReadinessSnapshot", findings)
            AssertPublic(GetType(MASOutputCapability), "MASOutputCapability", findings)
            AssertPublic(GetType(MASOutputCapabilityCatalog), "MASOutputCapabilityCatalog", findings)
            AssertPublic(GetType(MASOutputTarget), "MASOutputTarget", findings)
            AssertPublic(GetType(MASOutputOptions), "MASOutputOptions", findings)
            AssertPublic(GetType(MASOutputArtifactKind), "MASOutputArtifactKind", findings)
            AssertPublic(GetType(MASOutputFormat), "MASOutputFormat", findings)
            AssertPublic(GetType(MASOutputOperationStatus), "MASOutputOperationStatus", findings)
            AssertPublic(GetType(MASOutputFailureReason), "MASOutputFailureReason", findings)
            AssertPublic(GetType(MASOutputTargetKind), "MASOutputTargetKind", findings)
            AssertPublic(GetType(MASOutputCapabilityStatus), "MASOutputCapabilityStatus", findings)

            AssertFriendOnly(GetType(MASApplicationOutputGateway), "MASApplicationOutputGateway", findings)
            AssertFriendOnly(GetType(MASApplicationOutputSnapshot), "MASApplicationOutputSnapshot", findings)
            AssertFriendOnly(GetType(MASOutputProductRuntime), "MASOutputProductRuntime", findings)
            AssertFriendOnly(GetType(MASChartFoundationBuilder), "MASChartFoundationBuilder", findings)
            AssertFriendOnly(GetType(MASReportPrintPlanBuilder), "MASReportPrintPlanBuilder", findings)
        End Sub

        Private Shared Sub AssertPublic(typeItem As Type,
                                        subject As String,
                                        findings As IList(Of MASOutputApplicationRouteFinding))
            If typeItem Is Nothing Then Return
            If Not typeItem.IsPublic AndAlso Not typeItem.IsNestedPublic Then
                findings.Add(MASOutputApplicationRouteFinding.[Error](
                    "MASOutput.ApplicationRoute.PublicContractMissing",
                    subject,
                    "Output product contract types must be public for SDK consumers."))
            End If
        End Sub

        Private Shared Sub AssertFriendOnly(typeItem As Type,
                                            subject As String,
                                            findings As IList(Of MASOutputApplicationRouteFinding))
            If typeItem Is Nothing Then Return
            If typeItem.IsPublic OrElse typeItem.IsNestedPublic Then
                findings.Add(MASOutputApplicationRouteFinding.[Error](
                    "MASOutput.ApplicationRoute.PublicTypeLeak",
                    subject,
                    "Internal output route/foundation types must remain Friend; SDK consumers use MASApplication.Output public contracts instead."))
            End If
        End Sub
    End Class

End Namespace
