using System.Text.Json;

namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests;

public sealed class LegacyDevelopmentLiveStartupContractTests
{
    private const string ParityProjectGuid =
        "{0F72B3C8-8A81-4D1E-8E57-0C0E9A9960C1}";
    private const string PrototypeProjectGuid =
        "{E6F5F1A0-D0FD-4E6B-A76D-1E8E3DAE8606}";
    private const string ExactArgument = "--development-live";

    private static readonly string RepositoryRoot = FindRepositoryRoot();
    private static readonly string ParityRoot = Path.Combine(
        RepositoryRoot,
        "src",
        "MBN_STOCK_WEBVIEW.LegacyParityApp");

    [Fact]
    public void VisualStudioExposesOneLivePackageProfileWithoutDryRun()
    {
        using var document = JsonDocument.Parse(File.ReadAllText(Path.Combine(
            ParityRoot,
            "Properties",
            "launchSettings.json")));
        var profile = Assert.Single(document.RootElement.GetProperty("profiles")
            .EnumerateObject());

        Assert.Contains("Live", profile.Name, StringComparison.Ordinal);
        Assert.DoesNotContain("DryRun", profile.Name, StringComparison.Ordinal);
        Assert.Equal("MsixPackage", profile.Value.GetProperty("commandName").GetString());
        var arguments = profile.Value.TryGetProperty("commandLineArgs", out var value)
            ? value.GetString()
            : null;
        Assert.True(
            string.IsNullOrEmpty(arguments) ||
            string.Equals(arguments, ExactArgument, StringComparison.Ordinal),
            "The sole Live package profile may use only the normal launch or exact Live argument.");
    }

    [Fact]
    public void SharedSolutionLaunchStartsOnlyTheLegacyParityApp()
    {
        using var document = JsonDocument.Parse(File.ReadAllText(Path.Combine(
            RepositoryRoot,
            "MBN_STOCK_WEBVIEW.slnLaunch")));
        var profile = Assert.Single(document.RootElement.EnumerateArray());
        Assert.Equal("Legacy Parity App (VS F5)", profile.GetProperty("Name").GetString());
        var project = Assert.Single(profile.GetProperty("Projects").EnumerateArray());
        Assert.Equal(
            "src\\MBN_STOCK_WEBVIEW.LegacyParityApp\\MBN_STOCK_WEBVIEW.LegacyParityApp.csproj",
            project.GetProperty("Path").GetString());
        Assert.Equal("Start", project.GetProperty("Action").GetString());
    }

    [Fact]
    public void NormalDebugDeployTargetsParityInsteadOfThePrototype()
    {
        var solution = File.ReadAllText(Path.Combine(
            RepositoryRoot,
            "MBN_STOCK_WEBVIEW.sln"));

        Assert.Contains(
            $"{ParityProjectGuid}.Debug|x64.Deploy.0 = Debug|x64",
            solution,
            StringComparison.Ordinal);
        Assert.DoesNotContain(
            $"{PrototypeProjectGuid}.Debug|x64.Deploy.0 = Debug|x64",
            solution,
            StringComparison.Ordinal);
    }

    [Fact]
    public void AppAppliesRuntimeGateBeforeConstructingAnyRuntimeWindow()
    {
        var app = File.ReadAllText(Path.Combine(ParityRoot, "App.xaml.cs"));
        var bootstrap = app.IndexOf(
            "DevelopmentLiveLaunchBootstrap.TryApply(",
            StringComparison.Ordinal);
        var constructSetupWindow = app.IndexOf(
            "new FirstRunSetupWindow()",
            StringComparison.Ordinal);
        var constructMainWindow = app.IndexOf(
            "new MainWindow();",
            StringComparison.Ordinal);
        var sourceOnlyDirective = app.IndexOf(
            "#if SOURCE_ONLY_RUNTIME",
            StringComparison.Ordinal);
        var fullRuntimeDirective = app.IndexOf(
            "#else",
            constructSetupWindow,
            StringComparison.Ordinal);
        var runtimeEndDirective = app.IndexOf(
            "#endif",
            constructMainWindow,
            StringComparison.Ordinal);

        Assert.True(bootstrap >= 0);
        Assert.True(sourceOnlyDirective >= 0);
        Assert.True(constructSetupWindow > sourceOnlyDirective);
        Assert.True(fullRuntimeDirective > constructSetupWindow);
        Assert.True(bootstrap > fullRuntimeDirective);
        Assert.True(constructMainWindow > bootstrap);
        Assert.True(runtimeEndDirective > constructMainWindow);
        Assert.Contains("args.Arguments", app, StringComparison.Ordinal);
        Assert.Contains("Environment.GetCommandLineArgs()", app, StringComparison.Ordinal);
        Assert.Contains("ResolveLaunchArguments(", app, StringComparison.Ordinal);
        Assert.Contains(
            "IsDevelopmentLiveLaunchRequested = developmentLive.IsRequested;",
            app,
            StringComparison.Ordinal);
        Assert.Contains(
            "IsDevelopmentLiveLaunch = developmentLive.IsApplied;",
            app,
            StringComparison.Ordinal);
        Assert.DoesNotContain("#if DEBUG", app, StringComparison.Ordinal);
    }

    [Fact]
    public void AppliedDevelopmentLiveUsesSelectedRuntimePathsAndProtectedDatabaseInputs()
    {
        var window = File.ReadAllText(Path.Combine(ParityRoot, "MainWindow.xaml.cs"));
        var playout = File.ReadAllText(Path.Combine(
            ParityRoot,
            "MainWindow.Playout.cs"));

        Assert.Contains(
            "_isDevelopmentLiveLaunch = App.IsDevelopmentLiveLaunch;",
            window,
            StringComparison.Ordinal);
        Assert.Contains(
            "_isDevelopmentLiveLaunchRequested = App.IsDevelopmentLiveLaunchRequested;",
            window,
            StringComparison.Ordinal);
        Assert.Contains(
            "var selectedResourceDirectory =\n" +
            "            _appliedOperatorSettings.ResourceDirectory;",
            window.ReplaceLineEndings("\n"),
            StringComparison.Ordinal);
        Assert.Contains(
            "LegacyRuntimeStockCutMenuLoader.ResolveFromResourceDirectory(",
            window,
            StringComparison.Ordinal);
        Assert.Contains(
            "_isDevelopmentLiveLaunch ||\n" +
            "                selectedResourceDirectory is null\n" +
            "                ? null",
            window.ReplaceLineEndings("\n"),
            StringComparison.Ordinal);
        Assert.Contains(
            "DatabaseRuntime.CreateDevelopmentLiveLocalOverride()",
            window,
            StringComparison.Ordinal);
        Assert.Contains(
            "? PlayoutOptionsLoader.LoadDevelopmentLive(",
            playout,
            StringComparison.Ordinal);
        Assert.Contains(
            "operatorSceneDirectory:\n" +
            "                        _appliedOperatorSettings.SceneDirectory",
            playout.ReplaceLineEndings("\n"),
            StringComparison.Ordinal);
        Assert.DoesNotContain("forceSafeDryRun", playout, StringComparison.Ordinal);
        Assert.Contains(
            "if (_playoutOptions.Mode != PlayoutMode.Live)",
            playout,
            StringComparison.Ordinal);
    }

    [Fact]
    public void DatabaseFailureDoesNotDowngradeOrBlockValidatedLiveEngine()
    {
        var window = File.ReadAllText(Path.Combine(ParityRoot, "MainWindow.xaml.cs"))
            .ReplaceLineEndings("\n");
        var playout = File.ReadAllText(Path.Combine(
                ParityRoot,
                "MainWindow.Playout.cs"))
            .ReplaceLineEndings("\n");

        var runtimeReset = window.IndexOf(
            "_databaseRuntime = null;",
            StringComparison.Ordinal);
        var pagePlanReset = window.IndexOf(
            "fixedPagePlanProvider = null;",
            runtimeReset,
            StringComparison.Ordinal);
        var databaseFailureEnd = window.IndexOf(
            "_industryWorkflow = new LegacyIndustrySelectionWorkflow(",
            pagePlanReset,
            StringComparison.Ordinal);
        var playoutInitialization = window.IndexOf(
            "InitializePlayoutRuntime();",
            StringComparison.Ordinal);

        Assert.True(runtimeReset >= 0);
        Assert.True(pagePlanReset > runtimeReset);
        Assert.True(databaseFailureEnd > pagePlanReset);
        Assert.True(playoutInitialization > databaseFailureEnd);
        Assert.DoesNotContain(
            "_developmentLiveStartupBlocked = true;",
            window[runtimeReset..databaseFailureEnd],
            StringComparison.Ordinal);
        Assert.DoesNotContain("databaseInitializationSucceeded", window,
            StringComparison.Ordinal);
        Assert.Contains(
            "_isDevelopmentLiveLaunch\n" +
            "                ? PlayoutOptionsLoader.LoadDevelopmentLive(",
            playout,
            StringComparison.Ordinal);
        Assert.Contains(
            "if (_playoutOptions.Mode != PlayoutMode.Live)",
            playout,
            StringComparison.Ordinal);
        Assert.Contains(
            "_playoutEngine = PlayoutEngineFactory.Create(",
            playout,
            StringComparison.Ordinal);
    }

    [Fact]
    public void BootstrapOrPlayoutInvariantFailureBlocksEngineAndAutomaticConnect()
    {
        var window = File.ReadAllText(Path.Combine(ParityRoot, "MainWindow.xaml.cs"))
            .ReplaceLineEndings("\n");
        var playout = File.ReadAllText(Path.Combine(
                ParityRoot,
                "MainWindow.Playout.cs"))
            .ReplaceLineEndings("\n");

        var initialBlock = window.IndexOf(
            "_developmentLiveStartupBlocked =\n" +
            "            _isDevelopmentLiveLaunchRequested &&\n" +
            "            !_isDevelopmentLiveLaunch;",
            StringComparison.Ordinal);
        var earlyBlock = playout.IndexOf(
            "if (_developmentLiveStartupBlocked)",
            StringComparison.Ordinal);
        var earlyReturn = playout.IndexOf("return;", earlyBlock, StringComparison.Ordinal);
        var loadLive = playout.IndexOf(
            "PlayoutOptionsLoader.LoadDevelopmentLive(",
            StringComparison.Ordinal);
        var requireLive = playout.IndexOf(
            "if (_playoutOptions.Mode != PlayoutMode.Live)",
            StringComparison.Ordinal);
        var createEngine = playout.IndexOf(
            "_playoutEngine = PlayoutEngineFactory.Create(",
            StringComparison.Ordinal);
        var blockOnInitializationFailure = playout.IndexOf(
            "_developmentLiveStartupBlocked = true;",
            createEngine,
            StringComparison.Ordinal);

        Assert.True(initialBlock >= 0);
        Assert.True(earlyBlock >= 0);
        Assert.True(earlyReturn > earlyBlock);
        Assert.True(loadLive > earlyReturn);
        Assert.True(requireLive > loadLive);
        Assert.True(createEngine > requireLive);
        Assert.True(blockOnInitializationFailure > createEngine);
        Assert.DoesNotContain("forceSafeDryRun", playout, StringComparison.Ordinal);
        Assert.Contains(
            "if (!_developmentLiveStartupBlocked)\n" +
            "            {\n" +
            "                await ConnectPlayoutAsync(_lifetimeCancellation.Token);",
            window,
            StringComparison.Ordinal);
    }

    [Fact]
    public void TrackedExampleContainsOnlyNonUsableHashPlaceholders()
    {
        var example = File.ReadAllText(Path.Combine(
            RepositoryRoot,
            "Config",
            "playout.development-live.example.json"));
        using var document = JsonDocument.Parse(example);
        var root = document.RootElement;

        Assert.Equal(1, root.GetProperty("schemaVersion").GetInt32());
        Assert.Equal("Live", root.GetProperty("mode").GetString());
        Assert.StartsWith("<64-HEX-", root.GetProperty("nativeSha256").GetString());
        Assert.StartsWith("<64-HEX-", root.GetProperty("interopSha256").GetString());
        Assert.DoesNotContain(":\\", example, StringComparison.Ordinal);
    }

    [Fact]
    public void RealDevelopmentAuthorizationFileIsIgnoredEverywhere()
    {
        var ignore = File.ReadAllText(Path.Combine(RepositoryRoot, ".gitignore"));

        Assert.Contains("Config/playout.development-live.local.json", ignore,
            StringComparison.Ordinal);
        Assert.Contains("**/playout.development-live.local.json", ignore,
            StringComparison.Ordinal);
    }

    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.");
    }
}
