using System.Text.Json;
using MBN_STOCK_WEBVIEW.LegacyApplication;
using MMoneyCoderSharp.Data;

namespace MBN_STOCK_WEBVIEW.LegacyBridge.Tests;

public sealed class LegacyManualFinancialBridgeTests
{
    [Theory]
    [InlineData("revenue-composition", ManualFinancialScreenKind.RevenueComposition)]
    [InlineData("growth-metrics", ManualFinancialScreenKind.GrowthMetrics)]
    [InlineData("sales", ManualFinancialScreenKind.Sales)]
    [InlineData("operating-profit", ManualFinancialScreenKind.OperatingProfit)]
    public void Open_uses_closed_GraphE_screen_identity(
        string screen,
        ManualFinancialScreenKind expected)
    {
        var json = JsonSerializer.Serialize(new
        {
            type = "open-manual-financial",
            payload = new { screen }
        });

        Assert.True(LegacyBridgeProtocol.TryParseIntent(json, out var parsed, out _));
        Assert.Equal(expected, Assert.IsType<LegacyOpenManualFinancialIntent>(parsed).Screen);
        Assert.False(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"open-manual-financial\",\"payload\":{\"screen\":\"INPUT_SELL\"}}",
            out _,
            out _));
    }

    [Fact]
    public void Save_parses_all_typed_record_shapes_without_accepting_DB_authority()
    {
        AssertSaveType<ManualRevenueCompositionRecord>(
            """
            {"screen":"revenue-composition","stockName":"Alpha","baseDate":"2026.07","slices":[{"label":"A","percentageText":"25"},null,null,null,null]}
            """);
        AssertSaveType<ManualGrowthMetricsRecord>(
            """
            {"screen":"growth-metrics","stockName":"Alpha","salesGrowth":[1,2,null,4],"operatingProfitGrowth":[5,6,7,8],"netAssetGrowth":[9,10,11,12],"netIncomeGrowth":[13,14,15,16],"periods":["1Q","2Q","3Q","4Q"]}
            """);
        AssertSaveType<ManualSalesRecord>(
            """
            {"screen":"sales","stockName":"Alpha","quarters":[{"quarter":"1Q","value":1},{"quarter":"2Q","value":2},{"quarter":"3Q","value":3},{"quarter":"4Q","value":4},{"quarter":"5Q","value":5},{"quarter":"6Q","value":6}]}
            """);
        AssertSaveType<ManualOperatingProfitRecord>(
            """
            {"screen":"operating-profit","stockName":"Alpha","quarters":[{"quarter":"1Q","value":1},{"quarter":"2Q","value":2},{"quarter":"3Q","value":3},{"quarter":"4Q","value":4},{"quarter":"5Q","value":5},{"quarter":"6Q","value":6}]}
            """);

        Assert.False(LegacyBridgeProtocol.TryParseIntent(
            """
            {"type":"save-manual-financial","payload":{"record":{"screen":"sales","stockName":"Alpha","rowVersion":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","quarters":[]}}}
            """,
            out _,
            out _));
    }

    [Fact]
    public void Raw_save_preserves_legacy_strings_but_rejects_any_native_row_locator()
    {
        const string json =
            "{\"type\":\"save-manual-financial-raw\",\"payload\":{\"record\":" +
            "{\"screen\":\"sales\",\"stockName\":\"Alpha\",\"storageValues\":" +
            "[\"\",\"_\",\"label_value_extra\",\" 12 \",\"\\u0001legacy\",\"\\u0000tail\"]}}}";

        Assert.True(LegacyBridgeProtocol.TryParseIntent(json, out var parsed, out _));
        var raw = Assert.IsType<LegacySaveManualFinancialRawIntent>(parsed).Record;
        Assert.Equal(
            ["", "_", "label_value_extra", " 12 ", "\u0001legacy", "\u0000tail"],
            raw.StorageValues);

        Assert.False(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"save-manual-financial-raw\",\"payload\":{\"record\":" +
            "{\"screen\":\"sales\",\"stockName\":\"Alpha\",\"storageValues\":" +
            "[\"1Q_1\",\"2Q_2\",\"3Q_3\",\"4Q_4\",\"5Q_5\",\"6Q_6\"]," +
            "\"rowHandle\":\"AAAAAAAAAAAAAAAAAA\"}}}",
            out _,
            out _));
    }

    [Fact]
    public void CRUD_and_action_intents_accept_only_opaque_ids_or_empty_payloads()
    {
        Assert.True(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"load-manual-financial\",\"payload\":{\"resultId\":\"mf-row-0123456789abcdef\"}}",
            out var loaded,
            out _));
        Assert.IsType<LegacyLoadManualFinancialIntent>(loaded);

        foreach (var type in new[]
                 {
                     "begin-manual-financial-create",
                     "delete-manual-financial",
                     "delete-all-manual-financial",
                     "close-manual-financial"
                 })
        {
            Assert.True(LegacyBridgeProtocol.TryParseIntent(
                $"{{\"type\":\"{type}\",\"payload\":{{}}}}",
                out _,
                out _));
            Assert.False(LegacyBridgeProtocol.TryParseIntent(
                $"{{\"type\":\"{type}\",\"payload\":{{\"table\":\"INPUT_SELL\"}}}}",
                out _,
                out _));
        }

        Assert.True(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"activate-manual-financial-action\",\"payload\":{\"actionId\":\"sales\"}}",
            out var action,
            out _));
        Assert.Equal(
            "sales",
            Assert.IsType<LegacyActivateManualFinancialActionIntent>(action).ActionId);
        Assert.False(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"activate-manual-financial-action\",\"payload\":{\"actionId\":\"s5080\"}}",
            out _,
            out _));
    }

    [Fact]
    public void Empty_GraphE_stock_search_is_a_valid_quiet_no_op_intent()
    {
        Assert.True(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"search-manual-financial-stocks\",\"payload\":{\"stockName\":\"\"}}",
            out var parsed,
            out _));
        Assert.Equal(
            string.Empty,
            Assert.IsType<LegacySearchManualFinancialStocksIntent>(parsed).StockName);

        Assert.True(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"select-manual-financial-stock\",\"payload\":{\"resultId\":\"\"}}",
            out var selection,
            out _));
        Assert.Equal(
            string.Empty,
            Assert.IsType<LegacySelectManualFinancialStockIntent>(selection).ResultId);
    }

    [Fact]
    public void GraphE_find_and_name_sort_require_strict_generation_bound_payloads()
    {
        Assert.True(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"find-manual-financial\",\"payload\":{\"query\":\"Alpha\",\"direction\":\"down\",\"generation\":7}}",
            out var find,
            out _));
        var parsedFind = Assert.IsType<LegacyFindManualFinancialIntent>(find);
        Assert.Equal(LegacyManualFinancialFindDirection.Down, parsedFind.Direction);
        Assert.Equal(7, parsedFind.Generation);

        Assert.True(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"toggle-manual-financial-name-sort\",\"payload\":{\"generation\":7}}",
            out var sort,
            out _));
        Assert.Equal(7,
            Assert.IsType<LegacyToggleManualFinancialNameSortIntent>(sort).Generation);

        Assert.False(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"find-manual-financial\",\"payload\":{\"query\":\"Alpha\",\"direction\":\"sideways\",\"generation\":7}}",
            out _,
            out _));
        Assert.False(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"toggle-manual-financial-name-sort\",\"payload\":{\"generation\":7,\"table\":\"INPUT_SELL\"}}",
            out _,
            out _));
    }

    [Fact]
    public async Task Projection_exposes_editor_values_but_not_rowVersion_or_DB_scene_authority()
    {
        var manualService = new StaticManualService();
        var workflow = new LegacyManualFinancialWorkflow(
            manualService,
            new StaticStockService());
        var controller = new LegacyOperatorController(
            new EmptyStockLookup(),
            manualFinancialWorkflow: workflow);
        var opened = await controller.OpenManualFinancialAsync(ManualFinancialScreenKind.Sales);
        var loaded = await controller.LoadManualFinancialAsync(
            Assert.Single(opened.ManualFinancial!.Rows).ResultId);

        using var document = JsonDocument.Parse(LegacyBridgeProtocol.SerializeState(loaded));
        var manual = document.RootElement.GetProperty("payload").GetProperty("manualFinancial");

        Assert.Equal("sales", manual.GetProperty("screen").GetString());
        Assert.True(manual.GetProperty("generation").GetInt64() > 0);
        Assert.Equal(0, manual.GetProperty("listRejectedItemCount").GetInt32());
        Assert.True(manual.GetProperty("rows")[0].GetProperty("isDataReadable").GetBoolean());
        Assert.True(manual.GetProperty("rows")[0].GetProperty("isPlayoutReady").GetBoolean());
        Assert.Equal(
            ["Alpha", "1Q_1", "2Q_2", "3Q_3", "4Q_4", "5Q_5", "6Q_6"],
            manual.GetProperty("rows")[0]
                .GetProperty("previewValues")
                .EnumerateArray()
                .Select(static value => value.GetString()!)
                .ToArray());
        Assert.False(manual.GetProperty("rows")[0].TryGetProperty("rowVersion", out _));
        Assert.False(manual.GetProperty("rows")[0].TryGetProperty("rowHandle", out _));
        Assert.Equal("ascending", manual.GetProperty("nameSortDirection").GetString());
        Assert.Equal("Alpha", manual.GetProperty("selectedRecord").GetProperty("stockName").GetString());
        Assert.Equal(JsonValueKind.Null, manual.GetProperty("selectedRawRecord").ValueKind);
        Assert.Equal("sales", manual.GetProperty("action").GetProperty("actionId").GetString());
        Assert.False(manual.TryGetProperty("rowVersion", out _));
        Assert.False(manual.TryGetProperty("tableName", out _));
        Assert.False(manual.TryGetProperty("builderKey", out _));
        Assert.False(manual.GetProperty("stockCandidates")[0].TryGetProperty("code", out _));
    }

    private static void AssertSaveType<TRecord>(string recordJson)
        where TRecord : ManualFinancialRecord
    {
        var json = $"{{\"type\":\"save-manual-financial\",\"payload\":{{\"record\":{recordJson}}}}}";
        Assert.True(LegacyBridgeProtocol.TryParseIntent(json, out var parsed, out _));
        Assert.IsType<TRecord>(Assert.IsType<LegacySaveManualFinancialIntent>(parsed).Record);
    }

    private sealed class EmptyStockLookup : ILegacyStockLookup
    {
        public Task<LegacyStockSearchData> SearchAsync(
            string query,
            CancellationToken cancellationToken = default) =>
            Task.FromResult(new LegacyStockSearchData(query, [], []));
    }

    private sealed class StaticStockService : IStockSearchService
    {
        public Task<StockSearchResult> SearchAsync(
            string query,
            int maximumResults = LegacyStockSearchService.DefaultMaximumResults,
            CancellationToken cancellationToken = default) =>
            Task.FromResult(new StockSearchResult(
                query,
                DateTimeOffset.UtcNow,
                [new StockSearchItem(
                    StockMarket.Kospi,
                    DataSourceKind.Oracle,
                    "Alpha",
                    "000001")],
                false));
    }

    private sealed class StaticManualService : IManualFinancialScreenService
    {
        private static readonly ManualFinancialSnapshot Row = new(
            new ManualSalesRecord(
                new ManualFinancialIdentity(ManualFinancialScreenKind.Sales, "Alpha"),
                Enumerable.Range(1, 6)
                    .Select(index => new ManualQuarterValue($"{index}Q", index))
                    .ToArray()),
            new string('A', 64));

        public bool CanMutate => false;

        public Task<ManualFinancialSearchResult> SearchAsync(
            ManualFinancialScreenKind screen,
            string query = "",
            int maximumResults = LegacyManualFinancialScreenService.DefaultMaximumResults,
            CancellationToken cancellationToken = default) =>
            Task.FromResult(new ManualFinancialSearchResult(
                screen,
                query,
                DateTimeOffset.UtcNow,
                screen == ManualFinancialScreenKind.Sales ? [Row] : [],
                false));

        public Task<ManualFinancialSnapshot> GetAsync(
            ManualFinancialIdentity identity,
            CancellationToken cancellationToken = default) =>
            Task.FromResult(Row);

        public Task<ManualFinancialMutationReceipt> CreateAsync(
            ManualFinancialRecord record,
            CancellationToken cancellationToken = default) => throw new NotSupportedException();

        public Task<ManualFinancialMutationReceipt> UpdateAsync(
            ManualFinancialSnapshot expected,
            ManualFinancialRecord replacement,
            CancellationToken cancellationToken = default) => throw new NotSupportedException();

        public Task<ManualFinancialMutationReceipt> DeleteAsync(
            ManualFinancialSnapshot expected,
            CancellationToken cancellationToken = default) => throw new NotSupportedException();

        public Task<ManualFinancialMutationReceipt> DeleteAllAsync(
            ManualFinancialScreenKind screen,
            CancellationToken cancellationToken = default) => throw new NotSupportedException();
    }
}
