using System.Text.RegularExpressions;

namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests;

public sealed class LegacyOperatorCatalogParityWebTests
{
    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 App = File.ReadAllText(Path.Combine(WebRoot, "app.js"));
    private static readonly string Styles = File.ReadAllText(Path.Combine(WebRoot, "styles.css"));
    private static readonly string MainWindow = File.ReadAllText(Path.Combine(
        RepositoryRoot,
        "src",
        "MBN_STOCK_WEBVIEW.LegacyParityApp",
        "MainWindow.xaml.cs"));
    private static readonly string Playout = File.ReadAllText(Path.Combine(
        RepositoryRoot,
        "src",
        "MBN_STOCK_WEBVIEW.LegacyParityApp",
        "MainWindow.Playout.cs"));

    [Fact]
    public void Theme_and_expert_workspaces_expose_the_original_management_entry_points()
    {
        foreach (var label in new[]
                 {
                     "테마검색", "테마추가", "테마편집", "테마삭제",
                     "전문가 추가", "전문가 삭제", "추천행 편집·저장·삭제"
                 })
        {
            Assert.Contains(label, App, StringComparison.Ordinal);
        }

        foreach (var intent in new[]
                 {
                     "begin-uc4-theme-catalog",
                     "edit-uc4-selected-theme",
                     "delete-uc4-selected-theme",
                     "begin-uc6-expert-catalog",
                     "edit-uc6-selected-expert",
                     "delete-uc6-selected-expert"
                 })
        {
            Assert.Contains($"send(\"{intent}\", {{}})", App, StringComparison.Ordinal);
        }

        Assert.DoesNotContain("ThemeA 추가·편집·삭제", App, StringComparison.Ordinal);
        Assert.DoesNotContain("EList 추가·편집·삭제", App, StringComparison.Ordinal);
        foreach (var id in new[]
                 {
                     "uc4-theme-add", "uc4-theme-edit", "uc4-theme-delete",
                     "uc6-expert-add", "uc6-expert-edit", "uc6-expert-delete"
                 })
        {
            Assert.Contains($".id = \"{id}\"", App, StringComparison.Ordinal);
        }
    }

    [Fact]
    public void Modal_contains_search_create_edit_stock_and_reorder_controls_without_child_delete()
    {
        foreach (var id in new[]
                 {
                     "operator-catalog-modal",
                     "operator-catalog-query",
                     "operator-catalog-new",
                     "operator-catalog-results",
                     "operator-catalog-editor-market-field",
                     "operator-catalog-editor-market",
                     "operator-catalog-name",
                     "operator-catalog-name-check",
                     "operator-catalog-name-status",
                     "operator-catalog-stock-query",
                     "operator-catalog-stock-results",
                     "operator-catalog-add-stock",
                     "operator-catalog-draft-rows",
                     "operator-catalog-save",
                     "operator-catalog-delete-all"
                 })
        {
            Assert.Contains($"id=\"{id}\"", Markup, StringComparison.Ordinal);
        }

        Assert.Contains("operator-catalog-draft-row", Styles, StringComparison.Ordinal);
        Assert.Contains(">중복확인</button>", Markup, StringComparison.Ordinal);
        Assert.Contains("operator-catalog-name-field small.available", Styles,
            StringComparison.Ordinal);
        Assert.Contains("operator-catalog-backdrop[hidden]", Styles, StringComparison.Ordinal);
        Assert.Contains("operatorCatalogStockResults.addEventListener(\"dblclick\"", App,
            StringComparison.Ordinal);
        Assert.Contains("function reconcileOperatorCatalogStockResults(rows)", App,
            StringComparison.Ordinal);
        Assert.Contains("operatorCatalogStockResults.insertBefore(button, buttonAtPosition)", App,
            StringComparison.Ordinal);
        Assert.DoesNotContain("operatorCatalogStockClickTimer", App, StringComparison.Ordinal);
        Assert.DoesNotContain("window.setTimeout(function () {\n" +
            "      operatorCatalogStockClickTimer", App, StringComparison.Ordinal);
        Assert.Contains("dataset.operatorCatalogBuyAmountRowId = row.rowId", App,
            StringComparison.Ordinal);
        Assert.Contains("dataset.operatorCatalogRowId = row.rowId", App,
            StringComparison.Ordinal);
        Assert.Contains("event.dataTransfer.setData(operatorCatalogDragMime, sourceRowId)", App,
            StringComparison.Ordinal);
        Assert.Contains(".operator-catalog-draft-row.drag-before", Styles,
            StringComparison.Ordinal);
        Assert.Contains("remove.textContent = \"Delete\"", App, StringComparison.Ordinal);
        Assert.Contains("up.textContent = \"Up\"", App, StringComparison.Ordinal);
        Assert.Contains("down.textContent = \"Down\"", App, StringComparison.Ordinal);
        Assert.Contains("operatorCatalogStockSearchLine.hidden = !showCatalogRows", App,
            StringComparison.Ordinal);
        Assert.Contains("catalog.mode === \"edit\"", App, StringComparison.Ordinal);
        Assert.DoesNotContain("id=\"operator-catalog-delete\"", Markup,
            StringComparison.Ordinal);
        Assert.Contains("send(\"clear-operator-catalog-draft-rows\", {})", App,
            StringComparison.Ordinal);
        Assert.Contains("const isActive = row.rowId === catalog.activeDraftRowId", App,
            StringComparison.Ordinal);
        Assert.Contains("send(\"select-operator-catalog-draft-row\", { rowId: row.rowId })", App,
            StringComparison.Ordinal);
        Assert.Contains("send(\"remove-active-operator-catalog-draft-row\", {})", App,
            StringComparison.Ordinal);
    }

    [Fact]
    public void ThemeA_create_editor_can_change_market_without_discarding_the_typed_name()
    {
        Assert.Contains(
            "const canChangeCreateThemeMarket = isTheme && catalog.mode === \"create\";",
            App,
            StringComparison.Ordinal);
        Assert.Contains(
            "operatorCatalogEditorMarketField.hidden = !canChangeCreateThemeMarket;",
            App,
            StringComparison.Ordinal);
        Assert.Contains(
            "operatorCatalogEditorMarket.disabled = !canChangeCreateThemeMarket;",
            App,
            StringComparison.Ordinal);
        Assert.Contains(
            "root.querySelectorAll(\"button, input, select, textarea\")",
            App,
            StringComparison.Ordinal);
        Assert.Contains(
            "operatorCatalogEditorMarket.value = catalog.newThemeMarket === \"nxt\" ? \"nxt\" : \"krx\";",
            App,
            StringComparison.Ordinal);

        var start = App.IndexOf(
            "operatorCatalogEditorMarket.addEventListener(\"change\"",
            StringComparison.Ordinal);
        var end = App.IndexOf(
            "operatorCatalogResults.addEventListener",
            start,
            StringComparison.Ordinal);
        Assert.True(start >= 0 && end > start);
        var marketChange = App[start..end];
        Assert.Contains("operatorCatalogState.mode !== \"create\"", marketChange,
            StringComparison.Ordinal);
        Assert.Contains("operatorCatalogNameDraft = operatorCatalogName.value;", marketChange,
            StringComparison.Ordinal);
        Assert.Contains("send(\"change-create-theme-market\", {", marketChange,
            StringComparison.Ordinal);
        Assert.Contains("editorName: operatorCatalogName.value.trim()", marketChange,
            StringComparison.Ordinal);
        Assert.DoesNotContain("operatorCatalogDraftKey = \"\"", marketChange,
            StringComparison.Ordinal);
        Assert.DoesNotContain("operatorCatalogNameDraft = \"\"", marketChange,
            StringComparison.Ordinal);
        Assert.DoesNotContain("begin-create-theme-catalog", marketChange,
            StringComparison.Ordinal);

        var newButtonStart = App.IndexOf(
            "operatorCatalogNew.addEventListener(\"click\"",
            StringComparison.Ordinal);
        Assert.True(newButtonStart >= 0 && start > newButtonStart);
        var listCreate = App[newButtonStart..start];
        Assert.Contains("send(\"begin-create-theme-catalog\", {", listCreate,
            StringComparison.Ordinal);
        Assert.Contains("market: operatorCatalogNewMarket.value", listCreate,
            StringComparison.Ordinal);
    }

    [Fact]
    public void ThemeA_and_UC6_stock_picker_send_blank_button_and_Enter_searches_without_breaking_IME()
    {
        var start = App.IndexOf(
            "function searchOperatorCatalogStocks()",
            StringComparison.Ordinal);
        var end = App.IndexOf(
            "operatorCatalogStockResults.addEventListener",
            start,
            StringComparison.Ordinal);
        Assert.True(start >= 0 && end > start);
        var stockPicker = App[start..end];

        Assert.Contains(
            "const query = operatorCatalogStockQuery.value.trim();",
            stockPicker,
            StringComparison.Ordinal);
        Assert.Contains(
            "send(\"search-operator-catalog-stocks\", { query: query });",
            stockPicker,
            StringComparison.Ordinal);
        Assert.DoesNotContain("if (query)", stockPicker, StringComparison.Ordinal);
        Assert.Contains(
            "operatorCatalogStockSearch.addEventListener(\"click\", searchOperatorCatalogStocks);",
            stockPicker,
            StringComparison.Ordinal);
        Assert.Contains(
            "event.key === \"Enter\" && !isImeComposing(event)",
            stockPicker,
            StringComparison.Ordinal);
        Assert.Contains("searchOperatorCatalogStocks();", stockPicker, StringComparison.Ordinal);
    }

    [Fact]
    public void Web_sends_closed_intents_and_never_owns_catalog_DB_identity()
    {
        foreach (var intent in new[]
                 {
                     "open-operator-catalog",
                     "search-operator-catalog",
                     "select-operator-catalog-result",
                     "begin-create-theme-catalog",
                     "begin-create-expert-catalog",
                     "search-operator-catalog-stocks",
                     "select-operator-catalog-stock",
                     "add-operator-catalog-stock",
                     "activate-operator-catalog-stock",
                     "reorder-operator-catalog-draft-row",
                     "move-operator-catalog-draft-row",
                     "remove-operator-catalog-draft-row",
                     "select-operator-catalog-draft-row",
                     "remove-active-operator-catalog-draft-row",
                     "clear-operator-catalog-draft-rows",
                     "set-operator-catalog-draft-buy-amount",
                     "check-operator-catalog-theme-name",
                     "save-operator-catalog",
                     "confirm-native-dialog",
                     "cancel-native-dialog"
                 })
        {
            Assert.Contains($"\"{intent}\"", App, StringComparison.Ordinal);
        }

        var start = App.IndexOf("function renderOperatorCatalog", StringComparison.Ordinal);
        var end = App.IndexOf("function renderDialog", start, StringComparison.Ordinal);
        Assert.True(start >= 0 && end > start);
        var catalogSection = App[start..end];
        Assert.DoesNotMatch(
            new Regex("(?:themeCode|expertCode|stockCode|itemCode|expectedIdentity|SB_LIST|EXPERT_LIST)"),
            catalogSection);
        Assert.DoesNotContain("send(\"save-operator-catalog\"", App[..start], StringComparison.Ordinal);
        Assert.Contains(
            "send(\"check-operator-catalog-theme-name\", { name: name });",
            App,
            StringComparison.Ordinal);
        Assert.Contains("send(\"close-operator-catalog\", {})", App,
            StringComparison.Ordinal);
        Assert.Contains(
            "const preserveStoredThemeName = operatorCatalogState &&",
            App,
            StringComparison.Ordinal);
        Assert.Contains(
            "const name = preserveStoredThemeName ? rawName : canonicalName;",
            App,
            StringComparison.Ordinal);
        Assert.Contains("rowId: remove.dataset.operatorCatalogRemoveRowId", App,
            StringComparison.Ordinal);
        Assert.DoesNotContain(
            "operatorCatalogCancel.addEventListener(\"click\", function () {\n" +
            "    operatorCatalogDraftKey = \"\";\n" +
            "    searchOperatorCatalog();",
            App,
            StringComparison.Ordinal);
        Assert.DoesNotContain(
            "send(\"check-operator-catalog-theme-name\", { name: name,",
            App,
            StringComparison.Ordinal);
        Assert.DoesNotContain("delete-operator-catalog", App, StringComparison.Ordinal);
        Assert.DoesNotContain("window.confirm(\"선택한 테마를 삭제하겠습니까?\")", App,
            StringComparison.Ordinal);
        Assert.DoesNotContain("window.confirm(\"선택한 전문가를 삭제하겠습니까?\")", App,
            StringComparison.Ordinal);
        Assert.DoesNotContain("window.confirm(\"모두 삭제하겠습니까?\")", App,
            StringComparison.Ordinal);
        Assert.DoesNotContain(
            "window.confirm(\"선택한 ThemeA/EList 항목을 DB에서 삭제하시겠습니까?\")",
            App,
            StringComparison.Ordinal);
    }

    [Fact]
    public void Native_dialog_renders_caption_and_confirmation_and_sends_only_empty_decisions()
    {
        foreach (var id in new[]
                 {
                     "legacy-dialog-caption", "legacy-dialog-message",
                     "legacy-dialog-ok", "legacy-dialog-no"
                 })
        {
            Assert.Contains($"id=\"{id}\"", Markup, StringComparison.Ordinal);
        }

        Assert.Contains("state.dialog.isConfirmation === true", App, StringComparison.Ordinal);
        Assert.Contains("dialogCaption.textContent = state.dialog.caption || \"MmoneyCoder\"", App,
            StringComparison.Ordinal);
        Assert.Contains("dialogOk.textContent = legacyDialogIsConfirmation ? \"예\" : \"확인\"", App,
            StringComparison.Ordinal);
        Assert.Contains("dialogNo.hidden = !legacyDialogIsConfirmation", App,
            StringComparison.Ordinal);
        Assert.Contains("? \"confirm-native-dialog\"", App,
            StringComparison.Ordinal);
        Assert.Contains("send(\"cancel-native-dialog\", {});", App,
            StringComparison.Ordinal);

        var confirmStart = App.IndexOf(
            "dialogOk.addEventListener(\"click\"",
            StringComparison.Ordinal);
        var confirmEnd = App.IndexOf("if (webview)", confirmStart, StringComparison.Ordinal);
        Assert.True(confirmStart >= 0 && confirmEnd > confirmStart);
        var decisionHandlers = App[confirmStart..confirmEnd];
        Assert.DoesNotMatch(
            new Regex("(?:themeCode|expertCode|stockCode|itemCode|rowId|expectedIdentity)"),
            decisionHandlers);
        Assert.Contains(": \"dismiss-dialog\", {});", decisionHandlers,
            StringComparison.Ordinal);
    }

    [Fact]
    public void MainWindow_composes_the_non_retrying_real_services_and_dispatches_every_intent()
    {
        Assert.Contains("new OperatorCatalogMutationExecutor(", MainWindow, StringComparison.Ordinal);
        Assert.Contains("new LegacyThemeCatalogPersistenceService(", MainWindow, StringComparison.Ordinal);
        Assert.Contains("new LegacyExpertCatalogPersistenceService(", MainWindow, StringComparison.Ordinal);
        Assert.Contains(
            "new LegacyStockMasterIdentityValidationService(",
            MainWindow,
            StringComparison.Ordinal);
        Assert.Contains(
            "new LegacyOperatorCatalogSchemaValidationService(",
            MainWindow,
            StringComparison.Ordinal);
        Assert.Contains(
            "new LegacyOperatorCatalogStockSearchService(sharedStockSearchService)",
            MainWindow,
            StringComparison.Ordinal);
        Assert.Contains("LegacyOpenOperatorCatalogIntent", MainWindow, StringComparison.Ordinal);
        Assert.Contains("LegacyBeginUc4ThemeCatalogIntent", MainWindow, StringComparison.Ordinal);
        Assert.Contains("LegacyEditUc4SelectedThemeIntent", MainWindow, StringComparison.Ordinal);
        Assert.Contains("LegacyDeleteUc4SelectedThemeIntent", MainWindow, StringComparison.Ordinal);
        Assert.Contains("LegacyBeginUc6ExpertCatalogIntent", MainWindow, StringComparison.Ordinal);
        Assert.Contains("LegacyEditUc6SelectedExpertIntent", MainWindow, StringComparison.Ordinal);
        Assert.Contains("LegacyDeleteUc6SelectedExpertIntent", MainWindow, StringComparison.Ordinal);
        Assert.Contains("LegacyChangeCreateThemeMarketIntent", MainWindow,
            StringComparison.Ordinal);
        Assert.Contains("LegacyCheckOperatorCatalogThemeNameIntent", MainWindow,
            StringComparison.Ordinal);
        Assert.Contains("LegacySaveOperatorCatalogIntent", MainWindow, StringComparison.Ordinal);
        Assert.Contains("LegacySelectOperatorCatalogDraftRowIntent", MainWindow,
            StringComparison.Ordinal);
        var queuedSelectionStart = MainWindow.IndexOf(
            "private static bool IsQueuedLocalSelectionIntent", StringComparison.Ordinal);
        var queuedSelectionEnd = MainWindow.IndexOf(
            "private static bool IsOperatorSettingsIntent", queuedSelectionStart,
            StringComparison.Ordinal);
        Assert.True(queuedSelectionStart >= 0 && queuedSelectionEnd > queuedSelectionStart);
        var queuedSelections = MainWindow[queuedSelectionStart..queuedSelectionEnd];
        Assert.Contains("LegacySelectOperatorCatalogStockIntent", queuedSelections,
            StringComparison.Ordinal);
        Assert.Contains("LegacyActivateOperatorCatalogStockIntent", queuedSelections,
            StringComparison.Ordinal);
        Assert.Contains("LegacyRemoveActiveOperatorCatalogDraftRowIntent", MainWindow,
            StringComparison.Ordinal);
        Assert.Contains("LegacyConfirmNativeDialogIntent", MainWindow,
            StringComparison.Ordinal);
        Assert.Contains("LegacyCancelNativeDialogIntent", MainWindow,
            StringComparison.Ordinal);
        Assert.DoesNotContain("LegacyDeleteOperatorCatalogIntent", MainWindow,
            StringComparison.Ordinal);
        Assert.Contains("_intentGate.WaitAsync(", MainWindow, StringComparison.Ordinal);
    }

    [Fact]
    public void Playout_guard_blocks_catalog_draft_and_DB_mutations_without_weakening_existing_guards()
    {
        foreach (var intent in new[]
                 {
                     "LegacyBeginCreateThemeCatalogIntent",
                     "LegacyChangeCreateThemeMarketIntent",
                     "LegacyBeginCreateExpertCatalogIntent",
                     "LegacyBeginUc4ThemeCatalogIntent",
                     "LegacyEditUc4SelectedThemeIntent",
                     "LegacyDeleteUc4SelectedThemeIntent",
                     "LegacyBeginUc6ExpertCatalogIntent",
                     "LegacyEditUc6SelectedExpertIntent",
                     "LegacyDeleteUc6SelectedExpertIntent",
                     "LegacyAddOperatorCatalogStockIntent",
                     "LegacyActivateOperatorCatalogStockIntent",
                     "LegacyReorderOperatorCatalogDraftRowIntent",
                     "LegacyMoveOperatorCatalogDraftRowIntent",
                     "LegacyRemoveOperatorCatalogDraftRowIntent",
                     "LegacyRemoveActiveOperatorCatalogDraftRowIntent",
                     "LegacyClearOperatorCatalogDraftRowsIntent",
                     "LegacySetOperatorCatalogDraftBuyAmountIntent",
                     "LegacySaveOperatorCatalogIntent",
                     "LegacyConfirmNativeDialogIntent",
                     "LegacyActivateThemeActionIntent",
                     "LegacyActivateExpertActionIntent"
                 })
        {
            Assert.Contains(intent, Playout, StringComparison.Ordinal);
        }

        Assert.Contains("snapshot.OperatorCatalog?.WritesQuarantined", Playout, StringComparison.Ordinal);
        Assert.Contains("status.IsPlayCompletionPending", Playout, StringComparison.Ordinal);
        Assert.Contains("workflow.PreparedCutCode", Playout, StringComparison.Ordinal);
        Assert.Contains("workflow.OnAirCutCode", Playout, StringComparison.Ordinal);
    }

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