namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests;

public sealed class LegacyGateAHarnessContractTests
{
    private const string CurrentMsixSha256 =
        "0E44F87FDD31852B7DDFCD6CF3A6325DA674F1F5A23AD39CB262A725AEB84367";

    private static readonly string RepositoryRoot = FindRepositoryRoot();
    private static readonly string Runner = ReadScript("Invoke-LegacyPgmPrepareGate.ps1");
    private static readonly string PlanBuilder = ReadScript("New-LegacyPgmPreparePlan.ps1");
    private static readonly string NodeHelper = ReadScript("Test-LegacyPgmPrepareGate.mjs");

    [Fact]
    public void Node_receives_the_bearer_only_from_the_parent_owned_JIT_pipe()
    {
        Assert.Contains("import net from \"node:net\";", NodeHelper, StringComparison.Ordinal);
        Assert.Contains("\"capability-sha256\", \"permit-pipe\"", NodeHelper,
            StringComparison.Ordinal);
        Assert.Contains(
            "name.startsWith(\"MBN_LEGACY_PGM_GATE_A_\")",
            NodeHelper,
            StringComparison.Ordinal);
        Assert.DoesNotContain(
            "process.env[capabilityName]",
            NodeHelper,
            StringComparison.Ordinal);

        var receive = Slice(
            NodeHelper,
            "async function receiveJitCapability(expiresAt)",
            "async function prepareOnce(expiresAt)");
        Assert.Contains("net.createConnection(pipePath)", receive, StringComparison.Ordinal);
        Assert.Contains("bytes.length !== 64", receive, StringComparison.Ordinal);
        Assert.Contains("sha256(bytes) !== configuration.capabilitySha256", receive,
            StringComparison.Ordinal);
        Assert.Contains("jitPermitReceived = true", receive, StringComparison.Ordinal);

        var prepare = Slice(
            NodeHelper,
            "async function prepareOnce(expiresAt)",
            "async function captureScreenshot()");
        var boundary = RequiredIndex(prepare, "const preRevision = before.state.revision;");
        var permit = RequiredIndex(prepare, "await receiveJitCapability(expiresAt)");
        var arm = RequiredIndex(prepare, "__legacyPgmPrepareGate.arm(");
        var dispatchPossible = RequiredIndex(prepare, "prepareDispatchPossible = true;");
        Assert.True(boundary < permit && permit < arm && arm < dispatchPossible);

        var execute = Slice(NodeHelper, "async function execute()", "try {");
        Assert.Contains(
            "installGate(configuration.planSha256, configuration.capabilitySha256)",
            execute,
            StringComparison.Ordinal);
    }

    [Fact]
    public void Runner_authenticates_the_exact_Node_client_before_one_JIT_delivery()
    {
        var start = Slice(
            Runner,
            "function Start-NodeHelper([object] $Plan)",
            "function Write-Evidence([string] $Path)");
        Assert.Contains("New-CurrentUserOneShotPipe 'Node'", start, StringComparison.Ordinal);
        Assert.Contains("'--capability-sha256'", start, StringComparison.Ordinal);
        Assert.Contains("'--permit-pipe'", start, StringComparison.Ordinal);
        Assert.DoesNotContain(
            "$info.EnvironmentVariables[$capabilityName]",
            start,
            StringComparison.Ordinal);
        Assert.Contains("MBN_LEGACY_PGM_GATE_A_*", start, StringComparison.Ordinal);
        Assert.Contains("Assert-NodePermitClient", start, StringComparison.Ordinal);

        var client = Slice(
            Runner,
            "function Assert-NodePermitClient(",
            "function Stop-NodeHelperFailStop(");
        Assert.Contains("ReadNamedPipeClientProcessId", client, StringComparison.Ordinal);
        Assert.Contains("$clientProcessId -ne $Process.Id", client, StringComparison.Ordinal);
        Assert.Contains("$Process.StartTime.ToUniversalTime().Ticks", client,
            StringComparison.Ordinal);
        Assert.Contains("[int]$row.ParentProcessId -ne $PID", client,
            StringComparison.Ordinal);
        Assert.Contains("SamePath ([string]$row.ExecutablePath) $ExpectedExecutable", client,
            StringComparison.Ordinal);

        var delivery = RequiredIndex(start, "$permitPipe.Stream.Write($bytes,0,$bytes.Length)");
        foreach (var marker in new[]
                 {
                     "Assert-NodePermitClient",
                     "Assert-Authorization $script:authorization $Plan",
                     "Assert-Application $script:application",
                     "Assert-Pgm $Plan.pgm",
                     "Assert-Cdp ([int]$Plan.webView.port)"
                 })
        {
            Assert.True(RequiredIndex(start, marker) < delivery, marker);
        }

        var stop = Slice(
            Runner,
            "function Stop-NodeHelperFailStop(",
            "function Start-NodeHelper([object] $Plan)");
        Assert.Contains("Close-OneShotPipe $Pipe", stop, StringComparison.Ordinal);
        Assert.Contains("$Process.Kill()", stop, StringComparison.Ordinal);
        Assert.Contains("$Process.WaitForExit(0)", stop, StringComparison.Ordinal);
        Assert.Contains("$Process.WaitForExit(5000)", stop, StringComparison.Ordinal);
        Assert.Contains("was not proven stopped", stop, StringComparison.Ordinal);
        Assert.Contains("only the control-plane Node helper", stop, StringComparison.Ordinal);
        Assert.DoesNotContain("$script:application", stop, StringComparison.Ordinal);
        Assert.DoesNotContain("Assert-Pgm", stop, StringComparison.Ordinal);

        var catchBlock = start[RequiredIndex(start, "    catch {")..];
        Assert.Contains("Stop-NodeHelperFailStop $process $permitPipe", catchBlock,
            StringComparison.Ordinal);
        Assert.Contains("exceeded its outer timeout; parent fail-stop is required", start,
            StringComparison.Ordinal);
        var monitorTry = Slice(start, "    try {\n        $process.Refresh()", "    catch {");
        Assert.True(
            RequiredIndex(monitorTry, "$process.StartTime.ToUniversalTime().Ticks") <
            RequiredIndex(monitorTry, "while (-not $process.HasExited"));
        Assert.Contains("if (-not $process.HasExited)", monitorTry, StringComparison.Ordinal);
        Assert.Contains("$process.WaitForExit(0)", monitorTry, StringComparison.Ordinal);
    }

    [Fact]
    public void Package_wrapper_receives_launch_environment_over_an_authenticated_pipe_not_its_command_line()
    {
        var package = Slice(
            Runner,
            "function Start-Package([object] $Plan)",
            "function Quote-Argument([string] $Value)");
        Assert.DoesNotContain("$encodedEnvironment", package, StringComparison.Ordinal);
        Assert.Contains("New-CurrentUserOneShotPipe 'Package'", package,
            StringComparison.Ordinal);
        Assert.Contains("Assert-PackagePermitClient", package, StringComparison.Ordinal);
        Assert.Contains("[LegacyPgmPreparePackageIdentity]::Read($process.Handle)",
            Runner,
            StringComparison.Ordinal);
        Assert.Contains("$payload = $utf8NoBom.GetBytes(($environment | ConvertTo-Json -Compress))",
            package,
            StringComparison.Ordinal);
        Assert.Contains("$launchPipe.Stream.Write($payload,0,$payload.Length)", package,
            StringComparison.Ordinal);
        Assert.Contains("$client.ReadByte() -ne -1", package, StringComparison.Ordinal);
        var wrapperStop = Slice(
            Runner,
            "function Stop-PackageWrapperFailStop(",
            "function Start-Package([object] $Plan)");
        Assert.Contains("$Process.Kill()", wrapperStop, StringComparison.Ordinal);
        Assert.Contains("$Process.WaitForExit(0)", wrapperStop, StringComparison.Ordinal);
        Assert.Contains("$Process.WaitForExit(5000)", wrapperStop, StringComparison.Ordinal);
        Assert.Contains("was not proven stopped", wrapperStop, StringComparison.Ordinal);
        Assert.DoesNotContain("catch { return }", wrapperStop, StringComparison.Ordinal);
        Assert.Contains("$wrapper.WaitForExit(0)", package, StringComparison.Ordinal);
        Assert.Contains("without proving the exact wrapper exited", package,
            StringComparison.Ordinal);
        var child = Slice(package, "    $child = @\"", "\"@\n    $encodedChild");
        Assert.DoesNotContain("$encodedEnvironment", child, StringComparison.Ordinal);
        Assert.DoesNotContain("$script:gateCapability", child, StringComparison.Ordinal);
        Assert.DoesNotContain("$capabilityName", child, StringComparison.Ordinal);
        Assert.DoesNotContain("$environment", child, StringComparison.Ordinal);

        var pipes = Slice(
            Runner,
            "function New-CurrentUserOneShotPipe([string] $Purpose)",
            "function Close-OneShotPipe([object] $Pipe)");
        Assert.Contains("[IO.Pipes.PipeDirection]::InOut", pipes, StringComparison.Ordinal);
        Assert.Contains("[IO.Pipes.PipeSecurity]::new()", pipes, StringComparison.Ordinal);
        Assert.Contains("$identity.User", pipes, StringComparison.Ordinal);
        Assert.Contains("SetAccessRuleProtection($true, $false)", pipes,
            StringComparison.Ordinal);
    }

    [Fact]
    public void Managed_runtime_injection_environment_is_rejected_and_removed_from_children()
    {
        var preflight = Slice(
            Runner,
            "function Assert-NoUnsafeEnvironment",
            "function Assert-Pgm([object] $Pgm)");
        var package = Slice(
            Runner,
            "function Start-Package([object] $Plan)",
            "function Quote-Argument([string] $Value)");
        var child = Slice(package, "    $child = @\"", "\"@\n    $encodedChild");
        var node = Slice(
            Runner,
            "function Start-NodeHelper([object] $Plan)",
            "function Write-Evidence([string] $Path)");

        foreach (var prefix in new[] { "DOTNET_*", "CORECLR_*", "COR_*", "COMPlus_*" })
        {
            Assert.Contains(prefix, preflight, StringComparison.Ordinal);
            Assert.True(Count(child, prefix) >= 2, $"{prefix} must be removed before and after app launch.");
            Assert.Contains(prefix, node, StringComparison.Ordinal);
        }

        foreach (var scope in new[]
                 {
                     "[EnvironmentVariableTarget]::Process",
                     "[EnvironmentVariableTarget]::User",
                     "[EnvironmentVariableTarget]::Machine"
                 })
        {
            Assert.Contains(scope, preflight, StringComparison.Ordinal);
        }
    }

    [Fact]
    public void Database_source_is_pinned_leased_and_cannot_be_overridden_by_environment()
    {
        Assert.Contains("approvedDatabaseConfigPath", PlanBuilder, StringComparison.Ordinal);
        Assert.Contains(
            "database = New-FilePin $approvedDatabaseConfigPath 'database config'",
            PlanBuilder,
            StringComparison.Ordinal);
        Assert.Contains(
            "Assert-FilePin $script:plan.configuration.database 'database config'",
            Runner,
            StringComparison.Ordinal);
        Assert.Contains(
            "Add-ReadLease ([string]$script:plan.configuration.database.path) 'database config'",
            Runner,
            StringComparison.Ordinal);
        Assert.Contains("filePinsValidated = 12", Runner, StringComparison.Ordinal);
        Assert.Contains("const databasePin = plan?.configuration?.database;", NodeHelper,
            StringComparison.Ordinal);
        Assert.Contains("!isSha256(databasePin?.sha256)", NodeHelper,
            StringComparison.Ordinal);

        var preflight = Slice(
            Runner,
            "function Assert-NoUnsafeEnvironment",
            "function Assert-Pgm([object] $Pgm)");
        var package = Slice(
            Runner,
            "function Start-Package([object] $Plan)",
            "function Quote-Argument([string] $Value)");
        var child = Slice(package, "    $child = @\"", "\"@\n    $encodedChild");
        foreach (var prefix in new[]
                 {
                     "MBN_STOCK_ORACLE_*", "MBN_STOCK_MARIADB_*", "MBN_STOCK_DB_*"
                 })
        {
            Assert.Contains(prefix, preflight, StringComparison.Ordinal);
            Assert.True(Count(child, prefix) >= 2, $"{prefix} must be removed before and after app launch.");
        }
    }

    [Fact]
    public void Plan_contract_requires_helper_fail_stop_and_the_current_package_pin()
    {
        foreach (var source in new[] { Runner, PlanBuilder })
        {
            Assert.Contains(CurrentMsixSha256, source, StringComparison.Ordinal);
        }

        Assert.Contains("nodeHelperFailStopRequired = $true", PlanBuilder,
            StringComparison.Ordinal);
        Assert.Contains("packageWrapperFailStopRequired = $true", PlanBuilder,
            StringComparison.Ordinal);
        Assert.Contains("'nodeHelperFailStopRequired'", Runner, StringComparison.Ordinal);
        Assert.Contains("'packageWrapperFailStopRequired'", Runner, StringComparison.Ordinal);
        Assert.Contains("plan?.safety?.nodeHelperFailStopRequired !== true", NodeHelper,
            StringComparison.Ordinal);
        Assert.Contains("plan?.safety?.packageWrapperFailStopRequired !== true", NodeHelper,
            StringComparison.Ordinal);
        Assert.Contains(
            "Assert-JsonInt $Plan.safety.nodeHelperInvocationMaximum 1",
            Runner,
            StringComparison.Ordinal);
    }

    private static string Slice(string source, string startMarker, string endMarker)
    {
        var start = RequiredIndex(source, startMarker);
        var end = RequiredIndex(source, endMarker, start + startMarker.Length);
        return source[start..end];
    }

    private static int RequiredIndex(string source, string marker, int startIndex = 0)
    {
        var index = source.IndexOf(marker, startIndex, StringComparison.Ordinal);
        Assert.True(index >= 0, $"Required source marker was not found: {marker}");
        return index;
    }

    private static int Count(string source, string marker)
    {
        var count = 0;
        var index = 0;
        while ((index = source.IndexOf(marker, index, StringComparison.Ordinal)) >= 0)
        {
            count++;
            index += marker.Length;
        }

        return count;
    }

    private static string ReadScript(string fileName) =>
        File.ReadAllText(Path.Combine(RepositoryRoot, "scripts", fileName));

    private static string FindRepositoryRoot()
    {
        var current = new DirectoryInfo(AppContext.BaseDirectory);
        while (current is not null)
        {
            if (File.Exists(Path.Combine(current.FullName, "MBN_STOCK_WEBVIEW.sln")))
            {
                return current.FullName;
            }

            current = current.Parent;
        }

        throw new DirectoryNotFoundException("Repository root was not found.");
    }
}
