namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests;

public sealed class LegacySidebarIconContractTests
{
    private static readonly string RepositoryRoot = FindRepositoryRoot();
    private static readonly string WebRoot = Path.Combine(
        RepositoryRoot,
        "src",
        "MBN_STOCK_WEBVIEW.LegacyParityApp",
        "Web");
    private static readonly string Markup = File.ReadAllText(
        Path.Combine(WebRoot, "index.html"));
    private static readonly string Navigation = File.ReadAllText(
        Path.Combine(WebRoot, "workspace-navigation.js"));
    private static readonly string Styles = File.ReadAllText(
        Path.Combine(WebRoot, "styles.css"));
    private static readonly string PackageInputSmoke = File.ReadAllText(
        Path.Combine(RepositoryRoot, "scripts", "Test-LegacyPackageInput.mjs"));
    private static readonly string UiOnlySmoke = File.ReadAllText(
        Path.Combine(RepositoryRoot, "scripts", "Test-LegacyPackageUiOnlySmoke.mjs"));

    [Fact]
    public void EverySidebarDestinationUsesOneClosedSemanticSvgIcon()
    {
        var mappings = new (string Anchor, string Icon)[]
        {
            ("id=\"workspace-stock-tab\"", "stock-cut"),
            ("data-tab-id=\"overseas\"", "overseas"),
            ("data-tab-id=\"exchange\"", "exchange"),
            ("data-tab-id=\"index\"", "index"),
            ("data-tab-id=\"kospiIndustry\"", "kospi"),
            ("data-tab-id=\"kosdaqIndustry\"", "kosdaq"),
            ("data-tab-id=\"comparison\"", "comparison"),
            ("data-tab-id=\"theme\"", "theme"),
            ("data-tab-id=\"overseasStocks\"", "overseas-stocks"),
            ("data-tab-id=\"expert\"", "expert"),
            ("data-tab-id=\"tradingHalt\"", "trading-halt"),
            ("id=\"workspace-settings-tab\"", "settings")
        };

        foreach (var (anchor, icon) in mappings)
        {
            var button = Slice(Markup, anchor, "</button>");
            Assert.Contains($"data-icon=\"{icon}\"", button, StringComparison.Ordinal);
            Assert.Contains(
                $"href=\"#workspace-icon-{icon}\"",
                button,
                StringComparison.Ordinal);
            Assert.DoesNotContain("<img", button, StringComparison.OrdinalIgnoreCase);
        }

        var sprite = Slice(
            Markup,
            "<svg class=\"workspace-icon-sprite\"",
            "</svg>");
        Assert.DoesNotContain("<text", sprite, StringComparison.OrdinalIgnoreCase);
        Assert.DoesNotContain("http://", sprite, StringComparison.OrdinalIgnoreCase);
        Assert.DoesNotContain("https://", sprite, StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public void KospiAndKosdaqUseDistinctFontIndependentMonograms()
    {
        var kospi = Slice(
            Markup,
            "<symbol id=\"workspace-icon-kospi\"",
            "</symbol>");
        var kosdaq = Slice(
            Markup,
            "<symbol id=\"workspace-icon-kosdaq\"",
            "</symbol>");

        Assert.Contains(
            "M4.5 18V6h4a4 4 0 0 1 0 8h-4",
            kospi,
            StringComparison.Ordinal);
        Assert.Contains(
            "M15 6h4M17 6v12M15 18h4",
            kospi,
            StringComparison.Ordinal);
        Assert.Contains(
            "M2.3 18V6h1.6c2.8 0 4.3 2.2 4.3 6",
            kosdaq,
            StringComparison.Ordinal);
        Assert.Contains(
            "M9 18l2.5-12H13l2.5 12M10 14h4.5",
            kosdaq,
            StringComparison.Ordinal);
        Assert.Contains(
            "<ellipse cx=\"19.2\" cy=\"12\" rx=\"3.1\" ry=\"6\" stroke-width=\"1.65\">",
            kosdaq,
            StringComparison.Ordinal);
        Assert.Contains("m19.5 14.5 3.2 4", kosdaq, StringComparison.Ordinal);
        Assert.DoesNotContain("<text", kospi + kosdaq, StringComparison.OrdinalIgnoreCase);
        Assert.NotEqual(kospi, kosdaq);
    }

    [Fact]
    public void HeadingClonesTheSelectedIconAndThemeStylingStaysVectorNative()
    {
        Assert.Contains(
            "const titleIcon = document.querySelector(\".workspace-heading-icon\")",
            Navigation,
            StringComparison.Ordinal);
        var sync = Slice(
            Navigation,
            "function syncHeadingIcon(button)",
            "function refreshCurrentMenu()");
        Assert.Contains("source.cloneNode(true)", sync, StringComparison.Ordinal);
        Assert.Contains("titleIcon.replaceChildren(next)", sync, StringComparison.Ordinal);

        var workspace = Slice(
            Navigation,
            "function setWorkspace(name)",
            "function applyStartPreference(mode)");
        AssertInOrder(
            workspace,
            "activeWorkspace = name;",
            "refreshCurrentMenu();",
            "syncHeadingIcon(selectedButton);",
            "title.textContent");

        Assert.Contains(
            ".workspace-icon-sprite",
            Styles,
            StringComparison.Ordinal);
        Assert.Contains(
            ".workspace-nav-icon { shape-rendering: geometricPrecision; }",
            Styles,
            StringComparison.Ordinal);
        Assert.Contains(
            ".workspace-nav-toggle svg, .workspace-nav-item svg",
            Styles,
            StringComparison.Ordinal);
        Assert.Contains("stroke: currentColor;", Styles, StringComparison.Ordinal);
    }

    [Fact]
    public void PackageSmokesProveIconsArePaintedMappedAndCentered()
    {
        var mappings = new (string TabId, string Icon)[]
        {
            ("overseas", "overseas"),
            ("exchange", "exchange"),
            ("index", "index"),
            ("kospiIndustry", "kospi"),
            ("kosdaqIndustry", "kosdaq"),
            ("comparison", "comparison"),
            ("theme", "theme"),
            ("overseasStocks", "overseas-stocks"),
            ("expert", "expert"),
            ("tradingHalt", "trading-halt")
        };

        foreach (var harness in new[] { PackageInputSmoke, UiOnlySmoke })
        {
            foreach (var (tabId, icon) in mappings)
            {
                Assert.Contains(
                    $"{tabId}: \"{icon}\"",
                    harness,
                    StringComparison.Ordinal);
            }

            Assert.Contains("svg?.getBBox?.()", harness, StringComparison.Ordinal);
            Assert.Contains("reference:", harness, StringComparison.Ordinal);
            Assert.Contains("Number.parseFloat(style.opacity) > 0",
                harness,
                StringComparison.Ordinal);
            Assert.Contains("style.stroke !== \"none\" || style.fill !== \"none\"",
                harness,
                StringComparison.Ordinal);
            Assert.Contains("rendered: Boolean(", harness, StringComparison.Ordinal);
            Assert.Contains("expectedHeadingIcon", harness, StringComparison.Ordinal);
        }

        Assert.Contains("navigationItems:", PackageInputSmoke, StringComparison.Ordinal);
        Assert.Contains("iconCenterOffset:", PackageInputSmoke, StringComparison.Ordinal);
        Assert.Contains("iconCenterOffsetY:", PackageInputSmoke, StringComparison.Ordinal);
        Assert.Contains("item.iconCenterOffset > 1.5", PackageInputSmoke, StringComparison.Ordinal);
        Assert.Contains("item.iconCenterOffsetY > 1.5",
            PackageInputSmoke,
            StringComparison.Ordinal);
        Assert.Contains("collapsedIconCenterOffsets:", PackageInputSmoke, StringComparison.Ordinal);
        Assert.Contains("tab.icon.key === baseline.marketTabs[index].icon.key",
            PackageInputSmoke,
            StringComparison.Ordinal);

        Assert.Contains("layout.stockTabIcon.rendered === true",
            UiOnlySmoke,
            StringComparison.Ordinal);
        Assert.Contains("layout.settingsTabIcon.rendered === true",
            UiOnlySmoke,
            StringComparison.Ordinal);
        Assert.Contains("tab.icon.rendered === true",
            UiOnlySmoke,
            StringComparison.Ordinal);
    }

    private static string Slice(string value, string start, string end)
    {
        var startIndex = value.IndexOf(start, StringComparison.Ordinal);
        Assert.True(startIndex >= 0, $"Required marker was not found: {start}");
        var endIndex = value.IndexOf(
            end,
            startIndex + start.Length,
            StringComparison.Ordinal);
        Assert.True(endIndex > startIndex, $"Required marker was not found: {end}");
        return value[startIndex..(endIndex + end.Length)];
    }

    private static void AssertInOrder(string value, params string[] needles)
    {
        var previous = -1;
        foreach (var needle in needles)
        {
            var current = value.IndexOf(needle, previous + 1, StringComparison.Ordinal);
            Assert.True(current > previous, $"Required ordered marker was not found: {needle}");
            previous = current;
        }
    }

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