using MBN_STOCK_WEBVIEW.Core.Playout;
using MBN_STOCK_WEBVIEW.Core.Playout.Scenes;

namespace MBN_STOCK_WEBVIEW.Core.Tests;

public sealed class GridSceneBuilder5082Tests
{
    [Fact]
    public void Build_matches_original_three_by_three_value_and_visibility_order()
    {
        var data = new S5082SceneData(
            new(1000, -2000, 0),
            new(-1, 2, 0),
            new(0, 0, 3));

        var actual = new S5082SceneMutationBuilder().Build(data).Select(Describe).ToArray();

        Assert.Equal(
        [
            "V:row1_value1_1=1,000",
            "V:row1_value1_2=1,000",
            "V:row1_value1_3=1,000",
            "S:row1_value1_1=1",
            "S:row1_value1_2=0",
            "S:row1_value1_3=0",
            "V:row1_value2_1=-2,000",
            "V:row1_value2_2=-2,000",
            "V:row1_value2_3=-2,000",
            "S:row1_value2_1=0",
            "S:row1_value2_2=0",
            "S:row1_value2_3=1",
            "V:row1_value3_1=0",
            "V:row1_value3_2=0",
            "V:row1_value3_3=0",
            "S:row1_value3_1=0",
            "S:row1_value3_2=1",
            "S:row1_value3_3=0",
            "V:row2_value1_1=-1",
            "V:row2_value1_2=-1",
            "V:row2_value1_3=-1",
            "S:row2_value1_1=0",
            "S:row2_value1_2=0",
            "S:row2_value1_3=1",
            "V:row2_value2_1=2",
            "V:row2_value2_2=2",
            "V:row2_value2_3=2",
            "S:row2_value2_1=1",
            "S:row2_value2_2=0",
            "S:row2_value2_3=0",
            "V:row2_value3_1=0",
            "V:row2_value3_2=0",
            "V:row2_value3_3=0",
            "S:row2_value3_1=0",
            "S:row2_value3_2=1",
            "S:row2_value3_3=0",
            "V:row3_value1_1=0",
            "V:row3_value1_2=0",
            "V:row3_value1_3=0",
            "S:row3_value1_1=0",
            "S:row3_value1_2=1",
            "S:row3_value1_3=0",
            "V:row3_value2_1=0",
            "V:row3_value2_2=0",
            "V:row3_value2_3=0",
            "S:row3_value2_1=0",
            "S:row3_value2_2=1",
            "S:row3_value2_3=0",
            "V:row3_value3_1=3",
            "V:row3_value3_2=3",
            "V:row3_value3_3=3",
            "S:row3_value3_1=1",
            "S:row3_value3_2=0",
            "S:row3_value3_3=0"
        ], actual);
    }

    [Fact]
    public async Task Loader_maps_named_rows_with_a_spec_query_and_builder_consumes_them()
    {
        var table = new System.Data.DataTable();
        table.Columns.Add("NAME", typeof(string));
        table.Columns.Add("INDIVIDUAL_VALUE", typeof(decimal));
        table.Columns.Add("FOREIGN_VALUE", typeof(decimal));
        table.Columns.Add("INSTITUTION_VALUE", typeof(decimal));
        table.Rows.Add("코스닥", -1m, 2m, 3m);
        table.Rows.Add("코스피200", 4m, -5m, 6m);
        table.Rows.Add("코스피", 7m, 8m, -9m);
        var executor = new SpecExecutor(table);

        var data = await new S5082SceneDataLoader(executor).LoadAsync();
        var mutations = new S5082SceneMutationBuilder().Build(data);

        Assert.Equal(new InvestorTradingValues(7, 8, -9), data.Kospi);
        Assert.Equal(new InvestorTradingValues(-1, 2, 3), data.Kosdaq);
        Assert.Equal(new InvestorTradingValues(4, -5, 6), data.Kospi200);
        Assert.Equal(54, mutations.Count);
        Assert.NotNull(executor.Spec);
        Assert.Empty(executor.Spec.Parameters);
        Assert.Equal(0, executor.StringCalls);
    }

    private static string Describe(PlayoutMutation mutation) => mutation switch
    {
        PlayoutSetValue value => $"V:{value.ObjectName}={value.Value}",
        PlayoutSetVisible visible => $"S:{visible.ObjectName}={(visible.IsVisible ? 1 : 0)}",
        _ => mutation.GetType().Name
    };

    private sealed class SpecExecutor(System.Data.DataTable table) : MMoneyCoderSharp.Data.IDataQueryExecutor
    {
        public MMoneyCoderSharp.Data.DataQuerySpec? Spec { get; private set; }

        public int StringCalls { get; private set; }

        public System.Data.DataTable Execute(
            MMoneyCoderSharp.Data.DataSourceKind source,
            string tableName,
            string query)
        {
            StringCalls++;
            throw new InvalidOperationException();
        }

        public Task<System.Data.DataTable> ExecuteAsync(
            MMoneyCoderSharp.Data.DataSourceKind source,
            string tableName,
            string query,
            CancellationToken cancellationToken = default)
        {
            StringCalls++;
            throw new InvalidOperationException();
        }

        public Task<System.Data.DataTable> ExecuteAsync(
            MMoneyCoderSharp.Data.DataSourceKind source,
            string tableName,
            MMoneyCoderSharp.Data.DataQuerySpec query,
            CancellationToken cancellationToken = default)
        {
            Assert.Equal(MMoneyCoderSharp.Data.DataSourceKind.Oracle, source);
            Assert.Equal("SCENE_5082", tableName);
            Spec = query;
            return Task.FromResult(table.Copy());
        }
    }
}
