using System.Buffers.Binary;

namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests;

public sealed class LegacyStartupIntroNativeContractTests
{
    private static readonly string RepositoryRoot = FindRepositoryRoot();
    private static readonly string AppRoot = Path.Combine(
        RepositoryRoot,
        "src",
        "MBN_STOCK_WEBVIEW.LegacyParityApp");
    private static readonly string WindowXaml = File.ReadAllText(
        Path.Combine(AppRoot, "MainWindow.xaml"));
    private static readonly string Window = File.ReadAllText(
        Path.Combine(AppRoot, "MainWindow.xaml.cs"));
    private static readonly string StartupIntro = File.ReadAllText(
        Path.Combine(AppRoot, "MainWindow.StartupIntro.cs"));
    private static readonly string Manifest = File.ReadAllText(
        Path.Combine(AppRoot, "Package.appxmanifest"));
    private static readonly string Project = File.ReadAllText(
        Path.Combine(AppRoot, "MBN_STOCK_WEBVIEW.LegacyParityApp.csproj"));

    [Fact]
    public void IntroUsesOnlyPackagedBrandAssetsAndCompactCopy()
    {
        var overlay = Slice(
            WindowXaml,
            "x:Name=\"LoadingOverlay\"",
            "<InfoBar");

        Assert.Contains(
            "ms-appx:///Assets/Square150x150Logo.scale-200.png",
            overlay,
            StringComparison.Ordinal);
        Assert.Contains(
            "ms-appx:///Assets/StartupHero.png",
            overlay,
            StringComparison.Ordinal);
        Assert.Contains(
            "ms-appx:///Web/Brand/logo.png",
            overlay,
            StringComparison.Ordinal);
        Assert.Contains("V-STOCK", overlay, StringComparison.Ordinal);
        Assert.Contains("증권정보 송출 시스템", overlay, StringComparison.Ordinal);
        Assert.DoesNotContain("http://", overlay, StringComparison.OrdinalIgnoreCase);
        Assert.DoesNotContain("https://", overlay, StringComparison.OrdinalIgnoreCase);
        Assert.DoesNotContain("원본 호환 화면을 준비하고 있습니다.", overlay,
            StringComparison.Ordinal);
    }

    [Fact]
    public void GeneratedBroadcastHeroIsAProjectPackagedWidePng()
    {
        const string heroInclude =
            "<Content Include=\"..\\..\\Assets\\StartupHero.png\" Link=\"Assets\\StartupHero.png\" />";
        Assert.Contains(heroInclude, Project, StringComparison.Ordinal);

        var heroPath = Path.Combine(RepositoryRoot, "Assets", "StartupHero.png");
        var bytes = File.ReadAllBytes(heroPath);
        Assert.True(bytes.Length > 500_000);
        Assert.Equal(
            new byte[] { 137, 80, 78, 71, 13, 10, 26, 10 },
            bytes[..8]);
        Assert.True(bytes.Length >= 24);
        var width = BinaryPrimitives.ReadInt32BigEndian(bytes.AsSpan(16, 4));
        var height = BinaryPrimitives.ReadInt32BigEndian(bytes.AsSpan(20, 4));
        Assert.True(width >= 1600);
        Assert.True(height >= 900);
        Assert.True((double)width / height > 1.7);
    }

    [Fact]
    public void IntroUsesTheDeepNavyMbnAccentAndShortNativeMotion()
    {
        var overlay = Slice(
            WindowXaml,
            "x:Name=\"LoadingOverlay\"",
            "<InfoBar");

        Assert.Contains("#FF08182B", WindowXaml, StringComparison.OrdinalIgnoreCase);
        Assert.Contains("#FFF47B20", overlay, StringComparison.OrdinalIgnoreCase);
        Assert.Contains(
            "x:Name=\"StartupIntroEnterStoryboard\"",
            WindowXaml,
            StringComparison.Ordinal);
        Assert.Contains(
            "x:Name=\"StartupIntroExitStoryboard\"",
            WindowXaml,
            StringComparison.Ordinal);
        Assert.Contains("IsIndeterminate=\"True\"", overlay, StringComparison.Ordinal);
        Assert.Contains(
            "AutomationProperties.Name=\"매일경제TV V-STOCK 시작 화면\"",
            overlay,
            StringComparison.Ordinal);
        Assert.Contains(
            "AutomationProperties.AccessibilityView=\"Content\"",
            overlay,
            StringComparison.Ordinal);
    }

    [Fact]
    public void EveryStartupOutcomeKeepsItsCancellableTwoSecondHold()
    {
        Assert.Contains(
            "TimeSpan.FromMilliseconds(2050)",
            StartupIntro,
            StringComparison.Ordinal);
        Assert.Contains(
            "TimeSpan.FromMilliseconds(180)",
            StartupIntro,
            StringComparison.Ordinal);
        Assert.Contains("UISettings", StartupIntro, StringComparison.Ordinal);
        Assert.Contains(".AnimationsEnabled", StartupIntro, StringComparison.Ordinal);
        Assert.Contains(
            "Interlocked.CompareExchange(",
            StartupIntro,
            StringComparison.Ordinal);
        Assert.Contains(
            "_lifetimeCancellation.Token",
            StartupIntro,
            StringComparison.Ordinal);
        Assert.Contains("Task.Delay(", StartupIntro, StringComparison.Ordinal);
        Assert.DoesNotContain(
            "skipMinimumDuration",
            StartupIntro,
            StringComparison.Ordinal);
        Assert.Contains(
            "if (!_startupIntroAnimationsEnabled)",
            StartupIntro,
            StringComparison.Ordinal);
    }

    [Fact]
    public void WebReadyPostsTheFirstStateBeforeEveryOutcomeUsesTheSameDismissal()
    {
        var dispatch = Slice(
            Window,
            "private async Task ProcessIntentAsync(",
            "private static bool IsNamedPlaylistModalIntent(");
        var finalPost = RequiredIndex(dispatch, "PostState(state);");
        var readyDismiss = RequiredIndex(
            dispatch,
            "if (intent is LegacyReadyIntent)",
            finalPost);
        var normalDismiss = RequiredIndex(
            dispatch,
            "BeginDismissStartupIntro();",
            readyDismiss);
        Assert.True(finalPost < readyDismiss);
        Assert.True(readyDismiss < normalDismiss);

        var failurePost = RequiredIndex(dispatch, "PostState(failureState);");
        var immediateDismiss = RequiredIndex(
            dispatch,
            "BeginDismissStartupIntro();",
            failurePost);
        Assert.True(failurePost < immediateDismiss);

        var navigation = Slice(
            Window,
            "private void OnNavigationCompleted(",
            "private static void OnNewWindowRequested(");
        Assert.Contains("if (!args.IsSuccess)", navigation, StringComparison.Ordinal);
        Assert.Contains(
            "BeginDismissStartupIntro();",
            navigation,
            StringComparison.Ordinal);
        Assert.DoesNotContain(
            "LoadingOverlay.Visibility = Visibility.Collapsed;",
            navigation,
            StringComparison.Ordinal);
    }

    [Fact]
    public void IntroBlocksInputUntilItsOneShotDismissalCompletes()
    {
        var browser = Slice(WindowXaml, "<WebView2", "/>");
        Assert.Contains("x:Name=\"Browser\"", browser, StringComparison.Ordinal);
        Assert.Contains("IsHitTestVisible=\"False\"", browser, StringComparison.Ordinal);
        Assert.Contains(
            "LoadingOverlay.Visibility = Visibility.Collapsed;",
            StartupIntro,
            StringComparison.Ordinal);
        Assert.Contains(
            "LoadingOverlay.IsHitTestVisible = false;",
            StartupIntro,
            StringComparison.Ordinal);
        Assert.Contains(
            "Browser.IsHitTestVisible = true;",
            StartupIntro,
            StringComparison.Ordinal);

        var loaded = Slice(
            Window,
            "private async void OnRootLoaded(",
            "private void ConfigureWindow()");
        Assert.Contains("StartStartupIntro();", loaded, StringComparison.Ordinal);
    }

    [Fact]
    public void MsixSystemSplashMatchesTheNativeIntroCanvas()
    {
        Assert.Contains(
            "BackgroundColor=\"#08182B\"",
            Manifest,
            StringComparison.OrdinalIgnoreCase);
        Assert.Contains(
            "<uap:SplashScreen Image=\"Assets\\SplashScreen.png\" />",
            Manifest,
            StringComparison.Ordinal);
    }

    private static string Slice(string value, string start, string end)
    {
        var startIndex = RequiredIndex(value, start);
        var endIndex = RequiredIndex(value, end, startIndex + start.Length);
        return value[startIndex..endIndex];
    }

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

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

            directory = directory.Parent;
        }

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