namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests;

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

    [Fact]
    public void Theme_workspace_matches_original_vertical_visible_order_and_action_order()
    {
        var renderTheme = Extract(
            App,
            "  function renderTheme(state) {",
            "  function renderExpert(state) {");

        Assert.Contains("input.setAttribute(\"aria-label\", \"테마명\");", renderTheme,
            StringComparison.Ordinal);
        Assert.Contains("searchButton.textContent = \"테마검색\";", renderTheme,
            StringComparison.Ordinal);
        Assert.Contains("search.append(input, searchButton, addTheme, editTheme, deleteTheme);",
            renderTheme, StringComparison.Ordinal);
        Assert.Contains("resultsTitle.textContent = \"테마명\";", renderTheme,
            StringComparison.Ordinal);
        Assert.Contains("previewTitle.textContent = \"종목명\";", renderTheme,
            StringComparison.Ordinal);
        Assert.Contains("sortControls.className = \"theme-sort-controls\";", renderTheme,
            StringComparison.Ordinal);
        Assert.Contains("radio.type = \"radio\";", renderTheme,
            StringComparison.Ordinal);
        Assert.Contains(
            "workspace.append(search, resultsPanel, sortControls, actions, previewPanel);",
            renderTheme,
            StringComparison.Ordinal);
        Assert.DoesNotContain("theme-body", renderTheme, StringComparison.Ordinal);
        Assert.Contains(
            "grid-template-rows: auto minmax(72px, .8fr) auto auto minmax(72px, 1fr)",
            Styles,
            StringComparison.Ordinal);
        Assert.Contains("grid-template-columns: repeat(4, minmax(0, 1fr))", Styles,
            StringComparison.Ordinal);
        Assert.Contains(
            "[\"six-row-current\", 0],\n" +
            "      [\"twelve-row-current\", 1],\n" +
            "      [\"five-row-current\", 2],\n" +
            "      [\"five-row-expected\", 3]",
            renderTheme,
            StringComparison.Ordinal);
        Assert.Contains("[\"six-row-current\", \"6종목 현재가\"]", renderTheme,
            StringComparison.Ordinal);
        Assert.Contains("[\"twelve-row-current\", \"12종목 현재가\"]", renderTheme,
            StringComparison.Ordinal);
        Assert.Contains("[\"five-row-current\", \"현재가\"]", renderTheme,
            StringComparison.Ordinal);
        Assert.Contains("[\"five-row-expected\", \"예상체결가\"]", renderTheme,
            StringComparison.Ordinal);
    }

    [Fact]
    public void Main_theme_workspace_does_not_expose_the_non_original_NXT_session_selector()
    {
        var renderTheme = Extract(
            App,
            "  function renderTheme(state) {",
            "  function renderExpert(state) {");
        var clickDelegate = Extract(
            App,
            "  categoryTree.addEventListener(\"click\", function (event) {",
            "  categoryTree.addEventListener(\"change\", function (event) {");

        Assert.DoesNotContain("theme-nxt-session", App, StringComparison.Ordinal);
        Assert.DoesNotContain("themeSearchNxtSession", App, StringComparison.Ordinal);
        Assert.DoesNotContain("nxtSession", clickDelegate, StringComparison.Ordinal);
        Assert.Contains("send(\"search-themes\", { query: input ? input.value : \"\" });",
            clickDelegate, StringComparison.Ordinal);
    }

    [Fact]
    public void Selection_receipt_updates_existing_theme_rows_without_replacing_click_target_or_focus()
    {
        var renderTheme = Extract(
            App,
            "  function renderTheme(state) {",
            "  function renderExpert(state) {");
        var structureIdentity = Extract(
            renderTheme,
            "    const structureKey = JSON.stringify({",
            "    const existingWorkspace = categoryTree.querySelector(\".theme-workspace\");");
        var selectionReceipt = Extract(
            renderTheme,
            "    if (existingWorkspace && structureKey === themeRenderStructureKey) {",
            "    const workspace = document.createElement(\"div\");");
        var updater = Extract(
            App,
            "  function updateThemeWorkspace(workspace, theme) {",
            "  function renderTheme(state) {");

        Assert.DoesNotContain("selectedResultId", structureIdentity, StringComparison.Ordinal);
        Assert.DoesNotContain("selectedTheme", structureIdentity, StringComparison.Ordinal);
        Assert.DoesNotContain("previewItems", structureIdentity, StringComparison.Ordinal);
        Assert.DoesNotContain("isAvailable", structureIdentity, StringComparison.Ordinal);
        Assert.Contains("updateThemeWorkspace(existingWorkspace, theme);", selectionReceipt,
            StringComparison.Ordinal);
        Assert.Contains("return;", selectionReceipt, StringComparison.Ordinal);
        Assert.DoesNotContain("replaceChildren", selectionReceipt, StringComparison.Ordinal);
        Assert.Contains("syncSearchDraft(input, theme.query)", updater,
            StringComparison.Ordinal);
        Assert.Contains("button.classList.toggle(\"selected\", selected);", updater,
            StringComparison.Ordinal);
        Assert.Contains("button.setAttribute(\"aria-selected\"", updater,
            StringComparison.Ordinal);
        Assert.DoesNotContain("results.replaceChildren", updater, StringComparison.Ordinal);
        Assert.DoesNotContain("categoryTree.replaceChildren", updater, StringComparison.Ordinal);
    }

    [Fact]
    public void NXT_expected_price_action_stays_clickable_so_native_can_show_the_legacy_warning()
    {
        var clickability = Extract(
            App,
            "  function themeActionIsClickable(action) {",
            "  function updateThemeWorkspace(workspace, theme) {");
        var updater = Extract(
            App,
            "  function updateThemeWorkspace(workspace, theme) {",
            "  function renderTheme(state) {");
        var clickDelegate = Extract(
            App,
            "  categoryTree.addEventListener(\"click\", function (event) {",
            "  categoryTree.addEventListener(\"change\", function (event) {");

        Assert.Contains("action.actionId === \"five-row-expected\"", clickability,
            StringComparison.Ordinal);
        Assert.Contains("action.unavailableReason === \"expected-theme-is-krx-only\"",
            clickability, StringComparison.Ordinal);
        Assert.Contains("button.disabled = !themeActionIsClickable(action);", updater,
            StringComparison.Ordinal);
        Assert.Contains("? \"NXT는 데이터가 없습니다.\"", updater,
            StringComparison.Ordinal);
        Assert.Contains("send(\"activate-theme-action\"", clickDelegate,
            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.");
    }
}
