using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using SolutionErp.Domain.ApprovalWorkflowsV2;
using SolutionErp.Domain.Common;
using SolutionErp.Domain.Contracts;
using SolutionErp.Domain.Identity;
using SolutionErp.Domain.Master;

namespace SolutionErp.Infrastructure.Tests.Common;

// Plan C B-Wrap BW6 (S33) — ContractLevelOpinion schema persistence verification
// (Mig 33 — cookie-cutter mirror PE Mig 26).
//
// 3 assertion:
//   1. UNIQUE composite (ContractId, ApprovalWorkflowLevelId) — không cho 2 row
//      cùng (HĐ, Level slot) → DbUpdateException khi cố insert duplicate.
//   2. UPSERT pattern fetch + update — Comment thay đổi, vẫn 1 row only.
//   3. FK Cascade Contract — xoá HĐ → ContractLevelOpinions auto-deleted.
//
// FK Restrict ApprovalWorkflowLevel KHÔNG test (per spec):
//   - Admin xoá Level chặn nếu opinion tồn tại (data preservation guarantee)
//   - Verify ngoài qua manual SQL hoặc integration test riêng.
public class ContractV2SchemaPersistenceTests
{
    [Fact]
    public async Task ContractLevelOpinion_DuplicateComposite_ThrowsDbUpdateException()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();

        var approver = await fix.CreateUserAsync("a-bw6@test.local", "Approver BW6",
            departmentId: null, roles: new[] { AppRoles.CostControl });

        // Seed 1 Workflow + 1 Step + 1 Level
        var wf = new ApprovalWorkflow
        {
            Id = Guid.NewGuid(),
            Code = "QT-BW6-001",
            Version = 1,
            Name = "BW6 unique test",
            ApplicableType = ApprovalWorkflowApplicableType.Contract,
            IsActive = true,
            IsUserSelectable = true,
        };
        var step = new ApprovalWorkflowStep
        {
            Id = Guid.NewGuid(),
            ApprovalWorkflowId = wf.Id,
            Order = 1,
            DepartmentId = null,
            Name = "Bước 1",
        };
        var level = new ApprovalWorkflowLevel
        {
            Id = Guid.NewGuid(),
            ApprovalWorkflowStepId = step.Id,
            Order = 1,
            ApproverUserId = approver.Id,
        };
        db.ApprovalWorkflows.Add(wf);
        db.ApprovalWorkflowSteps.Add(step);
        db.ApprovalWorkflowLevels.Add(level);

        // Seed Supplier + Project + Contract V2
        var sup = new Supplier { Id = Guid.NewGuid(), Code = "ABC", Name = "NCC ABC", Type = SupplierType.NhaThauPhu };
        var proj = new Project { Id = Guid.NewGuid(), Code = "PROJ01", Name = "Dự án 01" };
        db.Suppliers.Add(sup);
        db.Projects.Add(proj);

        var contract = new Contract
        {
            Id = Guid.NewGuid(),
            Type = ContractType.HopDongThauPhu,
            Phase = ContractPhase.ChoDuyet,
            SupplierId = sup.Id,
            ProjectId = proj.Id,
            DrafterUserId = Guid.NewGuid(),
            TenHopDong = "Test BW6 unique",
            GiaTri = 10_000_000m,
            ApprovalWorkflowId = wf.Id,
            CurrentWorkflowStepIndex = 0,
            CurrentApprovalLevelOrder = 1,
        };
        db.Contracts.Add(contract);
        await db.SaveChangesAsync(CancellationToken.None);

        // 2 ContractLevelOpinion cùng (ContractId, ApprovalWorkflowLevelId)
        db.ContractLevelOpinions.Add(new ContractLevelOpinion
        {
            ContractId = contract.Id,
            ApprovalWorkflowLevelId = level.Id,
            Comment = "Ý kiến lần 1",
            SignedAt = DateTime.UtcNow,
            SignedByUserId = approver.Id,
            SignedByFullName = "Approver BW6",
        });
        db.ContractLevelOpinions.Add(new ContractLevelOpinion
        {
            ContractId = contract.Id,
            ApprovalWorkflowLevelId = level.Id,
            Comment = "Ý kiến lần 2 — duplicate",
            SignedAt = DateTime.UtcNow,
            SignedByUserId = approver.Id,
            SignedByFullName = "Approver BW6",
        });

        var act = async () => await db.SaveChangesAsync(CancellationToken.None);
        await act.Should().ThrowAsync<DbUpdateException>(
            "UNIQUE composite (ContractId, ApprovalWorkflowLevelId) — Mig 33");
    }

    [Fact]
    public async Task ContractLevelOpinion_UpsertPattern_FetchAndUpdate_KeepsSingleRow()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();

        var approver = await fix.CreateUserAsync("a-bw6-up@test.local", "Approver BW6 UP",
            departmentId: null, roles: new[] { AppRoles.CostControl });

        var (wf, step, level, sup, proj, contract) = await SeedBaseAsync(db, approver.Id);

        // Initial insert
        db.ContractLevelOpinions.Add(new ContractLevelOpinion
        {
            ContractId = contract.Id,
            ApprovalWorkflowLevelId = level.Id,
            Comment = "Ý kiến đầu tiên",
            SignedAt = DateTime.UtcNow,
            SignedByUserId = approver.Id,
            SignedByFullName = "Approver BW6 UP",
        });
        await db.SaveChangesAsync(CancellationToken.None);

        // UPSERT — fetch + update
        var existing = await db.ContractLevelOpinions
            .FirstAsync(o => o.ContractId == contract.Id && o.ApprovalWorkflowLevelId == level.Id);
        existing.Comment = "Ý kiến đã được cập nhật";
        existing.SignedAt = DateTime.UtcNow.AddSeconds(1);
        await db.SaveChangesAsync(CancellationToken.None);

        var rows = await db.ContractLevelOpinions
            .Where(o => o.ContractId == contract.Id).ToListAsync();
        rows.Should().HaveCount(1, "UPSERT giữ duy nhất 1 row per (HĐ, Level)");
        rows[0].Comment.Should().Be("Ý kiến đã được cập nhật");
    }

    [Fact]
    public async Task ContractLevelOpinion_FkCascade_DeleteContract_AlsoDeletesOpinions()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();

        var approver = await fix.CreateUserAsync("a-bw6-cas@test.local", "Approver BW6 CAS",
            departmentId: null, roles: new[] { AppRoles.CostControl });

        var (wf, step, level, sup, proj, contract) = await SeedBaseAsync(db, approver.Id);

        db.ContractLevelOpinions.Add(new ContractLevelOpinion
        {
            ContractId = contract.Id,
            ApprovalWorkflowLevelId = level.Id,
            Comment = "Sẽ bị wipe khi xoá HĐ",
            SignedAt = DateTime.UtcNow,
            SignedByUserId = approver.Id,
            SignedByFullName = "Approver BW6 CAS",
        });
        await db.SaveChangesAsync(CancellationToken.None);

        var beforeCount = await db.ContractLevelOpinions
            .CountAsync(o => o.ContractId == contract.Id);
        beforeCount.Should().Be(1);

        // Hard delete Contract — Cascade wipe opinions
        db.Contracts.Remove(contract);
        await db.SaveChangesAsync(CancellationToken.None);

        var afterCount = await db.ContractLevelOpinions
            .CountAsync(o => o.ContractId == contract.Id);
        afterCount.Should().Be(0, "FK Cascade Mig 33 — xoá HĐ wipe ContractLevelOpinions");
    }

    // Helper seed full chain: Workflow + Step + Level + Supplier + Project + Contract.
    private static async Task<(ApprovalWorkflow wf, ApprovalWorkflowStep step,
        ApprovalWorkflowLevel level, Supplier sup, Project proj, Contract contract)>
        SeedBaseAsync(TestApplicationDbContext db, Guid approverUserId)
    {
        var wf = new ApprovalWorkflow
        {
            Id = Guid.NewGuid(),
            Code = $"QT-BW6-{Guid.NewGuid().ToString()[..6]}",
            Version = 1,
            Name = "BW6 helper",
            ApplicableType = ApprovalWorkflowApplicableType.Contract,
            IsActive = true,
            IsUserSelectable = true,
        };
        var step = new ApprovalWorkflowStep
        {
            Id = Guid.NewGuid(),
            ApprovalWorkflowId = wf.Id,
            Order = 1,
            DepartmentId = null,
            Name = "Bước 1",
        };
        var level = new ApprovalWorkflowLevel
        {
            Id = Guid.NewGuid(),
            ApprovalWorkflowStepId = step.Id,
            Order = 1,
            ApproverUserId = approverUserId,
        };
        var sup = new Supplier { Id = Guid.NewGuid(), Code = "ABC", Name = "NCC ABC", Type = SupplierType.NhaThauPhu };
        var proj = new Project { Id = Guid.NewGuid(), Code = "PROJ01", Name = "Dự án 01" };
        var contract = new Contract
        {
            Id = Guid.NewGuid(),
            Type = ContractType.HopDongThauPhu,
            Phase = ContractPhase.ChoDuyet,
            SupplierId = sup.Id,
            ProjectId = proj.Id,
            DrafterUserId = Guid.NewGuid(),
            TenHopDong = "BW6 helper contract",
            GiaTri = 10_000_000m,
            ApprovalWorkflowId = wf.Id,
            CurrentWorkflowStepIndex = 0,
            CurrentApprovalLevelOrder = 1,
        };
        db.ApprovalWorkflows.Add(wf);
        db.ApprovalWorkflowSteps.Add(step);
        db.ApprovalWorkflowLevels.Add(level);
        db.Suppliers.Add(sup);
        db.Projects.Add(proj);
        db.Contracts.Add(contract);
        await db.SaveChangesAsync(CancellationToken.None);
        return (wf, step, level, sup, proj, contract);
    }
}
