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

namespace QuantEngine.Core.Tests;

public class DecisionLearningServiceTests
{
    [Fact]
    public async Task RecordDecisionAsync_TrimsAndPersistsNormalizedPayloads()
    {
        var store = new Mock<INormalizedLearningStore>(MockBehavior.Strict);
        Guid decisionId = Guid.NewGuid();
        Guid factorObservationId = Guid.NewGuid();
        Guid sourceObservationId = Guid.NewGuid();

        store.Setup(s => s.AppendDecisionAsync(It.Is<DecisionEventRecord>(r =>
            r.DecisionKey == "decision-1" &&
            r.InstrumentId == "005930" &&
            r.Action == "BUY" &&
            r.Gate == "PASS" &&
            r.SourceVersion == "v1" &&
            r.TraceJson.Contains("\"mode\":\"test\"") &&
            r.ProvenanceJson.Contains("\"origin\":\"unit\"")))).ReturnsAsync(decisionId);

        store.Setup(s => s.AppendSourceObservationAsync(It.Is<SourceObservationRecord>(r =>
            r.InstrumentId == "005930" &&
            r.SourceName == "kis" &&
            r.SourceVersion == "v1" &&
            r.PayloadJson == "{}" &&
            r.ProvenanceJson == "{}"))).ReturnsAsync(sourceObservationId);

        store.Setup(s => s.AppendFactorObservationAsync(It.Is<FactorObservationRecord>(r =>
            r.ObservationId == sourceObservationId &&
            r.FactorObservationId != Guid.Empty &&
            r.FactorId == "factor_a" &&
            r.FactorVersion == "1.0" &&
            r.Gate == "PASS" &&
            r.ProvenanceJson == "{}"))).ReturnsAsync(factorObservationId);

        store.Setup(s => s.AppendDecisionFactorEvidenceAsync(decisionId, factorObservationId, "primary"))
            .Returns(Task.CompletedTask);

        var service = new DecisionLearningService(store.Object);

        var result = await service.RecordDecisionAsync(
            " decision-1 ",
            DateTimeOffset.Parse("2026-07-13T00:00:00Z"),
            " 005930 ",
            " BUY ",
            " PASS ",
            12.5m,
            " v1 ",
            new[]
            {
                new FactorEvidenceInput(
                    Guid.NewGuid(),
                    " factor_a ",
                    " 1.0 ",
                    DateTimeOffset.Parse("2026-07-13T00:00:00Z"),
                    3.14m,
                    null,
                    " PASS ",
                    " primary ",
                    " kis ",
                    "{}",
                    "{}")
            },
            new { mode = "test" },
            new { origin = "unit" });

        Assert.Equal(decisionId, result);
        store.VerifyAll();
    }

    [Fact]
    public async Task RecordDecisionAsync_RejectsMissingFactors()
    {
        var service = new DecisionLearningService(new Mock<INormalizedLearningStore>().Object);

        await Assert.ThrowsAsync<ArgumentNullException>(() => service.RecordDecisionAsync(
            "decision-1",
            DateTimeOffset.UtcNow,
            "005930",
            "BUY",
            "PASS",
            null,
            "v1",
            null!));
    }
}
