using Microsoft.EntityFrameworkCore;
using SolutionErp.Domain.Hrm;
using SolutionErp.Infrastructure.Services;
using SolutionErp.Infrastructure.Tests.Common;

namespace SolutionErp.Infrastructure.Tests.Services;

// Plan G-H1 Phase 1.5 Test Bundle 1 (S34) — EmployeeCodeGenerator atomic
// SERIALIZABLE sequence generator. Format "NV/{YYYY}/{Seq:D4}", reset per năm.
//
// Mirror PurchaseEvaluationCodeGeneratorTests + ContractCodeGeneratorTests
// pattern: SqliteDbFixture + FixedDateTime stub + direct constructor.
//
// SQLite không enforce SERIALIZABLE isolation strict (provider mapping graceful)
// — đủ test format + sequential increment + year boundary, KHÔNG đủ test race
// condition (cần SQL Server thật cho integration test riêng).
public class EmployeeCodeGeneratorTests
{
    private static (EmployeeCodeGenerator 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 EmployeeCodeGenerator(fix.Db, dt);
        return (gen, fix, dt);
    }

    // ===== Spec Fact 1: First call of year — empty sequence table =====

    [Fact]
    public async Task GenerateAsync_FirstCallOfYear_ReturnsNV001()
    {
        var (gen, fix, _) = CreateGenerator(year: 2026);
        using (fix)
        {
            // Empty sequence table — fresh DB từ EnsureCreated().
            fix.Db.EmployeeCodeSequences.Should().BeEmpty();

            var code = await gen.GenerateAsync();

            // Format "NV/{YYYY}/{Seq:D4}" — 4-digit zero pad.
            code.Should().Be("NV/2026/0001");

            // DB row created với LastSeq=1.
            var seq = await fix.Db.EmployeeCodeSequences
                .SingleAsync(s => s.Prefix == "NV/2026");
            seq.LastSeq.Should().Be(1);
        }
    }

    // ===== Spec Fact 2: Sequential calls increment seq =====

    [Fact]
    public async Task GenerateAsync_SequentialCalls_IncrementSeq()
    {
        var (gen, fix, _) = CreateGenerator(year: 2026);
        using (fix)
        {
            // Seed prefix "NV/2026" với LastSeq=5 — simulate 5 NV đã tạo trong năm.
            fix.Db.EmployeeCodeSequences.Add(new EmployeeCodeSequence
            {
                Prefix = "NV/2026",
                LastSeq = 5,
                UpdatedAt = new DateTime(2026, 6, 14, 0, 0, 0, DateTimeKind.Utc),
            });
            await fix.Db.SaveChangesAsync();

            var code = await gen.GenerateAsync();

            // LastSeq 5 → 6 → format "NV/2026/0006".
            code.Should().Be("NV/2026/0006");

            var seq = await fix.Db.EmployeeCodeSequences
                .SingleAsync(s => s.Prefix == "NV/2026");
            seq.LastSeq.Should().Be(6);
        }
    }

    // ===== Spec Fact 3: Year boundary — new sequence row for new year =====

    [Fact]
    public async Task GenerateAsync_YearBoundary_NewSequenceForNewYear()
    {
        // Year=2025 seeded LastSeq=999; clock moved to 2026 → expect NEW row năm
        // 2026 LastSeq=1, năm 2025 row giữ nguyên (no mutation).
        var fix = new SqliteDbFixture();
        var dt = new FixedDateTime(new DateTime(2025, 12, 31, 23, 0, 0, DateTimeKind.Utc));
        var gen = new EmployeeCodeGenerator(fix.Db, dt);

        using (fix)
        {
            // Seed prefix "NV/2025" với LastSeq=999.
            fix.Db.EmployeeCodeSequences.Add(new EmployeeCodeSequence
            {
                Prefix = "NV/2025",
                LastSeq = 999,
                UpdatedAt = new DateTime(2025, 12, 30, 0, 0, 0, DateTimeKind.Utc),
            });
            await fix.Db.SaveChangesAsync();

            // Cross year boundary — clock → 2026.
            dt.UtcNow = new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc);

            var code = await gen.GenerateAsync();

            // New year prefix "NV/2026" — sequence reset bắt đầu 0001.
            code.Should().Be("NV/2026/0001");

            // 2 row trong DB: 2025 giữ nguyên LastSeq=999, 2026 new LastSeq=1.
            var rows = await fix.Db.EmployeeCodeSequences
                .OrderBy(s => s.Prefix)
                .ToListAsync();
            rows.Should().HaveCount(2);
            rows[0].Prefix.Should().Be("NV/2025");
            rows[0].LastSeq.Should().Be(999);  // Untouched
            rows[1].Prefix.Should().Be("NV/2026");
            rows[1].LastSeq.Should().Be(1);
        }
    }
}
