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

namespace SolutionErp.Infrastructure.Tests.Application;

// P11-C HMW Wave 2 (S51 2026-06-08) — gotcha #57 filtered-unique guard cho 4 HRM catalog
// có UNIQUE Code. Mirror EXACT HrmConfigHolidayTests Case 7 (filtered-unique-allows-reuse):
//   seed 1 row soft-deleted (IsDeleted=true) trong slot Code X →
//   gọi Create*Handler tạo row mới cùng Code X →
//   filtered index `[IsDeleted]=0` KHÔNG tính row đã xoá → SaveChanges thành công.
//
// CHỐT theo CODE (single source of truth) — 2 nhóm hành vi KHÁC nhau theo config:
//
//   ✅ Vehicle + Driver (Mig 44 — config ĐÃ filtered `.HasFilter("[IsDeleted] = 0")`)
//      VehicleConfiguration.cs:21 / DriverConfiguration.cs:23.
//      → recreate-on-soft-deleted-slot PHẢI GREEN (chứng minh 2 catalog mới đúng).
//
//   ❌ LeaveType + ShiftPattern (config BARE `.IsUnique()` KHÔNG filter = gotcha #57)
//      LeaveTypeConfiguration.cs:19 / ShiftPatternConfiguration.cs:19.
//      → DB UNIQUE tính CẢ row soft-deleted → handler app-check `!IsDeleted` PASS (slot
//        trông trống) → Add + SaveChanges → DB ném DbUpdateException (cùng bug class
//        Holiday từng RED trước Mig 43).
//      Test viết theo behavior MONG MUỐN (recreate SUCCESS, mirror Vehicle/Driver) →
//        DỰ KIẾN RED bây giờ = test-before REPRODUCE bug. Sau khi em main fix Mig 45
//        (.HasFilter cho 2 config) → 2 test này GREEN.
//
// LƯU Ý SOFT-DELETE TRONG TEST (giống HolidayTests):
//   AuditingInterceptor (production soft-delete: Deleted→Modified + IsDeleted=true) KHÔNG
//   wire trong SqliteDbFixture → `Remove + SaveChanges` ở fixture = HARD delete (xoá vật lý,
//   không còn row → không test được filtered-unique). Vì vậy ta SEED row với IsDeleted=true
//   thủ công để mô phỏng đúng trạng thái hậu-soft-delete (slot bị row đã xoá chiếm chỗ DB).
//
// Handlers chỉ cần IApplicationDbContext (no ICurrentUser/IDateTime) → new trực tiếp với fix.Db.
public class HrmConfigFilteredUniqueTests
{
    // ============================================================================
    // GROUP A — Vehicle + Driver: filtered config ĐÃ ĐÚNG → PHẢI GREEN
    // ============================================================================

    // ---- Vehicle ----

    [Fact]
    public async Task CreateVehicle_OnSoftDeletedCodeSlot_Succeeds_FilteredUniqueAllowsReuse()
    {
        using var fix = new SqliteDbFixture();
        var db = fix.Db;

        // Seed 1 xe Code="XE-01" đã soft-delete (IsDeleted=true).
        db.Vehicles.Add(new Vehicle
        {
            Id = Guid.NewGuid(),
            Code = "XE-01",
            Name = "Toyota Innova cũ",
            LicensePlate = "30A-00001",
            SeatCount = 7,
            IsActive = true,
            IsDeleted = true,
        });
        await db.SaveChangesAsync(CancellationToken.None);

        // Tạo xe mới CÙNG Code="XE-01" → filtered index bỏ qua row đã xoá → OK.
        var id = await new CreateVehicleHandler(db)
            .Handle(new CreateVehicleCommand("XE-01", "Toyota Innova mới", "30A-99999", 7, null),
                CancellationToken.None);

        id.Should().NotBeEmpty("filtered unique index không tính row đã soft-delete → slot reusable");
        (await db.Vehicles.CountAsync(x => x.Code == "XE-01" && !x.IsDeleted))
            .Should().Be(1, "chỉ 1 row active chiếm slot Code");
        (await db.Vehicles.CountAsync(x => x.Code == "XE-01"))
            .Should().Be(2, "soft-deleted gốc giữ lại cho audit + active mới");
    }

    // ---- Driver ----

    [Fact]
    public async Task CreateDriver_OnSoftDeletedCodeSlot_Succeeds_FilteredUniqueAllowsReuse()
    {
        using var fix = new SqliteDbFixture();
        var db = fix.Db;

        // Seed 1 tài xế Code="TX-01" đã soft-delete.
        db.Drivers.Add(new Driver
        {
            Id = Guid.NewGuid(),
            Code = "TX-01",
            Name = "Nguyễn Văn Tài (cũ)",
            PhoneNumber = "0900000001",
            LicenseNumber = "GPLX-OLD-01",
            LicenseClass = "B2",
            IsActive = true,
            IsDeleted = true,
        });
        await db.SaveChangesAsync(CancellationToken.None);

        var id = await new CreateDriverHandler(db)
            .Handle(new CreateDriverCommand("TX-01", "Trần Văn Mới", "0900099999", "GPLX-NEW-01", "C", null),
                CancellationToken.None);

        id.Should().NotBeEmpty("filtered unique index không tính row đã soft-delete → slot reusable");
        (await db.Drivers.CountAsync(x => x.Code == "TX-01" && !x.IsDeleted))
            .Should().Be(1, "chỉ 1 row active chiếm slot Code");
        (await db.Drivers.CountAsync(x => x.Code == "TX-01"))
            .Should().Be(2, "soft-deleted gốc giữ lại cho audit + active mới");
    }

    // ============================================================================
    // GROUP B — LeaveType + ShiftPattern + OtPolicy: BARE .IsUnique() = gotcha #57 → DỰ KIẾN RED
    //   (test-before REPRODUCE; assert behavior mong muốn = recreate SUCCESS).
    //   Sau Mig 45 (em main thêm .HasFilter cho 3 config) → 3 test này GREEN.
    //   (OtPolicy bị BỎ SÓT khỏi backlog gotcha #57 — em main bắt được S51 khi grep toàn bộ HRM catalog.)
    // ============================================================================

    // ---- LeaveType (gotcha #57 — DỰ KIẾN RED) ----

    [Fact]
    public async Task CreateLeaveType_OnSoftDeletedCodeSlot_Succeeds_FilteredUniqueAllowsReuse()
    {
        using var fix = new SqliteDbFixture();
        var db = fix.Db;

        // Seed 1 loại phép Code="ANNUAL" đã soft-delete.
        db.LeaveTypes.Add(new LeaveType
        {
            Id = Guid.NewGuid(),
            Code = "ANNUAL",
            Name = "Phép năm (cũ)",
            DaysPerYear = 12m,
            IsPaid = true,
            RequiresAttachment = false,
            IsActive = true,
            IsDeleted = true,
        });
        await db.SaveChangesAsync(CancellationToken.None);

        // Tạo loại phép mới CÙNG Code="ANNUAL". Handler app-check `!IsDeleted` PASS (slot trống
        // ở mức app), nhưng DB UNIQUE bare tính cả row đã xoá → SaveChanges sẽ ném
        // DbUpdateException CHỪNG NÀO Mig 45 chưa filter index. Đây là gotcha #57 confirmed.
        var act = async () => await new CreateLeaveTypeHandler(db)
            .Handle(new CreateLeaveTypeCommand("ANNUAL", "Phép năm (mới)", 12m, true, false, null),
                CancellationToken.None);

        await act.Should().NotThrowAsync(
            "MONG MUỐN: filtered unique cho phép tái dùng slot đã soft-delete (gotcha #57 — RED đến khi Mig 45 fix)");

        (await db.LeaveTypes.CountAsync(x => x.Code == "ANNUAL" && !x.IsDeleted))
            .Should().Be(1, "chỉ 1 row active chiếm slot Code");
        (await db.LeaveTypes.CountAsync(x => x.Code == "ANNUAL"))
            .Should().Be(2, "soft-deleted gốc giữ lại cho audit + active mới");
    }

    // ---- ShiftPattern (gotcha #57 — DỰ KIẾN RED) ----

    [Fact]
    public async Task CreateShift_OnSoftDeletedCodeSlot_Succeeds_FilteredUniqueAllowsReuse()
    {
        using var fix = new SqliteDbFixture();
        var db = fix.Db;

        // Seed 1 ca làm Code="HC" đã soft-delete.
        db.ShiftPatterns.Add(new ShiftPattern
        {
            Id = Guid.NewGuid(),
            Code = "HC",
            Name = "Hành chính (cũ)",
            StartTime = new TimeOnly(8, 0),
            EndTime = new TimeOnly(17, 0),
            BreakMinutes = 60,
            WorkDays = "Mon,Tue,Wed,Thu,Fri",
            IsActive = true,
            IsDeleted = true,
        });
        await db.SaveChangesAsync(CancellationToken.None);

        var act = async () => await new CreateShiftHandler(db)
            .Handle(new CreateShiftCommand("HC", "Hành chính (mới)", new TimeOnly(8, 0), new TimeOnly(17, 30),
                60, "Mon,Tue,Wed,Thu,Fri", null), CancellationToken.None);

        await act.Should().NotThrowAsync(
            "MONG MUỐN: filtered unique cho phép tái dùng slot đã soft-delete (gotcha #57 — RED đến khi Mig 45 fix)");

        (await db.ShiftPatterns.CountAsync(x => x.Code == "HC" && !x.IsDeleted))
            .Should().Be(1, "chỉ 1 row active chiếm slot Code");
        (await db.ShiftPatterns.CountAsync(x => x.Code == "HC"))
            .Should().Be(2, "soft-deleted gốc giữ lại cho audit + active mới");
    }

    // ---- OtPolicy (gotcha #57 — bare .IsUnique() BỊ BỎ SÓT khỏi backlog, em main bắt được S51) ----

    [Fact]
    public async Task CreateOtPolicy_OnSoftDeletedCodeSlot_Succeeds_FilteredUniqueAllowsReuse()
    {
        using var fix = new SqliteDbFixture();
        var db = fix.Db;

        // Seed 1 chính sách OT Code="STANDARD" đã soft-delete.
        db.OtPolicies.Add(new OtPolicy
        {
            Id = Guid.NewGuid(),
            Code = "STANDARD",
            Name = "Chính sách OT chuẩn (cũ)",
            MultiplierWeekday = 1.5m,
            MultiplierWeekend = 2.0m,
            MultiplierHoliday = 3.0m,
            MaxHoursPerDay = 4,
            MaxHoursPerMonth = 40,
            MaxHoursPerYear = 200,
            IsActive = true,
            IsDeleted = true,
        });
        await db.SaveChangesAsync(CancellationToken.None);

        var act = async () => await new CreateOtPolicyHandler(db)
            .Handle(new CreateOtPolicyCommand("STANDARD", "Chính sách OT chuẩn (mới)", 1.5m, 2.0m, 3.0m, 4, 40, 200, null),
                CancellationToken.None);

        await act.Should().NotThrowAsync(
            "MONG MUỐN: filtered unique cho phép tái dùng slot đã soft-delete (gotcha #57 — OtPolicy bị bỏ sót, fix chung Mig 45)");

        (await db.OtPolicies.CountAsync(x => x.Code == "STANDARD" && !x.IsDeleted))
            .Should().Be(1, "chỉ 1 row active chiếm slot Code");
        (await db.OtPolicies.CountAsync(x => x.Code == "STANDARD"))
            .Should().Be(2, "soft-deleted gốc giữ lại cho audit + active mới");
    }
}
