using SolutionErp.Domain.Contracts;
using SolutionErp.Infrastructure.Services;
using SolutionErp.Infrastructure.Tests.Common;

namespace SolutionErp.Infrastructure.Tests.Services;

// Tests cho ContractCodeGenerator — format mã RG-001 + atomic sequence increment.
// Dùng SQLite in-memory cho transactions thật, KHÔNG test race condition
// (cần SQL Server thật cho SERIALIZABLE strict — đó là integration test riêng).

public class ContractCodeGeneratorTests
{
    private static (ContractCodeGenerator gen, SqliteDbFixture fix, FixedDateTime dt) CreateGenerator(int year = 2026)
    {
        var fix = new SqliteDbFixture();
        var dt = new FixedDateTime(new DateTime(year, 6, 15, 0, 0, 0, DateTimeKind.Utc));
        var gen = new ContractCodeGenerator(fix.Db, dt);
        return (gen, fix, dt);
    }

    // ===== Format per ContractType (RG-001) =====

    [Theory]
    [InlineData(ContractType.HopDongThauPhu, "HĐTP")]
    [InlineData(ContractType.HopDongGiaoKhoan, "HĐGK")]
    [InlineData(ContractType.HopDongNhaCungCap, "NCC")]
    [InlineData(ContractType.HopDongDichVu, "HĐDV")]
    [InlineData(ContractType.HopDongMuaBan, "MB")]
    public async Task Generate_ProjectScoped_FormatMatches_RG001(ContractType type, string expectedTypeCode)
    {
        var (gen, fix, _) = CreateGenerator();
        using (fix)
        {
            var contract = new Contract { Type = type };
            var code = await gen.GenerateAsync(contract, projectCode: "FLOCK01", supplierCode: "BTBM");
            // Expected format: {Project}/{TypeCode}/SOL&{Supplier}/{Seq}
            code.Should().Be($"FLOCK01/{expectedTypeCode}/SOL&BTBM/01");
        }
    }

    [Fact]
    public async Task Generate_FrameworkContract_NCC_UsesYearScope_NotProjectCode()
    {
        var (gen, fix, _) = CreateGenerator(year: 2026);
        using (fix)
        {
            var contract = new Contract { Type = ContractType.HopDongNguyenTacNCC };
            var code = await gen.GenerateAsync(contract, projectCode: "FLOCK01", supplierCode: "BTBM");
            // Framework HĐ → scope = year (không phải project)
            code.Should().Be("2026/NCC/SOL&BTBM/01");
        }
    }

    [Fact]
    public async Task Generate_FrameworkContract_DichVu_UsesYearScope()
    {
        var (gen, fix, _) = CreateGenerator(year: 2026);
        using (fix)
        {
            var contract = new Contract { Type = ContractType.HopDongNguyenTacDichVu };
            var code = await gen.GenerateAsync(contract, projectCode: "FLOCK01", supplierCode: "ABC");
            code.Should().Be("2026/HĐDV/SOL&ABC/01");
        }
    }

    // ===== Sequence increment per prefix =====

    [Fact]
    public async Task Generate_SamePrefix_SequenceIncrements()
    {
        var (gen, fix, _) = CreateGenerator();
        using (fix)
        {
            var c = new Contract { Type = ContractType.HopDongThauPhu };
            var code1 = await gen.GenerateAsync(c, "FLOCK01", "BTBM");
            var code2 = await gen.GenerateAsync(c, "FLOCK01", "BTBM");
            var code3 = await gen.GenerateAsync(c, "FLOCK01", "BTBM");

            code1.Should().EndWith("/01");
            code2.Should().EndWith("/02");
            code3.Should().EndWith("/03");
        }
    }

    [Fact]
    public async Task Generate_DifferentPrefixes_IndependentSequences()
    {
        var (gen, fix, _) = CreateGenerator();
        using (fix)
        {
            var c = new Contract { Type = ContractType.HopDongThauPhu };
            var codeA = await gen.GenerateAsync(c, "FLOCK01", "BTBM");
            var codeB = await gen.GenerateAsync(c, "FLOCK02", "BTBM");
            var codeC = await gen.GenerateAsync(c, "FLOCK01", "OTHER");

            // Mỗi prefix có sequence riêng, đều bắt đầu /01
            codeA.Should().Be("FLOCK01/HĐTP/SOL&BTBM/01");
            codeB.Should().Be("FLOCK02/HĐTP/SOL&BTBM/01");
            codeC.Should().Be("FLOCK01/HĐTP/SOL&OTHER/01");
        }
    }

    // ===== Year boundary cho framework HĐ =====

    [Fact]
    public async Task Generate_Framework_YearChange_ResetsSequence()
    {
        var (gen, fix, dt) = CreateGenerator(year: 2026);
        using (fix)
        {
            var c = new Contract { Type = ContractType.HopDongNguyenTacNCC };

            // 2026: 2 phiếu
            var code2026_1 = await gen.GenerateAsync(c, "FLOCK01", "BTBM");
            var code2026_2 = await gen.GenerateAsync(c, "FLOCK01", "BTBM");
            code2026_1.Should().Be("2026/NCC/SOL&BTBM/01");
            code2026_2.Should().Be("2026/NCC/SOL&BTBM/02");

            // Sang 2027 → prefix mới (year đổi) → bắt đầu lại /01
            dt.UtcNow = new DateTime(2027, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            var code2027 = await gen.GenerateAsync(c, "FLOCK01", "BTBM");
            code2027.Should().Be("2027/NCC/SOL&BTBM/01");
        }
    }

    // ===== Persistence sequence row =====

    [Fact]
    public async Task Generate_PersistsSequenceRow_WithCorrectLastSeq()
    {
        var (gen, fix, _) = CreateGenerator();
        using (fix)
        {
            var c = new Contract { Type = ContractType.HopDongThauPhu };
            await gen.GenerateAsync(c, "FLOCK01", "BTBM");
            await gen.GenerateAsync(c, "FLOCK01", "BTBM");
            await gen.GenerateAsync(c, "FLOCK01", "BTBM");

            var seq = fix.Db.ContractCodeSequences.Single(s => s.Prefix == "FLOCK01/HĐTP/SOL&BTBM");
            seq.LastSeq.Should().Be(3);
            seq.UpdatedAt.Should().NotBe(default);
        }
    }
}
