Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Reflection
Imports Nexamas.UI.Rendering

Namespace Nexamas.UI.Quality

    ''' <summary>
    ''' Governance proof for the public SurfaceMaterial gateway manifest. This gate closes the
    ''' old stale-count gap by requiring the manifest's expected count, normalized entry count,
    ''' ready gateway count, required-member coverage, duplicate detection, and real public
    ''' reflection surface to agree with each other.
    ''' </summary>
    Friend NotInheritable Class MASPublicSurfaceMaterialGatewayManifestGate

        Private Sub New()
        End Sub

        Friend Shared Function Passes() As Boolean
            Dim manifest As MASPublicSurfaceMaterialGatewayManifest = MASPublicSurfaceMaterialGatewayManifest.CreateCurrent()
            If manifest Is Nothing Then Return False
            If manifest.EntryCount <= 0 Then Return False
            If manifest.EntryCount <> MASPublicSurfaceMaterialGatewayManifest.ExpectedGatewayMemberCount Then Return False
            If manifest.ReadyGatewayCount <> manifest.EntryCount Then Return False
            If Not manifest.HasLimitedPublicSurfaceMaterialGateways Then Return False
            If manifest.HasRenderVerificationCatchUpDebt Then Return False
            If Not HasUniqueGatewayEntries(manifest) Then Return False
            If Not DeclaredPublicMembersExist(manifest) Then Return False
            Return True
        End Function

        Private Shared Function HasUniqueGatewayEntries(manifest As MASPublicSurfaceMaterialGatewayManifest) As Boolean
            If manifest Is Nothing OrElse manifest.Entries Is Nothing Then Return False

            Dim keys As New HashSet(Of String)(StringComparer.Ordinal)
            For Each entry As MASPublicSurfaceMaterialGatewayEntry In manifest.Entries
                If entry Is Nothing Then Return False
                Dim key As String = entry.OwnerTypeName & "|" & entry.MemberName & "|" & entry.MemberKind
                If Not keys.Add(key) Then Return False
            Next

            Return keys.Count = manifest.EntryCount
        End Function

        Private Shared Function DeclaredPublicMembersExist(manifest As MASPublicSurfaceMaterialGatewayManifest) As Boolean
            If manifest Is Nothing OrElse manifest.Entries Is Nothing Then Return False

            Dim assembly As Assembly = GetType(MASPublicSurfaceMaterialGatewayManifest).Assembly
            If assembly Is Nothing Then Return False

            For Each entry As MASPublicSurfaceMaterialGatewayEntry In manifest.Entries
                If entry Is Nothing Then Return False
                If Not DeclaredPublicMemberExists(assembly, entry) Then Return False
            Next

            Return True
        End Function

        Private Shared Function DeclaredPublicMemberExists(assembly As Assembly,
                                                           entry As MASPublicSurfaceMaterialGatewayEntry) As Boolean
            If assembly Is Nothing OrElse entry Is Nothing Then Return False
            If String.IsNullOrWhiteSpace(entry.OwnerTypeName) OrElse String.IsNullOrWhiteSpace(entry.MemberName) Then Return False

            Dim ownerType As Type = assembly.GetType(entry.OwnerTypeName, throwOnError:=False, ignoreCase:=False)
            If ownerType Is Nothing Then Return False

            Const Flags As BindingFlags = BindingFlags.Public Or BindingFlags.Instance Or BindingFlags.Static
            If String.Equals(entry.MemberKind, "Property", StringComparison.OrdinalIgnoreCase) Then
                Return ownerType.GetProperty(entry.MemberName, Flags) IsNot Nothing
            End If

            If String.Equals(entry.MemberKind, "Function", StringComparison.OrdinalIgnoreCase) Then
                Return ownerType.GetMember(entry.MemberName, MemberTypes.Method, Flags).Length > 0
            End If

            Return False
        End Function

    End Class

End Namespace
