using Microsoft.EntityFrameworkCore;
using SolutionErp.Application.Master.Departments;
using SolutionErp.Application.Master.Projects;
using SolutionErp.Application.Master.Suppliers.Commands.CreateSupplier;
using SolutionErp.Domain.Master;
using SolutionErp.Infrastructure.Tests.Common;

namespace SolutionErp.Infrastructure.Tests.Application;

// P11-D HMW Wave 2 (S52 2026-06-08) — gotcha #57 filtered-unique guard cho 3 Master catalog
// có UNIQUE Code hiện đang BARE `.IsUnique()` KHÔNG `.HasFilter("[IsDeleted]=0")`:
//   - Department  (DepartmentConfiguration.cs:18, soft-delete :20)
//   - Project     (ProjectConfiguration.cs:19,    soft-delete :21)
//   - Supplier    (SupplierConfiguration.cs:24,   soft-delete :27)
//
// Mirror EXACT GROUP B của HrmConfigFilteredUniqueTests (S51 LeaveType/Shift/OtPolicy):
//   seed 1 row soft-deleted (IsDeleted=true) trong slot Code X →
//   gọi Create*CommandHandler tạo row mới CÙNG Code X →
//   MONG MUỐN: filtered index `[IsDeleted]=0` bỏ qua row đã xoá → SaveChanges thành công,
//   query default-filtered trả ĐÚNG 1 row active.
//
// gotcha #57 = BUG-CLASS → test-before BẮT BUỘC (docs/rules.md §7). Assert behavior FIXED.
//
// DỰ KIẾN RED TRÊN CODE HIỆN TẠI:
//   Handler app-check `db.X.AnyAsync(x => x.Code == request.Code)` chạy QUA HasQueryFilter
//   (!IsDeleted) → row soft-deleted bị loại → app-check PASS (slot trông trống ở mức app) →
//   db.X.Add + SaveChanges → DB UNIQUE bare tính CẢ row đã xoá → ném DbUpdateException
//   (SQLite Error 19 UNIQUE constraint failed). 3 test RED = REPRODUCE gotcha #57.
//   Sau khi em main fix migration (.HasFilter cho 3 config) → 3 test này GREEN.
//   (KHÔNG sửa Configuration.cs / Domain / migration ở đây — implementer làm.)
//
// LƯU Ý SOFT-DELETE TRONG TEST (giống HrmConfigFilteredUniqueTests):
//   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 SEED row 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 MasterCatalogFilteredUniqueTests
{
    // ---- Department (gotcha #57 — DỰ KIẾN RED) ----

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

        // Seed 1 phòng ban Code="DUP1" đã soft-delete (IsDeleted=true).
        db.Departments.Add(new Department
        {
            Id = Guid.NewGuid(),
            Code = "DUP1",
            Name = "Phòng ban cũ",
            IsDeleted = true,
        });
        await db.SaveChangesAsync(CancellationToken.None);

        // Tạo phòng ban mới CÙNG Code="DUP1". 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
        // migration chưa filter index. Đây là gotcha #57 confirmed.
        var act = async () => await new CreateDepartmentCommandHandler(db)
            .Handle(new CreateDepartmentCommand("DUP1", "Phòng ban mới", null, null, 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 fix migration)");

        (await db.Departments.CountAsync(x => x.Code == "DUP1"))
            .Should().Be(1, "default query filter (!IsDeleted) chỉ trả 1 row active chiếm slot Code");
        (await db.Departments.IgnoreQueryFilters().CountAsync(x => x.Code == "DUP1"))
            .Should().Be(2, "soft-deleted gốc giữ lại cho audit + active mới");
    }

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

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

        // Seed 1 dự án Code="DUP1" đã soft-delete.
        db.Projects.Add(new Project
        {
            Id = Guid.NewGuid(),
            Code = "DUP1",
            Name = "Dự án cũ",
            IsDeleted = true,
        });
        await db.SaveChangesAsync(CancellationToken.None);

        var act = async () => await new CreateProjectCommandHandler(db)
            .Handle(new CreateProjectCommand("DUP1", "Dự án mới", null, null, null, null, null, null, null, null, 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 fix migration)");

        (await db.Projects.CountAsync(x => x.Code == "DUP1"))
            .Should().Be(1, "default query filter (!IsDeleted) chỉ trả 1 row active chiếm slot Code");
        (await db.Projects.IgnoreQueryFilters().CountAsync(x => x.Code == "DUP1"))
            .Should().Be(2, "soft-deleted gốc giữ lại cho audit + active mới");
    }

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

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

        // Seed 1 NCC Code="DUP1" đã soft-delete.
        db.Suppliers.Add(new Supplier
        {
            Id = Guid.NewGuid(),
            Code = "DUP1",
            Name = "NCC cũ",
            Type = SupplierType.NhaCungCap,
            IsDeleted = true,
        });
        await db.SaveChangesAsync(CancellationToken.None);

        var act = async () => await new CreateSupplierCommandHandler(db)
            .Handle(new CreateSupplierCommand("DUP1", "NCC mới", SupplierType.NhaThauPhu,
                null, null, null, null, null, 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 fix migration)");

        (await db.Suppliers.CountAsync(x => x.Code == "DUP1"))
            .Should().Be(1, "default query filter (!IsDeleted) chỉ trả 1 row active chiếm slot Code");
        (await db.Suppliers.IgnoreQueryFilters().CountAsync(x => x.Code == "DUP1"))
            .Should().Be(2, "soft-deleted gốc giữ lại cho audit + active mới");
    }
}
