using System.Text.RegularExpressions;

namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests;

public sealed class LegacyWebContractTests
{
    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 Markup = File.ReadAllText(Path.Combine(WebRoot, "index.html"));
    private static readonly string Styles = File.ReadAllText(Path.Combine(WebRoot, "styles.css"));

    [Fact]
    public void ShellExposesOriginalStockCutPlaylistAndPlayoutControls()
    {
        foreach (var id in new[]
                 {
                     "stock-search",
                     "stock-search-button",
                     "stock-results",
                     "market-tabs",
                     "category-tree",
                     "catalog-expand-all",
                     "moving-average-5",
                     "moving-average-20",
                     "cut-list",
                     "cut-drag-feedback",
                     "playlist-drop-zone",
                     "playlist-rows",
                     "playlist-move-up",
                     "playlist-move-down",
                     "playlist-delete-selected",
                     "playlist-delete-all",
                     "db-save-button",
                     "db-load-button",
                     "named-playlist-modal",
                     "named-playlist-definitions",
                     "named-playlist-new-title",
                     "named-playlist-create-button",
                     "named-playlist-delete-button",
                     "named-playlist-confirm-button",
                     "legacy-dialog"
                 })
        {
            Assert.Contains($"id=\"{id}\"", Markup, StringComparison.Ordinal);
        }

        Assert.Contains("TAKE IN", Markup, StringComparison.Ordinal);
        Assert.Contains("TAKE OUT", Markup, StringComparison.Ordinal);
        Assert.Contains("NEXT", Markup, StringComparison.Ordinal);
        Assert.Matches("class=\"connection-state\"[^>]*>미연결</div>", Markup);
        Assert.Matches(
            @"class=""[^""]*\bactive\b[^""]*""[^>]*data-tab-id=""overseas""[\s\S]*?<span class=""workspace-nav-label"">해외<\/span>[\s\S]*?<\/button>",
            Markup);
        Assert.Matches(
            @"data-tab-id=""tradingHalt""[\s\S]*?<span class=""workspace-nav-label"">거래정지<\/span>[\s\S]*?<\/button>",
            Markup);
    }

    [Fact]
    public void WebLayerDoesNotOwnSceneDatabaseIdentityOrPlaylistState()
    {
        Assert.DoesNotMatch(
            new Regex("(?:sceneCode|cutCode|stockCode|dataCode|builderKey)"),
            App);
        Assert.DoesNotMatch(
            new Regex("(?:localStorage|sessionStorage|playlist\\.push|fetch\\s*\\()"),
            App);
        Assert.DoesNotMatch(
            new Regex("\\b(?:5001|N5001|5006|5011|5086|8035|8061|8040|8046|8051|8056|8003|5037)\\b"),
            App);
    }

    [Fact]
    public void DoubleClickUsesPointerFactsInsteadOfBrowserPolicy()
    {
        Assert.Contains("cut-pointer-down", App, StringComparison.Ordinal);
        Assert.Contains("timestampMilliseconds", App, StringComparison.Ordinal);
        Assert.Contains("event.ctrlKey", App, StringComparison.Ordinal);
        Assert.Contains("event.shiftKey", App, StringComparison.Ordinal);
        Assert.Contains("allowAppend: !playlistMutationLocked", App,
            StringComparison.Ordinal);
        Assert.Contains("send(\"clear-cut-selection\", {})", App,
            StringComparison.Ordinal);
        Assert.Contains("event.target !== cutList", App, StringComparison.Ordinal);
        Assert.Contains("button.addEventListener(\"keydown\"", App,
            StringComparison.Ordinal);
        Assert.Contains("stockResults.querySelectorAll(\"button[data-result-index]\")", App,
            StringComparison.Ordinal);
        var cutFactory = App[
            App.IndexOf("function createCutElement", StringComparison.Ordinal)..App.IndexOf("function renderCuts", StringComparison.Ordinal)];
        Assert.DoesNotContain("dblclick", cutFactory, StringComparison.Ordinal);
    }

    [Fact]
    public void CutRowsAreReconciledInPlace()
    {
        Assert.Contains("dataset.physicalIndex", App, StringComparison.Ordinal);
        Assert.Contains("function createCutElement", App, StringComparison.Ordinal);
        Assert.DoesNotContain("cutList.replaceChildren", App, StringComparison.Ordinal);
    }

    [Fact]
    public void StockAndPlaylistRowsPreserveNativeControlFocus()
    {
        Assert.Contains("dataset.resultIndex", App, StringComparison.Ordinal);
        Assert.Contains("dataset.rowId", App, StringComparison.Ordinal);
        Assert.Contains("playlistPlayoutPresentation", App, StringComparison.Ordinal);
        Assert.Contains("playout.currentEntryId", App, StringComparison.Ordinal);
        Assert.Contains("playout.currentCueIndexZeroBased", App, StringComparison.Ordinal);
        Assert.Contains("presentation.pageText", App, StringComparison.Ordinal);
        Assert.Contains("aria-current", App, StringComparison.Ordinal);
        Assert.Contains("scrollIntoView({ block: \"nearest\", inline: \"nearest\" })", App,
            StringComparison.Ordinal);
        Assert.Contains(".playlist-data-row.on-air", Styles, StringComparison.Ordinal);
        Assert.Contains("#fdc8d2", Styles, StringComparison.OrdinalIgnoreCase);
        Assert.Contains(".playlist-data-row.on-air.selected", Styles, StringComparison.Ordinal);
        Assert.DoesNotContain("stockResults.replaceChildren", App, StringComparison.Ordinal);
        Assert.DoesNotContain("playlistRows.replaceChildren", App, StringComparison.Ordinal);
        var boundaryStart = App.IndexOf(
            "function selectPlaylistBoundary", StringComparison.Ordinal);
        var boundaryEnd = App.IndexOf(
            "function movePlaylistKeyboardSelection", boundaryStart, StringComparison.Ordinal);
        var boundaryHelper = App[boundaryStart..boundaryEnd];
        Assert.DoesNotContain(".focus(", boundaryHelper, StringComparison.Ordinal);
        Assert.Contains("scrollIntoView", boundaryHelper, StringComparison.Ordinal);
        var arrowEnd = App.IndexOf(
            "function clearDelayedClick", boundaryEnd, StringComparison.Ordinal);
        var arrowHelper = App[boundaryEnd..arrowEnd];
        Assert.Contains("next.focus({ preventScroll: true })", arrowHelper,
            StringComparison.Ordinal);
    }

    [Fact]
    public void PlaylistRowsSendSelectionAndMutationIntentsOnly()
    {
        foreach (var intent in new[]
                 {
                     "select-playlist-row",
                     "activate-playlist-row",
                     "set-playlist-enabled",
                     "reorder-playlist-rows",
                     "move-selected-playlist-rows",
                     "delete-selected-playlist-rows",
                     "delete-all-playlist-rows"
                 })
        {
            Assert.Contains($"send(\"{intent}\"", App, StringComparison.Ordinal);
        }

        Assert.Contains("control: event.ctrlKey", App, StringComparison.Ordinal);
        Assert.Contains("shift: event.shiftKey", App, StringComparison.Ordinal);
        Assert.Contains("event.key === \"Delete\"", App, StringComparison.Ordinal);
        Assert.Contains("row.isSelected", App, StringComparison.Ordinal);
        Assert.Contains("row.isActive === true", App, StringComparison.Ordinal);
        Assert.Contains("send(\"toggle-active-playlist-enabled\", {})", App,
            StringComparison.Ordinal);
        Assert.Contains("enabled.setAttribute(\"aria-disabled\", playlistStructureMutationLocked", App,
            StringComparison.Ordinal);
        Assert.Contains("interactionDisabled = playlistStructureMutationLocked", App,
            StringComparison.Ordinal);
        Assert.Contains("playlistEnableAll.disabled = playlistProgramEnableMutationLocked", App,
            StringComparison.Ordinal);
        Assert.Contains("playlistDeleteSelected.disabled = playlistDeleteSelectedLocked", App,
            StringComparison.Ordinal);
        Assert.Contains("deferSelectedRowPlainClickForDrag", App, StringComparison.Ordinal);
        Assert.Contains("deferredPlaylistPlainSelectionRowId", App, StringComparison.Ordinal);
        Assert.Contains("rowHeader.className = \"playlist-row-header\"", App,
            StringComparison.Ordinal);
        Assert.Contains("event.stopPropagation()", App, StringComparison.Ordinal);
        Assert.Contains("send(\"activate-playlist-row\", { rowId: element.dataset.rowId })", App,
            StringComparison.Ordinal);
        Assert.Contains("rowHeader.textContent = String(position + 1)", App,
            StringComparison.Ordinal);
        Assert.Contains("beginPlaylistDragGesture(rowHeader, element, event)", App,
            StringComparison.Ordinal);
        Assert.Contains("rowHeader.addEventListener(\"pointermove\", onPlaylistPointerMove)", App,
            StringComparison.Ordinal);
        Assert.Contains("handle.setPointerCapture(event.pointerId)", App,
            StringComparison.Ordinal);
        Assert.Contains("window.LegacyCutDrag.createDragRectangle(position, cutDragSize)", App,
            StringComparison.Ordinal);
        Assert.Contains("gesture.rectangle, playlistPointerPosition(event)", App,
            StringComparison.Ordinal);
        Assert.Contains("pointed.closest(\".playlist-row-header\")", App,
            StringComparison.Ordinal);
        Assert.Contains("targetHeader.closest(\".playlist-data-row\")", App,
            StringComparison.Ordinal);
        Assert.DoesNotContain("playlistDragMime", App, StringComparison.Ordinal);
        Assert.DoesNotContain("event.dataTransfer.setData(playlistDragMime", App,
            StringComparison.Ordinal);
        Assert.DoesNotContain("selectedRowIds", App, StringComparison.Ordinal);
        Assert.Contains("element.draggable = false", App, StringComparison.Ordinal);
        Assert.Contains("enabled.draggable = false", App, StringComparison.Ordinal);
        Assert.Contains("beginCutDragGesture(element, event, position)", App,
            StringComparison.Ordinal);
        Assert.Contains("window.LegacyCutDrag.shouldStart", App, StringComparison.Ordinal);
        Assert.Contains("(playlistDragGesture && playlistDragGesture.started)", App,
            StringComparison.Ordinal);
        Assert.Contains("const isDragSource = playlistDragGesture?.started === true", App,
            StringComparison.Ordinal);
        Assert.Contains("dragTargetPosition === \"after\"", App,
            StringComparison.Ordinal);
        Assert.Contains("clearPlaylistDragGesture()", App, StringComparison.Ordinal);
        Assert.Contains("<div aria-hidden=\"true\"></div><div>송출</div>", Markup,
            StringComparison.Ordinal);
        Assert.Contains("grid-template-columns: 30px 40px 100px", Styles,
            StringComparison.Ordinal);
        Assert.Contains(".playlist-data-row .playlist-row-header", Styles,
            StringComparison.Ordinal);
        Assert.Contains("if (!playlistDeleteSelectedLocked) send(\"delete-selected-playlist-rows\"", App,
            StringComparison.Ordinal);
        var deleteBranchStart = App.LastIndexOf(
            "if (event.key === \"Delete\"",
            StringComparison.Ordinal);
        var deleteBranchEnd = App.LastIndexOf(
            "} else if (event.key === \" \"",
            StringComparison.Ordinal);
        Assert.True(deleteBranchStart >= 0 && deleteBranchEnd > deleteBranchStart);
        Assert.DoesNotContain(
            "event.preventDefault()",
            App[deleteBranchStart..deleteBranchEnd],
            StringComparison.Ordinal);
        var homeEndBranchStart = App.LastIndexOf(
            "} else if ((event.key === \"Home\"",
            StringComparison.Ordinal);
        var homeEndBranchEnd = App.IndexOf(
            "\n  });",
            homeEndBranchStart,
            StringComparison.Ordinal);
        Assert.True(homeEndBranchStart >= 0 && homeEndBranchEnd > homeEndBranchStart);
        Assert.DoesNotContain(
            "event.preventDefault()",
            App[homeEndBranchStart..homeEndBranchEnd],
            StringComparison.Ordinal);
        Assert.Contains(".playlist-data-row.drag-before", Styles, StringComparison.Ordinal);
        Assert.Contains(".playlist-data-row.drag-after", Styles, StringComparison.Ordinal);
    }

    [Fact]
    public void PlaylistReorderUsesKeyedDomNodesAndPreservesFocus()
    {
        Assert.Contains("const existingRows = new Map()", App, StringComparison.Ordinal);
        Assert.Contains("existingRows.get(row.rowId)", App, StringComparison.Ordinal);
        Assert.Contains("playlistRows.insertBefore(element, elementAtPosition)", App, StringComparison.Ordinal);
        Assert.Contains("const retainedRowIds = new Set", App, StringComparison.Ordinal);
        Assert.DoesNotContain("playlistRows.replaceChild", App, StringComparison.Ordinal);
    }

    [Fact]
    public void MarketTabDragSwapsOnHoverThroughExpectedCSharpPositions()
    {
        Assert.Contains("send(\"hover-swap-tabs\"", App, StringComparison.Ordinal);
        Assert.Contains("source: draggedTabId", App, StringComparison.Ordinal);
        Assert.Contains("expectedSourceIndex: sourceIndex", App, StringComparison.Ordinal);
        Assert.Contains("expectedTargetIndex: targetIndex", App, StringComparison.Ordinal);
        Assert.Contains("event.dataTransfer.setData(\"text/x-mbn-tab\"", App,
            StringComparison.Ordinal);
        Assert.Contains("function hasCrossedLegacyTabSwapThreshold", App,
            StringComparison.Ordinal);
        Assert.Contains("sourceRect.width < targetRect.width", App,
            StringComparison.Ordinal);
        Assert.Contains(
            "clientX > targetRect.left + targetRect.width - sourceRect.width",
            App,
            StringComparison.Ordinal);
        Assert.Contains("clientX < targetRect.left + sourceRect.width", App,
            StringComparison.Ordinal);
        Assert.Contains("if (requestTabHoverSwap(targetId)) tabDragLastTargetId = targetId", App,
            StringComparison.Ordinal);
        Assert.Contains("marketTabs.addEventListener(\"dragover\", onMarketTabDragOver)", App,
            StringComparison.Ordinal);
        Assert.DoesNotContain("send(\"swap-tabs\"", App, StringComparison.Ordinal);
    }

    [Fact]
    public void MarketTabRenderingPreservesSidebarLabelsAndReconcilesNativeSelection()
    {
        Assert.Contains("button.querySelector(\".workspace-nav-label\")", App,
            StringComparison.Ordinal);
        Assert.Contains("if (label) label.textContent = tab.label", App,
            StringComparison.Ordinal);
        Assert.Contains("window.LegacyWorkspaceNavigation?.syncMarket", App,
            StringComparison.Ordinal);
    }

    [Fact]
    public void FixedCatalogUsesOpaqueLeafAndParentBatchIntents()
    {
        Assert.Contains("state.fixedCatalog", App, StringComparison.Ordinal);
        Assert.Contains("dataset.actionId", App, StringComparison.Ordinal);
        Assert.Contains("dataset.sectionIndex", App, StringComparison.Ordinal);
        Assert.Contains("send(\"activate-fixed-action\"", App, StringComparison.Ordinal);
        Assert.Contains("send(\"activate-fixed-section\"", App, StringComparison.Ordinal);
        Assert.Contains("send(\"activate-industry-section\"", App,
            StringComparison.Ordinal);
        Assert.Contains("send(\"activate-comparison-section\"", App,
            StringComparison.Ordinal);
        Assert.Contains("dataset.industrySectionIndex", App, StringComparison.Ordinal);
        Assert.Contains("dataset.comparisonSectionIndex", App, StringComparison.Ordinal);
        Assert.Contains("send(\"set-moving-averages\"", App, StringComparison.Ordinal);
    }

    [Fact]
    public void Uc1TreeSeparatesDisclosureFromSelectionAndPreservesRenderState()
    {
        Assert.Contains("function beginTreeRender", App, StringComparison.Ordinal);
        Assert.Contains("function finishTreeRender", App, StringComparison.Ordinal);
        Assert.Contains("const treeUiStateByTab = new Map()", App, StringComparison.Ordinal);
        Assert.Contains("pendingTreeResetTabs.add(nextTabId)", App, StringComparison.Ordinal);
        Assert.Contains("catalogExpandAll.checked = true", App, StringComparison.Ordinal);
        Assert.Contains("firstRoot.scrollIntoView", App, StringComparison.Ordinal);
        Assert.Contains("event.target.closest(\"[data-tree-disclosure]\")", App,
            StringComparison.Ordinal);
        Assert.Contains("event.preventDefault()", App, StringComparison.Ordinal);
        Assert.Contains("treeInteractionLocked = state.isBusy === true || " +
            "state.fixedSectionBatch != null", App, StringComparison.Ordinal);
        Assert.Contains("summary[data-tree-root].tree-node-selected", Styles,
            StringComparison.Ordinal);
        Assert.Contains("summary[data-tree-root]::-webkit-details-marker", Styles,
            StringComparison.Ordinal);
    }

    [Fact]
    public void IndustryWorkspaceUsesIndexesAndOpaqueActionIds()
    {
        Assert.Contains("state.industry", App, StringComparison.Ordinal);
        Assert.Contains("dataset.industryIndex", App, StringComparison.Ordinal);
        Assert.Contains("dataset.industryActionId", App, StringComparison.Ordinal);
        Assert.Contains("send(\"select-industry\"", App, StringComparison.Ordinal);
        Assert.Contains("send(\"double-click-industry\"", App, StringComparison.Ordinal);
        Assert.Contains("send(\"swap-industries\"", App, StringComparison.Ordinal);
        Assert.Contains("send(\"activate-industry-action\"", App, StringComparison.Ordinal);
        Assert.DoesNotContain("swap.textContent = \"↕ 교환\"", App,
            StringComparison.Ordinal);
    }

    [Fact]
    public void ComparisonWorkspaceUsesOnlyOpaqueTargetPairAndActionIds()
    {
        foreach (var value in new[]
                 {
                     "search-comparison-domestic",
                     "search-comparison-world",
                     "select-comparison-domestic",
                     "select-comparison-world",
                     "select-comparison-fixed",
                     "choose-comparison-domestic",
                     "choose-comparison-world",
                     "choose-comparison-fixed",
                     "swap-comparison-targets",
                     "clear-comparison-targets",
                     "add-comparison-pair",
                     "select-comparison-pair",
                     "move-comparison-pair",
                     "delete-selected-comparison-pair",
                     "delete-all-comparison-pairs",
                     "activate-comparison-action"
                 })
        {
            Assert.Contains($"\"{value}\"", App, StringComparison.Ordinal);
        }

        Assert.Contains("dataset.comparisonSelectionId", App, StringComparison.Ordinal);
        Assert.Contains("dataset.comparisonPairId", App, StringComparison.Ordinal);
        Assert.Contains("dataset.comparisonActionId", App, StringComparison.Ordinal);
        Assert.Contains("comparisonTargetClickTimer", App, StringComparison.Ordinal);
        foreach (var originalLabel in new[] { "◀▶", "추가", "UP", "DOWN", "DEL", "DEL ALL" })
        {
            Assert.Contains($"\"{originalLabel}\"", App, StringComparison.Ordinal);
        }
        Assert.DoesNotContain("clear.dataset.comparisonClear", App, StringComparison.Ordinal);
        Assert.DoesNotContain(
            "window.confirm(\"저장된 종목 비교쌍을 모두 삭제하겠습니까?\")",
            App,
            StringComparison.Ordinal);
        Assert.Contains("? \"confirm-native-dialog\"", App, StringComparison.Ordinal);
        Assert.Contains("send(\"cancel-native-dialog\", {});", App,
            StringComparison.Ordinal);
        Assert.DoesNotContain("stockCode", App, StringComparison.Ordinal);
    }

    [Fact]
    public void ThemeWorkspaceUsesOpaqueSelectionSortAndActionIds()
    {
        Assert.Contains("state.theme", App, StringComparison.Ordinal);
        Assert.Contains("dataset.themeResultId", App, StringComparison.Ordinal);
        Assert.Contains("dataset.themeActionId", App, StringComparison.Ordinal);
        Assert.Contains("send(\"search-themes\"", App, StringComparison.Ordinal);
        Assert.Contains("send(\"select-theme\"", App, StringComparison.Ordinal);
        Assert.Contains("send(\"set-theme-sort\"", App, StringComparison.Ordinal);
        Assert.Contains("send(\"activate-theme-action\"", App, StringComparison.Ordinal);
    }

    [Fact]
    public void ExpertAndTradingHaltWorkspacesUseOpaqueIntents()
    {
        foreach (var intent in new[]
                 {
                     "search-experts",
                     "select-expert",
                     "activate-expert-action",
                     "search-trading-halts",
                     "select-trading-halt",
                     "toggle-trading-halt-sort",
                     "activate-trading-halt-action"
                 })
        {
            Assert.Contains($"send(\"{intent}\"", App, StringComparison.Ordinal);
        }
    }

    [Fact]
    public void DbSaveLoadUseClosedNamedPlaylistIntentsWhilePlayoutRemainsDisabled()
    {
        foreach (var intent in new[]
                 {
                     "refresh-named-playlists",
                     "select-named-playlist",
                     "load-named-playlist-by-id",
                     "create-named-playlist",
                     "save-current-named-playlist-to",
                     "delete-selected-named-playlist"
                 })
        {
            Assert.Contains($"\"{intent}\"", App, StringComparison.Ordinal);
        }

        Assert.Contains("state.namedPlaylist", App, StringComparison.Ordinal);
        Assert.Contains("dataset.namedPlaylistDefinitionId", App, StringComparison.Ordinal);
        Assert.Contains(
            "beginNamedPlaylistSelection(definition.dataset.namedPlaylistDefinitionId)", App,
            StringComparison.Ordinal);
        Assert.Contains(
            "dbLoadButton.disabled = playlistStructureMutationLocked || busy || !named",
            App,
            StringComparison.Ordinal);
        Assert.Contains("requestId: requestId", App, StringComparison.Ordinal);
        Assert.Contains("commandResult.targetId === pending.targetId", App,
            StringComparison.Ordinal);
        Assert.Contains("named.isWriteQuarantined", App, StringComparison.Ordinal);
        Assert.DoesNotContain("programCode", App, StringComparison.OrdinalIgnoreCase);
        Assert.DoesNotContain("listText", App, StringComparison.OrdinalIgnoreCase);

        foreach (var label in new[] { "TAKE IN", "NEXT", "TAKE OUT" })
        {
            Assert.Matches($"<button[^>]*disabled[^>]*>[^<]*{Regex.Escape(label)}", Markup);
        }
    }

    [Fact]
    public void SeparatorRowsPreserveOriginalDragGesture()
    {
        Assert.Contains(
            "element.dataset.cutDragEnabled = cutSelectionEnabled && !cutDragLocked",
            App,
            StringComparison.Ordinal);
        Assert.Contains("element.draggable = false", App, StringComparison.Ordinal);
        Assert.DoesNotContain("cutDragEnabled = !row.isSeparator", App,
            StringComparison.Ordinal);
    }

    [Fact]
    public void CutDragFeedbackFollowsTheCapturedPointerWithoutOwningInput()
    {
        Assert.Contains("id=\"cut-drag-feedback\"", Markup, StringComparison.Ordinal);
        Assert.Contains("aria-hidden=\"true\"", Markup, StringComparison.Ordinal);
        Assert.Contains("function showCutDragFeedback", App, StringComparison.Ordinal);
        Assert.Contains("cutDragFeedback.dataset.dropAllowed", App,
            StringComparison.Ordinal);
        Assert.Contains("showCutDragFeedback(event, dropAllowed)", App,
            StringComparison.Ordinal);
        Assert.Contains("cutDragFeedback.hidden = true", App,
            StringComparison.Ordinal);
        Assert.Contains(".cut-drag-feedback", Styles, StringComparison.Ordinal);
        Assert.Contains("pointer-events: none", Styles, StringComparison.Ordinal);
    }

    [Fact]
    public void BusySnapshotFreezesControlsWithoutQueueingInput()
    {
        Assert.Contains("state.isBusy === true", App, StringComparison.Ordinal);
        Assert.Contains("classList.toggle(\"busy\"", App, StringComparison.Ordinal);
        Assert.Contains(
            "body.busy .operator-shell { pointer-events: none; }",
            Styles,
            StringComparison.Ordinal);
    }

    private static string FindRepositoryRoot()
    {
        DirectoryInfo? directory = new(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("The repository root could not be located.");
    }
}
