using System.Text.RegularExpressions;

namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests;

public sealed class LegacyIndustryListWebContractTests
{
    private static readonly string RepositoryRoot = FindRepositoryRoot();
    private static readonly string App = File.ReadAllText(Path.Combine(
        RepositoryRoot,
        "src",
        "MBN_STOCK_WEBVIEW.LegacyParityApp",
        "Web",
        "app.js"));

    [Fact]
    public void Industry_list_is_one_tab_stop_and_rows_are_programmatic_descendants()
    {
        var renderIndustry = Extract(
            App,
            "  function renderIndustry(state) {",
            "  function renderOverseas(state) {");
        var listFactory = Extract(
            renderIndustry,
            "    const list = document.createElement(\"div\");",
            "    const comparisonTitle = document.createElement(\"strong\");");

        Assert.Contains("list.setAttribute(\"role\", \"listbox\");", listFactory,
            StringComparison.Ordinal);
        Assert.Contains("list.tabIndex = 0;", listFactory, StringComparison.Ordinal);
        Assert.Contains("industry.industries.forEach(function (row) {", listFactory,
            StringComparison.Ordinal);
        Assert.Contains("button.dataset.industryIndex = String(row.index);", listFactory,
            StringComparison.Ordinal);
        Assert.Contains("button.tabIndex = -1;", listFactory, StringComparison.Ordinal);
        Assert.Contains("button.setAttribute(\"role\", \"option\");", listFactory,
            StringComparison.Ordinal);
        Assert.Single(Regex.Matches(listFactory, @"\.tabIndex\s*=\s*0;").Cast<Match>());
        Assert.DoesNotContain("button.tabIndex = 0;", listFactory, StringComparison.Ordinal);
    }

    [Fact]
    public void Industry_list_handles_arrow_home_and_end_navigation()
    {
        var keyHandler = Extract(
            App,
            "  categoryTree.addEventListener(\"keydown\", function (event) {",
            "  window.addEventListener(\"pointerup\", function () {");

        Assert.Contains("const industryList = event.target.closest(\".industry-list\");",
            keyHandler, StringComparison.Ordinal);
        Assert.Contains(
            "[\"ArrowUp\", \"ArrowDown\", \"Home\", \"End\"].includes(event.key)",
            keyHandler,
            StringComparison.Ordinal);
        Assert.Contains("event.preventDefault();", keyHandler, StringComparison.Ordinal);
        Assert.Contains("if (event.key === \"Home\") nextIndex = 0;", keyHandler,
            StringComparison.Ordinal);
        Assert.Contains("else if (event.key === \"End\") nextIndex = rows.length - 1;",
            keyHandler, StringComparison.Ordinal);
        Assert.Contains(
            "else if (event.key === \"ArrowUp\") nextIndex = Math.max(0, selectedIndex - 1);",
            keyHandler,
            StringComparison.Ordinal);
        Assert.Contains(
            "else nextIndex = Math.min(rows.length - 1, selectedIndex + 1);",
            keyHandler,
            StringComparison.Ordinal);
        Assert.Contains("selectIndustryRow(nextRow);", keyHandler, StringComparison.Ordinal);
        Assert.Contains("nextRow.scrollIntoView({ block: \"nearest\", inline: \"nearest\" });",
            keyHandler, StringComparison.Ordinal);
    }

    [Fact]
    public void Selection_only_state_receipt_updates_rows_without_replacing_the_workspace()
    {
        var renderIndustry = Extract(
            App,
            "  function renderIndustry(state) {",
            "  function renderOverseas(state) {");
        var structureIdentity = Extract(
            renderIndustry,
            "    const structureKey = JSON.stringify({",
            "    const existingWorkspace = categoryTree.querySelector(\".industry-workspace\");");
        var selectionOnlyReceipt = Extract(
            renderIndustry,
            "    if (existingWorkspace && structureKey === industryRenderStructureKey) {",
            "    const previousList = categoryTree.querySelector(\".industry-list\");");

        Assert.Contains("market: industry.market", structureIdentity, StringComparison.Ordinal);
        Assert.Contains("rows: industry.industries.map", structureIdentity,
            StringComparison.Ordinal);
        Assert.Contains("actions: industry.actions.map", structureIdentity,
            StringComparison.Ordinal);
        Assert.DoesNotContain("selectedIndex", structureIdentity, StringComparison.Ordinal);

        Assert.Contains("existingWorkspace.querySelectorAll(\"button[data-industry-index]\")",
            selectionOnlyReceipt, StringComparison.Ordinal);
        Assert.Contains(
            "const selected = Number(row.dataset.industryIndex) === industry.selectedIndex;",
            selectionOnlyReceipt,
            StringComparison.Ordinal);
        Assert.Contains("row.classList.toggle(\"selected\", selected);", selectionOnlyReceipt,
            StringComparison.Ordinal);
        Assert.Contains(
            "row.setAttribute(\"aria-selected\", selected ? \"true\" : \"false\");",
            selectionOnlyReceipt,
            StringComparison.Ordinal);
        Assert.Contains("return;", selectionOnlyReceipt, StringComparison.Ordinal);
        Assert.DoesNotContain("replaceChildren", selectionOnlyReceipt, StringComparison.Ordinal);
        Assert.DoesNotContain("document.createElement", selectionOnlyReceipt,
            StringComparison.Ordinal);
    }

    [Fact]
    public void Industry_list_is_appended_before_comparison_label_and_slots()
    {
        var renderIndustry = Extract(
            App,
            "  function renderIndustry(state) {",
            "  function renderOverseas(state) {");

        Assert.Contains("comparisonTitle.textContent = \"업종비교\";", renderIndustry,
            StringComparison.Ordinal);
        Assert.Contains("selector.append(list, comparisonTitle, slots);", renderIndustry,
            StringComparison.Ordinal);
        Assert.DoesNotContain("selector.append(comparisonTitle, slots, list);", renderIndustry,
            StringComparison.Ordinal);
    }

    [Fact]
    public void Comparison_text_boxes_commit_closed_text_intents_without_replacing_focused_dom()
    {
        var renderIndustry = Extract(
            App,
            "  function renderIndustry(state) {",
            "  function renderOverseas(state) {");
        var changeHandler = Extract(
            App,
            "  categoryTree.addEventListener(\"change\", function (event) {",
            "  categoryTree.addEventListener(\"keydown\", function (event) {");

        Assert.Contains("first.dataset.industrySlot = \"first\";", renderIndustry,
            StringComparison.Ordinal);
        Assert.Contains("second.dataset.industrySlot = \"second\";", renderIndustry,
            StringComparison.Ordinal);
        Assert.Contains("first.maxLength = 128;", renderIndustry, StringComparison.Ordinal);
        Assert.Contains("second.maxLength = 128;", renderIndustry, StringComparison.Ordinal);
        Assert.DoesNotContain("first.readOnly = true;", renderIndustry,
            StringComparison.Ordinal);
        Assert.DoesNotContain("second.readOnly = true;", renderIndustry,
            StringComparison.Ordinal);
        Assert.DoesNotContain("firstName: industry.firstName", renderIndustry,
            StringComparison.Ordinal);
        Assert.DoesNotContain("secondName: industry.secondName", renderIndustry,
            StringComparison.Ordinal);
        Assert.Contains("document.activeElement !== firstInput", renderIndustry,
            StringComparison.Ordinal);
        Assert.Contains("document.activeElement !== secondInput", renderIndustry,
            StringComparison.Ordinal);
        Assert.Contains("send(\"set-industry-comparison-text\"", changeHandler,
            StringComparison.Ordinal);
        Assert.Contains("slot: event.target.dataset.industrySlot", changeHandler,
            StringComparison.Ordinal);
        Assert.Contains("value: event.target.value", changeHandler,
            StringComparison.Ordinal);
    }

    private static string Extract(string source, string startToken, string endToken)
    {
        var start = source.IndexOf(startToken, StringComparison.Ordinal);
        var end = source.IndexOf(endToken, start + startToken.Length, StringComparison.Ordinal);
        Assert.True(start >= 0, $"Start token was not found: {startToken}");
        Assert.True(end > start, $"End token was not found after start token: {endToken}");
        return source[start..end];
    }

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