using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using SolutionErp.Application.Office;
using SolutionErp.Domain.Identity;
using SolutionErp.Infrastructure.Tests.Common;

namespace SolutionErp.Infrastructure.Tests.Application;

// Phase 11 P11-F (S52 2026-06-08) — test-after critical-algo codegen MaTicket.
// CreateItTicketHandler set e.MaTicket = WorkflowAppCodeGen.GenerateMaDonTuAsync(db, "IT", clock.Now.Year, ...)
// → fullPrefix "IT/{year}" → format "IT/{year}/001" (seq D3 per-year, LastSeq++ atomic).
//
// ItTicket = kanban KHÔNG workflow V2 → gen mã lúc Create (khác Leave/OT gen lúc Submit).
// Coverage = FORMAT (regex) + SEQUENCE (001→002 cùng prefix) + PER-YEAR-PREFIX (year boundary tách seq).
//
// GOTCHA Serializable-on-SQLite (em main spec): codegen dùng
// BeginTransactionAsync(IsolationLevel.Serializable). SqliteDbFixture.cs comment xác nhận
// SQLite map IsolationLevel enum GRACEFULLY (no exception, just default behavior) — đã proven
// bởi WorkflowAppApproveV2Tests (assert "DT/LR/2026/001" + LastSeq==1 PASS). Cùng codegen path
// → KHÔNG throw trên SQLite. Test này confirm lại cho prefix "IT".
public class ItTicketCodeGenTests
{
    private static readonly DateTime FixedNow = new(2026, 6, 8, 8, 0, 0, DateTimeKind.Utc);

    private static (IdentityFixture fix, TestApplicationDbContext db, FixedDateTime clock) NewCtx()
    {
        var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var clock = new FixedDateTime(FixedNow);
        return (fix, db, clock);
    }

    private static TestCurrentUser AsUser(User u)
        => new() { UserId = u.Id, FullName = u.FullName, Roles = Array.Empty<string>() };

    private static CreateItTicketCommand BuildCmd(string title = "Máy in hỏng")
        => new(title, "Máy in tầng 3 không nhận lệnh in.", Category: 1, Priority: 2);

    // ============ Case 1: Format khớp regex IT/{year}/{seq:D3} ============

    [Fact]
    public async Task CreateItTicket_GeneratesMaTicket_MatchesFormat()
    {
        var (fix, db, clock) = NewCtx();
        using (fix)
        {
            var requester = await fix.CreateUserAsync("req-it1@test.local", "Người tạo", null, Array.Empty<string>());

            var handler = new CreateItTicketHandler(db, AsUser(requester), clock);
            var id = await handler.Handle(BuildCmd(), CancellationToken.None);

            var ticket = await db.ItTickets.FirstAsync(t => t.Id == id);
            ticket.MaTicket.Should().NotBeNullOrEmpty();
            ticket.MaTicket.Should().MatchRegex(@"^IT/\d{4}/\d{3}$", "format IT/{year}/{seq:D3}");
            ticket.MaTicket.Should().Be("IT/2026/001", "year = clock.Now.Year, seq đầu tiên = 001");

            // Sequence row tạo đúng key per-year prefix.
            var seq = await db.WorkflowAppCodeSequences.FirstAsync(s => s.Prefix == "IT/2026");
            seq.LastSeq.Should().Be(1);
        }
    }

    // ============ Case 2: Sequential — 2 create liên tiếp 001 → 002 ============

    [Fact]
    public async Task CreateItTicket_Sequential_IncrementsSeqSamePrefix()
    {
        var (fix, db, clock) = NewCtx();
        using (fix)
        {
            var requester = await fix.CreateUserAsync("req-it2@test.local", "Người tạo", null, Array.Empty<string>());
            var handler = new CreateItTicketHandler(db, AsUser(requester), clock);

            var id1 = await handler.Handle(BuildCmd("Ticket 1"), CancellationToken.None);
            var id2 = await handler.Handle(BuildCmd("Ticket 2"), CancellationToken.None);

            var t1 = await db.ItTickets.FirstAsync(t => t.Id == id1);
            var t2 = await db.ItTickets.FirstAsync(t => t.Id == id2);

            t1.MaTicket.Should().Be("IT/2026/001");
            t2.MaTicket.Should().Be("IT/2026/002", "LastSeq++ trên cùng prefix IT/2026");

            // Chỉ 1 sequence row (cùng prefix) — LastSeq = 2.
            var seqs = await db.WorkflowAppCodeSequences.Where(s => s.Prefix == "IT/2026").ToListAsync();
            seqs.Should().HaveCount(1);
            seqs[0].LastSeq.Should().Be(2);
        }
    }

    // ============ Case 3: Per-year-prefix — đổi năm → seq reset (key prefix mới) ============

    [Fact]
    public async Task CreateItTicket_DifferentYear_UsesSeparatePrefixSequence()
    {
        var (fix, db, clock) = NewCtx();
        using (fix)
        {
            var requester = await fix.CreateUserAsync("req-it3@test.local", "Người tạo", null, Array.Empty<string>());
            var handler = new CreateItTicketHandler(db, AsUser(requester), clock);

            // 2 ticket năm 2026.
            await handler.Handle(BuildCmd("2026-a"), CancellationToken.None);
            await handler.Handle(BuildCmd("2026-b"), CancellationToken.None);

            // Tua clock sang năm 2027 → prefix mới IT/2027 → seq bắt đầu lại từ 001.
            clock.UtcNow = new DateTime(2027, 1, 5, 8, 0, 0, DateTimeKind.Utc);
            var id2027 = await handler.Handle(BuildCmd("2027-a"), CancellationToken.None);

            var t2027 = await db.ItTickets.FirstAsync(t => t.Id == id2027);
            t2027.MaTicket.Should().Be("IT/2027/001", "year boundary → key prefix mới → seq reset 001");

            var seq2026 = await db.WorkflowAppCodeSequences.FirstAsync(s => s.Prefix == "IT/2026");
            var seq2027 = await db.WorkflowAppCodeSequences.FirstAsync(s => s.Prefix == "IT/2027");
            seq2026.LastSeq.Should().Be(2, "2 ticket năm 2026 không bị ảnh hưởng");
            seq2027.LastSeq.Should().Be(1);
        }
    }
}
