using QuantEngine.Application.Services;
using QuantEngine.Core.Interfaces;

namespace QuantEngine.Core.Tests;

public class HistoryIngestionE2ETests
{
    [Fact]
    public async Task AppendDecisionThenReadSnapshotRoundTripsThroughApplicationFlow()
    {
        var store = new FakeHistoryStore();
        var ingestion = new HistoryIngestionService(store);
        var reader = new PostgresqlHistorySnapshotReader(store);

        var appendCount = await ingestion.AppendDecisionAsync(new Dictionary<string, object?>
        {
            ["decision_id"] = "dec-001",
            ["decided_at"] = DateTimeOffset.Parse("2026-06-26T09:00:00+09:00"),
            ["instrument_id"] = "005930",
            ["action"] = "BUY",
            ["gate"] = "PASS",
            ["score"] = 87.5,
            ["source_version"] = "v1",
            ["provenance"] = new Dictionary<string, object?>
            {
                ["source"] = "unit-test"
            }
        });

        Assert.Equal(1, appendCount);

        var rows = await reader.ReadAsync("decision_result_history", 10);
        Assert.Single(rows);
        Assert.Equal("dec-001", rows[0]["decision_id"]);
        Assert.Equal("005930", rows[0]["instrument_id"]);
        Assert.Equal("BUY", rows[0]["action"]);
        Assert.Equal("PASS", rows[0]["gate"]);
        Assert.Equal(87.5, rows[0]["score"]);
    }

    [Fact]
    public async Task AppendFactorOutputThenReadSnapshotPreservesPayload()
    {
        var store = new FakeHistoryStore();
        var ingestion = new HistoryIngestionService(store);
        var reader = new PostgresqlHistorySnapshotReader(store);

        var appendCount = await ingestion.AppendFactorOutputAsync(
            factorId: "RS_VERDICT_V2",
            factorVersion: "2026-06-26",
            outputValue: 1.23,
            outputGate: "PASS",
            sourceVersion: "source-42",
            observedAt: DateTimeOffset.Parse("2026-06-26T10:00:00+09:00"));

        Assert.Equal(1, appendCount);

        var rows = await reader.ReadAsync("factor_output_history", 10);
        Assert.Single(rows);
        Assert.Equal("RS_VERDICT_V2", rows[0]["factor_id"]);
        Assert.Equal("2026-06-26", rows[0]["factor_version"]);
        Assert.Equal(1.23, rows[0]["output_value"]);
        Assert.Equal("PASS", rows[0]["output_gate"]);
        Assert.Equal("source-42", rows[0]["source_version"]);
    }

    private sealed class FakeHistoryStore : IPostgresqlHistoryStore
    {
        private readonly Dictionary<string, List<IDictionary<string, object?>>> _rows = new();

        public Task<int> AppendAsync(string domain, IDictionary<string, object?> payload)
        {
            if (!_rows.TryGetValue(domain, out var list))
            {
                list = new List<IDictionary<string, object?>>();
                _rows[domain] = list;
            }

            list.Add(new Dictionary<string, object?>(payload));
            return Task.FromResult(1);
        }

        public Task<IReadOnlyList<IDictionary<string, object?>>> SnapshotAsync(string domain, int limit = 500)
        {
            if (!_rows.TryGetValue(domain, out var list))
            {
                return Task.FromResult<IReadOnlyList<IDictionary<string, object?>>>(Array.Empty<IDictionary<string, object?>>());
            }

            return Task.FromResult<IReadOnlyList<IDictionary<string, object?>>>(list.Take(limit).ToList());
        }

        public Task<long> RecordWaterfallExecutionAsync(string runId, string ticker, int rank, string stage, string action, int targetQty, decimal? targetPrice, decimal? bidAskSpreadBps, decimal? slippageBps, string status, string rationale)
        {
            return Task.FromResult(1L);
        }

        public Task<long> RecordShadowLedgerAsync(string runId, string ticker, string blockedGate, string blockedReason, decimal shadowPrice, int shadowQty, decimal? shadowTpPrice, decimal? shadowSlPrice)
        {
            return Task.FromResult(1L);
        }
    }
}
