using System.Xml.Linq;

namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests;

public sealed class LegacyRuntimeAssetDeploymentContractTests
{
    private static readonly string RepositoryRoot = FindRepositoryRoot();
    private static readonly string ProjectPath = Path.Combine(
        RepositoryRoot,
        "src",
        "MBN_STOCK_WEBVIEW.LegacyParityApp",
        "MBN_STOCK_WEBVIEW.LegacyParityApp.csproj");

    [Fact]
    public void LegacyParityProjectUsesCutsInPlaceAndLinksOnlyAllowlistedRes()
    {
        var project = XDocument.Load(ProjectPath);
        var contents = project.Descendants("Content").ToArray();

        Assert.DoesNotContain(
            contents,
            element => string.Equals(
                element.Attribute("Include")?.Value,
                "$(LegacyCutsSourceRoot)\\**\\*",
                StringComparison.Ordinal));
        AssertLinkedRuntimeTree(contents, "@(LegacyPackagedResAsset)", "Res\\");
        var cutsScrub = Assert.Single(
            project.Descendants("Target"),
            target => string.Equals(
                target.Attribute("Name")?.Value,
                "ScrubCopiedCutsFromBuildOutput",
                StringComparison.Ordinal));
        Assert.Contains(
            "$(TargetDir)Cuts",
            Assert.Single(cutsScrub.Elements("RemoveDir"))
                .Attribute("Directories")?.Value,
            StringComparison.Ordinal);

        var packagedResNames = project.Descendants("LegacyPackagedResAsset")
            .Select(element => Path.GetFileName(element.Attribute("Include")?.Value))
            .ToHashSet(StringComparer.OrdinalIgnoreCase);
        string[] requiredUiMenus =
        [
            "setup.ini",
            "기타.ini",
            "업종.ini",
            "업종_코스닥.ini",
            "업종_코스피.ini",
            "종목.ini",
            "종목_0111.ini",
            "종목비교.ini",
            "지수.ini",
            "지수_0318.ini",
            "지수_250401.ini",
            "해외.ini",
            "환율.ini"
        ];

        Assert.Equal(34, packagedResNames.Count);
        Assert.All(requiredUiMenus, name => Assert.Contains(name, packagedResNames));
        Assert.DoesNotContain("MmoneyCoder.ini", packagedResNames);
        Assert.DoesNotContain("MmoneyCoder - 복사본.ini", packagedResNames);
        Assert.DoesNotContain("MmoneyCoder.ini.bak", packagedResNames);
        Assert.DoesNotContain("MmoneyCoder.zip", packagedResNames);
        Assert.DoesNotContain("afiedt.buf.txt", packagedResNames);
    }

    [Fact]
    public void GraphEPreviewsExposeOnlyFourAllowlistedBitmapsThroughTheTrustedWebRoot()
    {
        var project = XDocument.Load(ProjectPath);
        var previewNames = project.Descendants("LegacyFinancialPreviewAsset")
            .Select(element => Path.GetFileName(element.Attribute("Include")?.Value)!)
            .ToHashSet(StringComparer.OrdinalIgnoreCase);

        Assert.Equal(
            new HashSet<string>(
                ["pie.bmp", "Grow.bmp", "sell.bmp", "profit.bmp"],
                StringComparer.OrdinalIgnoreCase),
            previewNames);

        var previewContent = Assert.Single(
            project.Descendants("Content"),
            element => string.Equals(
                element.Attribute("Include")?.Value,
                "@(LegacyFinancialPreviewAsset)",
                StringComparison.Ordinal));
        Assert.Equal(
            "Web\\Previews\\%(Filename)%(Extension)",
            previewContent.Element("Link")?.Value);
        Assert.Equal(
            "Web\\Previews\\%(Filename)%(Extension)",
            previewContent.Element("TargetPath")?.Value);
        Assert.Equal(
            "PreserveNewest",
            previewContent.Element("CopyToOutputDirectory")?.Value);
        Assert.Equal(
            "PreserveNewest",
            previewContent.Element("CopyToPublishDirectory")?.Value);

        Assert.DoesNotContain(
            project.Descendants("Content"),
            element => string.Equals(
                element.Attribute("Include")?.Value,
                "$(LegacyResSourceRoot)\\**\\*",
                StringComparison.OrdinalIgnoreCase));

        var window = File.ReadAllText(Path.Combine(
            RepositoryRoot,
            "src",
            "MBN_STOCK_WEBVIEW.LegacyParityApp",
            "MainWindow.xaml.cs"));
        Assert.Equal(
            1,
            window.Split(
                "SetVirtualHostNameToFolderMapping(",
                StringSplitOptions.None).Length - 1);
        Assert.Contains("LegacyBridgeProtocol.TrustedHost", window, StringComparison.Ordinal);
        Assert.Contains("webRoot", window, StringComparison.Ordinal);
        Assert.DoesNotContain(
            "SetVirtualHostNameToFolderMapping(\"res",
            window,
            StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public void CustomerWordmarkIsProjectedFromTheAllowlistedLegacyAssetIntoTheTrustedWebRoot()
    {
        var project = XDocument.Load(ProjectPath);
        var brandAsset = Assert.Single(project.Descendants("LegacyBrandAsset"));
        Assert.Equal(
            "$(LegacyResSourceRoot)\\logo.png",
            brandAsset.Attribute("Include")?.Value);

        var brandContent = Assert.Single(
            project.Descendants("Content"),
            element => string.Equals(
                element.Attribute("Include")?.Value,
                "@(LegacyBrandAsset)",
                StringComparison.Ordinal));
        Assert.Equal(
            "Web\\Brand\\%(Filename)%(Extension)",
            brandContent.Element("Link")?.Value);
        Assert.Equal(
            "Web\\Brand\\%(Filename)%(Extension)",
            brandContent.Element("TargetPath")?.Value);
        Assert.Equal(
            "PreserveNewest",
            brandContent.Element("CopyToOutputDirectory")?.Value);
        Assert.Equal(
            "PreserveNewest",
            brandContent.Element("CopyToPublishDirectory")?.Value);

        var packagedResNames = project.Descendants("LegacyPackagedResAsset")
            .Select(element => Path.GetFileName(element.Attribute("Include")?.Value))
            .ToHashSet(StringComparer.OrdinalIgnoreCase);
        Assert.Contains("logo.png", packagedResNames);
    }

    [Fact]
    public void WebViewIgnoresItsPersistentCacheBeforeExactTrustedNavigation()
    {
        var window = File.ReadAllText(Path.Combine(
            RepositoryRoot,
            "src",
            "MBN_STOCK_WEBVIEW.LegacyParityApp",
            "MainWindow.xaml.cs"));

        var mapping = window.IndexOf(
            "SetVirtualHostNameToFolderMapping(",
            StringComparison.Ordinal);
        var disableCache = window.IndexOf(
            "\"Network.setCacheDisabled\"",
            StringComparison.Ordinal);
        var navigation = window.IndexOf(
            "Browser.Source = new Uri(",
            StringComparison.Ordinal);

        Assert.True(mapping >= 0);
        Assert.True(disableCache > mapping);
        Assert.True(navigation > disableCache);
        Assert.Contains(
            "CoreWebView2HostResourceAccessKind.DenyCors",
            window,
            StringComparison.Ordinal);
        Assert.Equal(
            1,
            window.Split(
                "\"Network.setCacheDisabled\"",
                StringSplitOptions.None).Length - 1);
        Assert.Contains(
            "\"{\\\"cacheDisabled\\\":true}\"",
            window,
            StringComparison.Ordinal);
        Assert.Contains(
            "$\"https://{LegacyBridgeProtocol.TrustedHost}/index.html\"",
            window,
            StringComparison.Ordinal);
        Assert.DoesNotContain(
            "$\"https://{LegacyBridgeProtocol.TrustedHost}/index.html?",
            window,
            StringComparison.Ordinal);
    }

    [Fact]
    public void DatabaseCredentialsStayInUserLocalConfigurationAndNeverEnterBuildOutputs()
    {
        var project = XDocument.Load(ProjectPath);
        Assert.DoesNotContain(
            project.Descendants().Where(element =>
                element.Name.LocalName is "Content" or "None"),
            element => (element.Attribute("Include")?.Value ?? string.Empty)
                .Contains("MmoneyCoder.ini", StringComparison.OrdinalIgnoreCase));

        Assert.DoesNotContain(
            project.Descendants("UpToDateCheckInput"),
            element => (element.Attribute("Include")?.Value ?? string.Empty)
                .Contains("MmoneyCoder.ini", StringComparison.OrdinalIgnoreCase));
        Assert.DoesNotContain(
            project.Descendants("UpToDateCheckBuilt"),
            element => (element.Attribute("Include")?.Value ?? string.Empty)
                .Contains("MmoneyCoder.ini", StringComparison.OrdinalIgnoreCase));
        Assert.DoesNotContain(
            project.Descendants("Target"),
            element => string.Equals(
                element.Attribute("Name")?.Value,
                "CopyLegacyDatabaseIniToLocalOutput",
                StringComparison.Ordinal));
        Assert.DoesNotContain(
            project.Descendants("Target"),
            element => string.Equals(
                element.Attribute("Name")?.Value,
                "RemoveLegacyDatabaseIniFromLocalOutput",
                StringComparison.Ordinal));

        var scrubTarget = Assert.Single(
            project.Descendants("Target"),
            element => string.Equals(
                element.Attribute("Name")?.Value,
                "ScrubLegacyDatabaseIniFromBuildOutput",
                StringComparison.Ordinal));
        Assert.Equal("PrepareForBuild", scrubTarget.Attribute("BeforeTargets")?.Value);
        var scrubDelete = Assert.Single(scrubTarget.Elements("Delete"));
        Assert.Equal(
            "$(TargetDir)Res\\MmoneyCoder.ini",
            scrubDelete.Attribute("Files")?.Value);

        var validationTarget = Assert.Single(
            project.Descendants("Target"),
            element => string.Equals(
                element.Attribute("Name")?.Value,
                "ValidateLegacyRuntimeAssets",
                StringComparison.Ordinal));
        Assert.DoesNotContain(
            validationTarget.Elements("Error"),
            element => (element.Attribute("Condition")?.Value ?? string.Empty)
                .Contains("MmoneyCoder.ini", StringComparison.OrdinalIgnoreCase));

        var rejectionTarget = Assert.Single(
            project.Descendants("Target"),
            element => string.Equals(
                element.Attribute("Name")?.Value,
                "RejectLegacyDatabaseIniFromPackagePayload",
                StringComparison.Ordinal));
        Assert.Equal(
            "_ComputeAppxPackagePayload",
            rejectionTarget.Attribute("BeforeTargets")?.Value);
        var forbiddenPayload = Assert.Single(
            rejectionTarget.Descendants("_ForbiddenLegacyDatabasePackagePayload"));
        Assert.Contains(
            "%(PackagingOutputs.Filename)%(PackagingOutputs.Extension)",
            forbiddenPayload.Attribute("Condition")?.Value,
            StringComparison.Ordinal);
        Assert.Single(rejectionTarget.Elements("Error"));
    }

    [Fact]
    public void FreshCloneUsesIsolatedSourceOnlyOutputWithoutWeakeningFullBuilds()
    {
        var project = XDocument.Load(ProjectPath);

        var sourceRootWasSpecified = Assert.Single(
            project.Descendants("LegacyRuntimeSourceRootWasSpecified"));
        Assert.Equal(
            "'$(LegacyRuntimeSourceRoot)' != ''",
            sourceRootWasSpecified.Attribute("Condition")?.Value);

        var modes = project.Descendants("LegacyRuntimeAssetsMode").ToArray();
        Assert.Equal(2, modes.Length);
        var requiredMode = Assert.Single(
            modes,
            mode => string.Equals(mode.Value, "Required", StringComparison.Ordinal));
        var requiredCondition = requiredMode.Attribute("Condition")?.Value;
        Assert.Contains(
            "$(LegacyRuntimeSourceRootWasSpecified)",
            requiredCondition,
            StringComparison.Ordinal);
        Assert.Contains(
            "'$(Configuration)' == 'Release'",
            requiredCondition,
            StringComparison.Ordinal);
        Assert.Contains(
            "$(GenerateAppxPackageOnBuild)",
            requiredCondition,
            StringComparison.Ordinal);
        Assert.Contains("$(PublishAppxPackage)", requiredCondition, StringComparison.Ordinal);

        var autoMode = Assert.Single(
            modes,
            mode => string.Equals(mode.Value, "Auto", StringComparison.Ordinal));
        Assert.Equal(
            "'$(LegacyRuntimeAssetsMode)' == ''",
            autoMode.Attribute("Condition")?.Value);

        var assetsEnabled = Assert.Single(
            project.Descendants("LegacyRuntimeAssetsEnabled"),
            element => (element.Attribute("Condition")?.Value ?? string.Empty)
                .Contains("Exists('$(LegacyRuntimeSourceRoot)')", StringComparison.Ordinal));
        Assert.Equal("true", assetsEnabled.Value);

        var sourceOnlyOutput = Assert.Single(project.Descendants("BaseOutputPath"));
        Assert.Equal(@"bin\SourceOnly\", sourceOnlyOutput.Value);
        Assert.Contains(
            "$(LegacyRuntimeAssetsEnabled)",
            sourceOnlyOutput.Attribute("Condition")?.Value,
            StringComparison.Ordinal);
        var sourceOnlyIntermediate = Assert.Single(
            project.Descendants("IntermediateOutputPath"));
        Assert.Equal(
            @"obj\SourceOnly\$(Platform)\$(Configuration)\",
            sourceOnlyIntermediate.Value);
        Assert.Contains(
            "$(LegacyRuntimeAssetsEnabled)",
            sourceOnlyIntermediate.Attribute("Condition")?.Value,
            StringComparison.Ordinal);
        var sourceOnlyConstants = Assert.Single(project.Descendants("DefineConstants"));
        Assert.Contains(
            "SOURCE_ONLY_RUNTIME",
            sourceOnlyConstants.Value,
            StringComparison.Ordinal);
        Assert.Contains(
            "$(LegacyRuntimeAssetsEnabled)",
            sourceOnlyConstants.Attribute("Condition")?.Value,
            StringComparison.Ordinal);

        var validationTarget = Assert.Single(
            project.Descendants("Target"),
            element => string.Equals(
                element.Attribute("Name")?.Value,
                "ValidateLegacyRuntimeAssets",
                StringComparison.Ordinal));
        var sourceOnlyMessage = Assert.Single(
            validationTarget.Elements("Message"),
            message => (message.Attribute("Condition")?.Value ?? string.Empty)
                .Contains("$(LegacyRuntimeAssetsMode)", StringComparison.Ordinal));
        Assert.Contains(
            "$(LegacyRuntimeAssetsMode)",
            sourceOnlyMessage.Attribute("Condition")?.Value,
            StringComparison.Ordinal);
        Assert.Contains(
            "safe first-run setup app",
            sourceOnlyMessage.Attribute("Text")?.Value,
            StringComparison.Ordinal);
        Assert.Contains(
            "select the existing Cuts and Res folders",
            sourceOnlyMessage.Attribute("Text")?.Value,
            StringComparison.Ordinal);

        var fullRuntimeMessage = Assert.Single(
            validationTarget.Elements("Message"),
            message => (message.Attribute("Text")?.Value ?? string.Empty)
                .Contains(
                    "External legacy runtime paths are enabled",
                    StringComparison.Ordinal));
        Assert.Contains(
            "Cuts files remain in the selected folder",
            fullRuntimeMessage.Attribute("Text")?.Value,
            StringComparison.Ordinal);

        var runtimeValidationErrors = validationTarget.Elements("Error")
            .Where(error => (error.Attribute("Condition")?.Value ?? string.Empty)
                .Contains("Exists(", StringComparison.Ordinal))
            .ToArray();
        Assert.NotEmpty(runtimeValidationErrors);
        Assert.All(runtimeValidationErrors, error =>
            Assert.Contains(
                "$(LegacyRuntimeAssetsValidationEnabled)",
                error.Attribute("Condition")?.Value,
                StringComparison.Ordinal));
        Assert.Contains(
            runtimeValidationErrors,
            error => (error.Attribute("Text")?.Value ?? string.Empty).Contains(
                "Initialize-ExistingDevelopmentPc.ps1",
                StringComparison.Ordinal));

        string[] externalContentIncludes =
        [
            "@(LegacyPackagedResAsset)",
            "@(LegacyFinancialPreviewAsset)",
            "@(LegacyBrandAsset)"
        ];
        Assert.All(externalContentIncludes, include =>
        {
            var content = Assert.Single(
                project.Descendants("Content"),
                element => string.Equals(
                    element.Attribute("Include")?.Value,
                    include,
                    StringComparison.Ordinal));
            Assert.Contains(
                "$(LegacyRuntimeAssetsEnabled)",
                content.Attribute("Condition")?.Value,
                StringComparison.Ordinal);
        });
        Assert.DoesNotContain(
            project.Descendants("Content"),
            element => string.Equals(
                element.Attribute("Include")?.Value,
                @"$(LegacyCutsSourceRoot)\**\*",
                StringComparison.Ordinal));

        Assert.DoesNotContain(
            project.Descendants("Target"),
            element => string.Equals(
                element.Attribute("Name")?.Value,
                "CopyLegacyDatabaseIniToLocalOutput",
                StringComparison.Ordinal));

        var mainWindow = File.ReadAllText(Path.Combine(
            RepositoryRoot,
            "src",
            "MBN_STOCK_WEBVIEW.LegacyParityApp",
            "MainWindow.xaml.cs"));
        Assert.Contains("#if SOURCE_ONLY_RUNTIME", mainWindow, StringComparison.Ordinal);
        Assert.Contains(
            "Source-only builds do not initialize a database runtime.",
            mainWindow,
            StringComparison.Ordinal);

        var playout = File.ReadAllText(Path.Combine(
            RepositoryRoot,
            "src",
            "MBN_STOCK_WEBVIEW.LegacyParityApp",
            "MainWindow.Playout.cs"));
        Assert.DoesNotContain("forceSafeDryRun", playout,
            StringComparison.Ordinal);
        Assert.Contains("if (_playoutOptions.Mode != PlayoutMode.Live)", playout,
            StringComparison.Ordinal);
    }

    [Fact]
    public void LegacyRuntimeSourceRootIsOverrideableAndValidatedBeforeBuild()
    {
        var project = XDocument.Load(ProjectPath);
        var sourceRoot = Assert.Single(
            project.Descendants("LegacyRuntimeSourceRoot"),
            element => element.Attribute("Condition") is not null);

        Assert.Equal("'$(LegacyRuntimeSourceRoot)' == ''", sourceRoot.Attribute("Condition")!.Value);
        Assert.Contains(".first-run-runtime-not-configured-0F72B3C8", sourceRoot.Value,
            StringComparison.Ordinal);

        var target = Assert.Single(
            project.Descendants("Target"),
            element => string.Equals(
                element.Attribute("Name")?.Value,
                "ValidateLegacyRuntimeAssets",
                StringComparison.Ordinal));
        Assert.Equal("PrepareForBuild", target.Attribute("BeforeTargets")?.Value);

        var conditions = target.Elements("Error")
            .Select(error => error.Attribute("Condition")?.Value)
            .Where(condition => condition is not null)
            .ToArray();
        string[] requiredSentinels =
        [
            "$(LegacyRuntimeSourceRoot)",
            "$(LegacyCutsSourceRoot)",
            "$(LegacyResSourceRoot)",
            "$(LegacyResSourceRoot)\\logo.png",
            "$(LegacyResSourceRoot)\\종목.ini",
            "$(LegacyResSourceRoot)\\업종_코스피.ini",
            "$(LegacyResSourceRoot)\\업종_코스닥.ini",
            "$(LegacyResSourceRoot)\\해외.ini",
            "$(LegacyResSourceRoot)\\환율.ini",
            "$(LegacyResSourceRoot)\\지수.ini"
        ];

        Assert.All(requiredSentinels, sentinel =>
            Assert.Contains(conditions, condition =>
                condition!.Contains(sentinel, StringComparison.Ordinal)));

        var requiredScenes = Assert.Single(project.Descendants("LegacyRequiredScene"))
            .Attribute("Include")!.Value
            .Split(';', StringSplitOptions.RemoveEmptyEntries);
        Assert.Equal(45, requiredScenes.Length);
        Assert.Equal(requiredScenes.Length,
            requiredScenes.Distinct(StringComparer.Ordinal).Count());
        Assert.Contains("5001", requiredScenes);
        Assert.Contains("5076", requiredScenes);
        Assert.Contains("5079", requiredScenes);
        Assert.Contains("6001", requiredScenes);

        var requiredAssets = Assert.Single(
                project.Descendants("LegacyRequiredBuiltInAsset"))
            .Attribute("Include")!.Value
            .Split(';', StringSplitOptions.RemoveEmptyEntries);
        Assert.Equal(9, requiredAssets.Length);
        Assert.Contains(@"images\주유기merge.png", requiredAssets);
        Assert.Contains(@"images\35752913_l.jpg", requiredAssets);
        Assert.Contains(@"Images\KRX.png", requiredAssets);
        Assert.Contains(@"Images\NXT.png", requiredAssets);

        Assert.DoesNotContain(
            target.Elements("Error"),
            error => (error.Attribute("Condition")?.Value ?? string.Empty)
                .Contains("MmoneyCoder.ini", StringComparison.OrdinalIgnoreCase));
    }

    [Fact]
    public void DevelopmentLiveHandoffUsesAnIgnoredVerifiedRuntimeRootAndSafeLocalFiles()
    {
        var directoryPropsPath = Path.Combine(RepositoryRoot, "Directory.Build.props");
        var directoryProps = XDocument.Load(directoryPropsPath);
        var localImport = Assert.Single(directoryProps.Descendants("Import"));
        Assert.Equal(
            "$(MSBuildThisFileDirectory)Directory.Build.local.props",
            localImport.Attribute("Project")?.Value);
        Assert.Contains(
            "Exists(",
            localImport.Attribute("Condition")?.Value,
            StringComparison.Ordinal);

        var gitIgnore = File.ReadAllLines(Path.Combine(RepositoryRoot, ".gitignore"));
        Assert.Contains("Directory.Build.local.props", gitIgnore);

        var initializer = File.ReadAllText(Path.Combine(
            RepositoryRoot,
            "scripts",
            "Initialize-DevelopmentLiveConfig.ps1"));
        Assert.Contains(
            "[ValidateSet('127.0.0.1', '::1')]",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains("mode = 'Live'", initializer, StringComparison.Ordinal);
        Assert.DoesNotContain("mode = 'DryRun'", initializer, StringComparison.Ordinal);
        Assert.Contains(
            "trustedLiveOutputEnabled = $true",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "maximumReconnectAttempts = 0",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains("reconnectEnabled = $false", initializer, StringComparison.Ordinal);
        Assert.Contains(
            "maximumAutomaticRefreshesPerTakeIn = 0",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "I_AUTHORIZE_LIVE_PROGRAM_OUTPUT_FOR_THIS_LAUNCH",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "SetAccessRuleProtection($true, $false)",
            initializer,
            StringComparison.Ordinal);
        var authorizationInvalidation = initializer.IndexOf(
            "Invalidate-ExistingAuthorization -Path $authorizationPath",
            StringComparison.Ordinal);
        var baseConfigurationWrite = initializer.IndexOf(
            "Write-ProtectedJson $playoutPath",
            StringComparison.Ordinal);
        Assert.True(authorizationInvalidation >= 0);
        Assert.True(baseConfigurationWrite > authorizationInvalidation);
        Assert.Contains(
            "Write-ProtectedJson $authorizationPath",
            initializer,
            StringComparison.Ordinal);
    }

    [Fact]
    public void DevelopmentRuntimeBundleRequiresIndependentHashAndClosedPayloadValidation()
    {
        var bundleCreator = File.ReadAllText(Path.Combine(
            RepositoryRoot,
            "scripts",
            "New-LegacyRuntimeBundle.ps1"));
        var bundleInitializer = File.ReadAllText(Path.Combine(
            RepositoryRoot,
            "scripts",
            "Initialize-LegacyRuntimeBundle.ps1"));

        Assert.Contains("$expectedResAssetCount = 34", bundleCreator, StringComparison.Ordinal);
        Assert.Contains(
            "Get-GitRepositoryAncestor -Path $sourceRoot",
            bundleCreator,
            StringComparison.Ordinal);
        Assert.Contains(
            "MmoneyCoder material is forbidden",
            bundleCreator,
            StringComparison.Ordinal);
        Assert.Contains(
            "LegacyRuntimeBundle.zip.sha256",
            bundleCreator,
            StringComparison.Ordinal);

        Assert.Contains(
            "[string] $ExpectedSha256",
            bundleInitializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "ExpectedSha256 must be a separately delivered",
            bundleInitializer,
            StringComparison.Ordinal);
        var archiveHashValidation = bundleInitializer.IndexOf(
            "$actualArchiveHash.Equals(",
            StringComparison.Ordinal);
        var archiveOpen = bundleInitializer.IndexOf(
            "New-Object IO.Compression.ZipArchive(",
            StringComparison.Ordinal);
        Assert.True(archiveHashValidation >= 0);
        Assert.True(archiveOpen > archiveHashValidation);
        Assert.Contains(
            "Archive entry expanded beyond its declared length",
            bundleInitializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "$actualCutsFileCount -le 0",
            bundleInitializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "[Environment+SpecialFolder]::LocalApplicationData",
            bundleInitializer,
            StringComparison.Ordinal);
        Assert.DoesNotContain(
            "Get-NormalizedFullPath -Path $env:LOCALAPPDATA",
            bundleInitializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Existing Directory.Build.local.props is conditional, duplicated",
            bundleInitializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Assert-NoReparsePointInTree",
            bundleInitializer,
            StringComparison.Ordinal);
    }

    [Fact]
    public void ExistingDevelopmentPcInitializerUsesLocalAssetsWithoutPerformingLiveActions()
    {
        var initializer = File.ReadAllText(Path.Combine(
            RepositoryRoot,
            "scripts",
            "Initialize-ExistingDevelopmentPc.ps1"));

        Assert.Contains("[string] $LegacyRuntimeSourceRoot", initializer,
            StringComparison.Ordinal);
        Assert.Contains("[switch] $NoFolderPicker", initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            @"..\MBN_STOCK_N\MBN_STOCK_N\bin\Debug",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Test-DirectCutsAndResRootCandidate",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "-Path $siblingCandidate",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "System.Windows.Forms.FolderBrowserDialog",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "$selectedLeaf.Equals('Cuts'",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "$selectedLeaf.Equals('Res'",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "single common parent of Cuts and Res",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "must be on a ready local fixed drive",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "must be outside and must not overlap this repository",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains("Assert-NoReparsePointInExistingAncestry", initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Legacy runtime source root contains characters reserved by MSBuild.",
            initializer,
            StringComparison.Ordinal);
        Assert.DoesNotContain("$bundleCreatorPath =", initializer,
            StringComparison.Ordinal);
        Assert.DoesNotContain("$bundleInitializerPath =", initializer,
            StringComparison.Ordinal);
        Assert.DoesNotContain("& $bundleCreatorPath", initializer,
            StringComparison.Ordinal);
        Assert.DoesNotContain("& $bundleInitializerPath", initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "function New-DirectRuntimeBindingFile",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "function Get-RuntimeFoldersPreflight",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "runtime-folders.local.json",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains("[switch] $ReplaceRuntimeBinding", initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "The selected Cuts/Res path differs from the existing runtime binding.",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains("Set-RuntimeBindingAtomically", initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "[IO.File]::Replace($temporary, $destination, $backup)",
            initializer,
            StringComparison.Ordinal);
        var directBinding = initializer.LastIndexOf(
            "New-DirectRuntimeBindingFile",
            StringComparison.Ordinal);
        var bindingReplace = initializer.LastIndexOf(
            "Set-RuntimeBindingAtomically",
            StringComparison.Ordinal);
        Assert.True(directBinding >= 0);
        Assert.True(bindingReplace > directBinding);

        Assert.Contains("[string] $DatabaseIniPath", initializer,
            StringComparison.Ordinal);
        Assert.Contains("[IO.Path]::IsPathRooted($DatabaseIniPath)", initializer,
            StringComparison.Ordinal);
        Assert.Contains("[switch] $SkipDatabaseProfile", initializer,
            StringComparison.Ordinal);
        Assert.Contains("[switch] $ReplaceDatabaseProfile", initializer,
            StringComparison.Ordinal);
        Assert.Contains(@"Res\MmoneyCoder.ini", initializer,
            StringComparison.Ordinal);
        Assert.Contains("$maximumDatabaseIniLength = 1MB", initializer,
            StringComparison.Ordinal);
        Assert.Contains("Copy-DatabaseProfileAtomically", initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "[IO.File]::Replace($temporaryPath, $Destination, $backupPath)",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains("SetAccessRuleProtection($true, $false)", initializer,
            StringComparison.Ordinal);

        Assert.Contains("'Inspect-K3DRegistration.ps1'", initializer,
            StringComparison.Ordinal);
        Assert.Contains("Get-VerifiedK3DRegistration -Reports $registrationReports",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "[string] $registration.RegistryHive -cne 'HKLM'",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "[string] $registration.RegistryView -cne 'Registry64'",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "[string] $registration.CurrentUserOverrides -cne 'None'",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "[string] $classReport.ThreadingModel -cne 'Apartment'",
            initializer,
            StringComparison.Ordinal);
        Assert.DoesNotContain("Activator.CreateInstance", initializer,
            StringComparison.Ordinal);
        Assert.DoesNotContain("Connect(", initializer, StringComparison.Ordinal);
        Assert.DoesNotContain("TAKE IN", initializer, StringComparison.Ordinal);
        Assert.DoesNotContain("TAKE OUT", initializer, StringComparison.Ordinal);

        Assert.Contains("[switch] $ConfigureDevelopmentLive", initializer,
            StringComparison.Ordinal);
        Assert.Contains("[switch] $ReplaceLiveConfig", initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "-ConfigureDevelopmentLive requires the default Debug x64 build verification.",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Independent native and interop SHA-256 approvals are both required.",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Get-FileHash -LiteralPath $nativePath -Algorithm SHA256",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Get-FileHash -LiteralPath $interopPath -Algorithm SHA256",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "$PlayoutHost -cne '127.0.0.1'",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Existing Development Live files differ from the exact requested",
            initializer,
            StringComparison.Ordinal);

        Assert.Contains("& $DotNetPath build $ProjectPath",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains("'--no-restore'", initializer, StringComparison.Ordinal);
        Assert.Contains(
            "'-p:LegacyRuntimeAssetsMode=Required'",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "$runtimeRootProperty = \"-p:LegacyRuntimeSourceRoot=$ExpectedRuntimeRoot\"",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Get-Command -Name 'dotnet.exe' -CommandType Application",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "'-getProperty:LegacyRuntimeAssetsEnabled'",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "unexpectedly resolved as SourceOnly",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Debug x64 copied Cuts instead of using the selected external path.",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Credential-bearing MmoneyCoder.ini entered the Debug build output.",
            initializer,
            StringComparison.Ordinal);

        var runtimeWrite = initializer.LastIndexOf(
            "Set-RuntimeBindingAtomically",
            StringComparison.Ordinal);
        var pinWrite = initializer.IndexOf(
            "-Description 'K3D persistent pin'",
            StringComparison.Ordinal);
        var databaseWrite = initializer.LastIndexOf(
            "Copy-DatabaseProfileAtomically",
            StringComparison.Ordinal);
        var runtimeFoldersWrite = initializer.LastIndexOf(
            "-Description 'runtime folder configuration'",
            StringComparison.Ordinal);
        var build = initializer.LastIndexOf(
            "Invoke-DebugBuildAndVerify",
            StringComparison.Ordinal);
        var finalK3dVerification = initializer.LastIndexOf(
            "$finalNativeHash = (",
            StringComparison.Ordinal);
        var liveWrite = initializer.LastIndexOf(
            "& $liveInitializerPath @liveArguments",
            StringComparison.Ordinal);
        var cleanup = initializer.LastIndexOf(
            "-Path $rollbackRoot",
            StringComparison.Ordinal);
        Assert.True(pinWrite >= 0);
        Assert.True(build > pinWrite);
        Assert.True(databaseWrite > build);
        Assert.True(runtimeFoldersWrite > databaseWrite);
        Assert.True(finalK3dVerification > runtimeFoldersWrite);
        Assert.True(liveWrite > finalK3dVerification);
        Assert.True(runtimeWrite > liveWrite);
        Assert.True(cleanup > runtimeWrite);
    }

    [Fact]
    public void ExistingDevelopmentPcInitializerRollsBackAndReissuesLiveFailClosed()
    {
        var initializer = File.ReadAllText(Path.Combine(
            RepositoryRoot,
            "scripts",
            "Initialize-ExistingDevelopmentPc.ps1"));

        Assert.Contains("function New-FileRollbackSnapshot", initializer,
            StringComparison.Ordinal);
        Assert.Contains("function Restore-FileRollbackSnapshot", initializer,
            StringComparison.Ordinal);
        Assert.Contains("OriginalBytes = [IO.File]::ReadAllBytes($backupPath)",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains("AccessSddl = $accessSddl", initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "$Description rollback backup failed its immediate ACL/hash verification.",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Previous database profile backup failed immediate ACL/hash verification.",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "$Description rollback did not restore the exact bytes and ACL.",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "'Local\\MBN_STOCK_WEBVIEW.ExistingDevelopmentPcInitialization.v1'",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Another existing-development-PC initialization is already running.",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Development Live base configuration rollback failed:",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "The durable pin remains at",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "anchor could not be restored:",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Set-UserOnlyDirectoryAcl -Path $rollbackRoot",
            initializer,
            StringComparison.Ordinal);

        var databaseMutation = initializer.LastIndexOf(
            "$databaseMutationStarted = $true",
            StringComparison.Ordinal);
        var databaseWrite = initializer.LastIndexOf(
            "Copy-DatabaseProfileAtomically",
            StringComparison.Ordinal);
        var databaseRollback = initializer.LastIndexOf(
            "-Snapshot $databaseSnapshot",
            StringComparison.Ordinal);
        var runtimeFoldersMutation = initializer.LastIndexOf(
            "$runtimeFoldersMutationStarted = $true",
            StringComparison.Ordinal);
        var runtimeFoldersRollback = initializer.LastIndexOf(
            "-Snapshot $runtimeFoldersSnapshot",
            StringComparison.Ordinal);
        var runtimeMutation = initializer.LastIndexOf(
            "$runtimeBindingMutationStarted = $true",
            StringComparison.Ordinal);
        var runtimeWrite = initializer.LastIndexOf(
            "Set-RuntimeBindingAtomically",
            StringComparison.Ordinal);
        var runtimeRollback = initializer.LastIndexOf(
            "-Snapshot $propsSnapshot",
            StringComparison.Ordinal);
        Assert.True(databaseMutation >= 0 && databaseMutation < databaseWrite);
        Assert.True(runtimeFoldersMutation >= 0);
        Assert.True(runtimeMutation >= 0 && runtimeMutation < runtimeWrite);
        Assert.True(databaseRollback > databaseWrite);
        Assert.True(runtimeFoldersRollback > runtimeFoldersMutation);
        Assert.True(runtimeRollback > runtimeWrite);
        Assert.Contains("Original failure:", initializer, StringComparison.Ordinal);
        Assert.Contains("Secondary recovery failure(s):", initializer,
            StringComparison.Ordinal);

        Assert.Contains("function Assert-ExactObjectProperties", initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "-Description 'K3D registration inspection result'",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "function New-DirectRuntimeBindingFile",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "Runtime folder configuration rollback failed:",
            initializer,
            StringComparison.Ordinal);

        Assert.Contains("function Test-ExactUtf8Text", initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "case changes, type coercion, reordered properties",
            initializer,
            StringComparison.Ordinal);
        Assert.DoesNotContain("Get-JsonContractText", initializer,
            StringComparison.Ordinal);
        var livePreflightStart = initializer.IndexOf(
            "function Get-DevelopmentLivePreflight",
            StringComparison.Ordinal);
        var livePreflightEnd = initializer.IndexOf(
            "function Copy-DatabaseProfileAtomically",
            livePreflightStart,
            StringComparison.Ordinal);
        Assert.True(livePreflightStart >= 0 && livePreflightEnd > livePreflightStart);
        var livePreflightFunction = initializer[livePreflightStart..livePreflightEnd];
        Assert.DoesNotContain("NeedsWrite = $false", livePreflightFunction,
            StringComparison.Ordinal);
        Assert.Contains("DevelopmentLiveReused = $false", initializer,
            StringComparison.Ordinal);

        var firstPersistentLiveInvalidation = initializer.IndexOf(
            "# Invalidate the previous one-launch authorization before database/runtime",
            StringComparison.Ordinal);
        var setupDirectoryCreation = initializer.IndexOf(
            "[IO.Directory]::CreateDirectory($setupParent)",
            StringComparison.Ordinal);
        var buildVerification = initializer.LastIndexOf(
            "Invoke-DebugBuildAndVerify",
            StringComparison.Ordinal);
        var pinIssue = initializer.IndexOf(
            "-Description 'K3D persistent pin'",
            StringComparison.Ordinal);
        var finalK3dVerification = initializer.LastIndexOf(
            "$finalNativeHash = (",
            StringComparison.Ordinal);
        var liveIssue = initializer.LastIndexOf(
            "$liveResults = @(& $liveInitializerPath @liveArguments)",
            StringComparison.Ordinal);
        var liveJsonVerification = initializer.LastIndexOf(
            "-ExpectedJson ([string] $livePreflight.AuthorizationJson)",
            StringComparison.Ordinal);
        var bindingCommit = initializer.LastIndexOf(
            "Set-RuntimeBindingAtomically",
            StringComparison.Ordinal);
        var setupCleanup = initializer.LastIndexOf(
            "-Path $rollbackRoot",
            StringComparison.Ordinal);
        var failClosedLiveRemoval = initializer.LastIndexOf(
            "Remove-DevelopmentLiveAuthorizationFailClosed",
            StringComparison.Ordinal);
        Assert.True(pinIssue >= 0);
        Assert.True(pinIssue < firstPersistentLiveInvalidation);
        Assert.True(firstPersistentLiveInvalidation < setupDirectoryCreation);
        Assert.True(setupDirectoryCreation < buildVerification);
        Assert.True(buildVerification < finalK3dVerification);
        Assert.True(finalK3dVerification < liveIssue);
        Assert.True(liveIssue < liveJsonVerification);
        Assert.True(liveJsonVerification < bindingCommit);
        Assert.True(bindingCommit < setupCleanup);
        Assert.True(liveJsonVerification < failClosedLiveRemoval);
        Assert.Contains(
            "Development Live initializer returned unexpected success-stream output.",
            initializer,
            StringComparison.Ordinal);
        Assert.Contains(
            "$Description failed its exact JSON/ACL verification.",
            initializer,
            StringComparison.Ordinal);
    }

    [Fact]
    public void RepositoryDoesNotContainCopiedLegacyRuntimeTrees()
    {
        Assert.False(Directory.Exists(Path.Combine(RepositoryRoot, "Cuts")));
        Assert.False(Directory.Exists(Path.Combine(RepositoryRoot, "Res")));
        Assert.False(Directory.Exists(Path.Combine(
            RepositoryRoot,
            "src",
            "MBN_STOCK_WEBVIEW.LegacyParityApp",
            "Cuts")));
        Assert.False(Directory.Exists(Path.Combine(
            RepositoryRoot,
            "src",
            "MBN_STOCK_WEBVIEW.LegacyParityApp",
            "Res")));
    }

    private static void AssertLinkedRuntimeTree(
        IReadOnlyCollection<XElement> contents,
        string include,
        string targetPrefix)
    {
        var content = Assert.Single(
            contents,
            element => string.Equals(
                element.Attribute("Include")?.Value,
                include,
                StringComparison.Ordinal));

        Assert.StartsWith(targetPrefix, content.Element("Link")?.Value, StringComparison.Ordinal);
        Assert.StartsWith(
            targetPrefix,
            content.Element("TargetPath")?.Value,
            StringComparison.Ordinal);
        Assert.Equal("PreserveNewest", content.Element("CopyToOutputDirectory")?.Value);
        Assert.Equal("PreserveNewest", content.Element("CopyToPublishDirectory")?.Value);
    }

    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("Could not locate the repository root.");
    }
}
