using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using SolutionErp.Application.Common.Interfaces;
using SolutionErp.Application.PurchaseEvaluations;
using SolutionErp.Domain.ApprovalWorkflowsV2;
using SolutionErp.Domain.PurchaseEvaluations;
using SolutionErp.Infrastructure.Tests.Common;

namespace SolutionErp.Infrastructure.Tests.Application;

// S85 TEST-AFTER cho ROOT FIX bug-class S42 (anh Kiệt FDC UAT R5).
// BUG (đã fix em-main): form Sửa header CLEAR Quy trình duyệt của phiếu.
//   Trước: entity.ApprovalWorkflowId = request.ApprovalWorkflowId   (ABSOLUTE-SET →
//          FE form Sửa KHÔNG gửi field → request.ApprovalWorkflowId = null → CLEAR WF
//          → kết submit "chưa chọn quy trình" = whack-a-mole bug-class S42).
//   Sau (fix):  if (request.ApprovalWorkflowId is not null)
//                   entity.ApprovalWorkflowId = request.ApprovalWorkflowId;
//          → client không gửi (null) GIỮ WF cũ; CHỈ đổi khi FE gửi giá trị thật.
//
// Test theo CODE (single source of truth, S34) — PurchaseEvaluationFeatures.cs:
//   - Line 285-286: null-safe guard (giống BudgetPeriodAmount/ExpectedRemaining/WorkItemId).
//   - Line 253-266: KHI awId != entity.ApprovalWorkflowId (tức CÓ đổi) → validate WF
//     tồn tại + ApplicableType khớp Type phiếu. PRESERVE case (request null) SHORT-CIRCUIT
//     trước validate đó (vì `is not null` false) → KHÔNG cần seed WF cho case PRESERVE.
//
// Handler: UpdatePurchaseEvaluationDraftCommandHandler(IApplicationDbContext db, ICurrentUser).
// Phase guard: chỉ DangSoanThao / TraLai. Changelog Add → cần IdentityFixture (SaveChanges).
public class PeUpdateDraftWorkflowPreserveTests
{
    private sealed class FakeCurrentUser : ICurrentUser
    {
        public Guid? UserId { get; init; } = Guid.NewGuid();
        public string? Email { get; init; }
        public string? FullName { get; init; } = "Người soạn";
        public IReadOnlyList<string> Roles { get; init; } = System.Array.Empty<string>();
        public bool IsAuthenticated => UserId is not null;
    }

    // Seed 1 PE Nháp với ApprovalWorkflowId gán sẵn (mô phỏng phiếu ĐÃ chọn WF).
    private static PurchaseEvaluation BuildPe(
        Guid? approvalWorkflowId,
        decimal? budgetPeriodAmount = null,
        string code = "PE-WFP-001")
        => new()
        {
            Id = Guid.NewGuid(),
            Type = PurchaseEvaluationType.DuyetNcc,
            Phase = PurchaseEvaluationPhase.DangSoanThao,
            MaPhieu = code,
            TenGoiThau = "Gói thầu test",
            ProjectId = Guid.NewGuid(),
            DrafterUserId = Guid.NewGuid(),
            ApprovalWorkflowId = approvalWorkflowId,
            BudgetPeriodAmount = budgetPeriodAmount,
        };

    // Seed 1 ApprovalWorkflow V2 thật, ApplicableType=DuyetNcc (khớp PE Type) để pass
    // validate block khi đổi WF (CHANGE case). Trả về Id.
    private static async Task<Guid> SeedWorkflowAsync(TestApplicationDbContext db, string code)
    {
        var wf = new ApprovalWorkflow
        {
            Id = Guid.NewGuid(),
            Code = code,
            Version = 1,
            ApplicableType = ApprovalWorkflowApplicableType.DuyetNcc,
            Name = code,
            IsActive = true,
            IsUserSelectable = true,
        };
        db.ApprovalWorkflows.Add(wf);
        await db.SaveChangesAsync(CancellationToken.None);
        return wf.Id;
    }

    // ============================================================
    // CASE 1 — PRESERVE (cốt lõi bug-class S42, reproduce + verify fix)
    // ============================================================

    [Fact]
    public async Task UpdateDraft_NullApprovalWorkflowId_PreservesExistingWorkflow()
    {
        // Phiếu Nháp CÓ ApprovalWorkflowId=W → UpdateDraft với ApprovalWorkflowId=null
        // (FE form Sửa header KHÔNG gửi field) → entity.ApprovalWorkflowId PHẢI VẪN == W.
        // TRƯỚC FIX: absolute-set null → W bị clear (= bug). SAU FIX: null-safe → giữ W.
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();

        // PE.ApprovalWorkflowId là FK Restrict → seed WF thật (case này request null nên
        // KHÔNG đi vào validate block, nhưng PE seed vẫn cần FK hợp lệ).
        var w = await SeedWorkflowAsync(db, "QT-DN-V2-PRESERVE");
        var pe = BuildPe(approvalWorkflowId: w, code: "PE-WFP-PRESERVE");
        db.PurchaseEvaluations.Add(pe);
        await db.SaveChangesAsync(CancellationToken.None);

        // Update header KHÔNG kèm ApprovalWorkflowId (default null) — các field khác có giá trị.
        var cmd = new UpdatePurchaseEvaluationDraftCommand(
            pe.Id,
            TenGoiThau: "Gói thầu đã sửa",
            DiaDiem: "Công trường A",
            MoTa: "Mô tả mới",
            PaymentTerms: "30 ngày");   // ApprovalWorkflowId / WorkItemId / Budget* để default null
        var handler = new UpdatePurchaseEvaluationDraftCommandHandler(db, new FakeCurrentUser());
        await handler.Handle(cmd, CancellationToken.None);

        var reload = await db.PurchaseEvaluations.AsNoTracking().FirstAsync(x => x.Id == pe.Id);
        reload.ApprovalWorkflowId.Should().Be(w,
            "form Sửa KHÔNG gửi approvalWorkflowId (null) → null-safe guard GIỮ WF cũ (root fix bug-class S42)");
        // Sanity: field text vẫn cập nhật bình thường (handler vẫn chạy, không phải no-op).
        reload.TenGoiThau.Should().Be("Gói thầu đã sửa");
        reload.DiaDiem.Should().Be("Công trường A");
    }

    // ============================================================
    // CASE 2 — CHANGE (đổi WF khi CÓ gửi giá trị thật)
    // ============================================================

    [Fact]
    public async Task UpdateDraft_NonNullApprovalWorkflowId_ChangesToNewWorkflow()
    {
        // Phiếu có WF=W1 → UpdateDraft với ApprovalWorkflowId=W2 (W2 != null) →
        // entity.ApprovalWorkflowId == W2. CHO PHÉP đổi khi FE gửi giá trị.
        // W2 phải là WF thật ApplicableType=DuyetNcc để pass validate block (line 253-266).
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();

        var w1 = await SeedWorkflowAsync(db, "QT-DN-V2-W1");
        var w2 = await SeedWorkflowAsync(db, "QT-DN-V2-W2");

        var pe = BuildPe(approvalWorkflowId: w1, code: "PE-WFP-CHANGE");
        db.PurchaseEvaluations.Add(pe);
        await db.SaveChangesAsync(CancellationToken.None);

        var cmd = new UpdatePurchaseEvaluationDraftCommand(
            pe.Id,
            TenGoiThau: "Gói thầu test",
            DiaDiem: null,
            MoTa: null,
            PaymentTerms: null,
            ApprovalWorkflowId: w2);   // FE GỬI W2 → đổi
        var handler = new UpdatePurchaseEvaluationDraftCommandHandler(db, new FakeCurrentUser());
        await handler.Handle(cmd, CancellationToken.None);

        var reload = await db.PurchaseEvaluations.AsNoTracking().FirstAsync(x => x.Id == pe.Id);
        reload.ApprovalWorkflowId.Should().Be(w2,
            "FE gửi ApprovalWorkflowId không-null → cho phép đổi WF (null-safe guard chỉ chặn null, không chặn đổi)");
    }

    // ============================================================
    // CASE 3 (bonus) — mirror null-safe field BudgetPeriodAmount đã có (S61)
    // ============================================================

    [Fact]
    public async Task UpdateDraft_NullBudgetPeriodAmount_PreservesExistingBudget()
    {
        // Cùng convention null-safe S42 (line 289-290): phiếu có BudgetPeriodAmount=100tr,
        // UpdateDraft không gửi (null) → GIỮ 100tr (KHÔNG null-hóa). Đối chứng cho thấy
        // ApprovalWorkflowId nay đã CÙNG pattern với Budget* (đồng nhất bug-class S42).
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();

        const decimal budget = 100_000_000m;
        var w = await SeedWorkflowAsync(db, "QT-DN-V2-BUDGET");   // FK Restrict → WF thật
        var pe = BuildPe(approvalWorkflowId: w, budgetPeriodAmount: budget, code: "PE-WFP-BUDGET");
        db.PurchaseEvaluations.Add(pe);
        await db.SaveChangesAsync(CancellationToken.None);

        var cmd = new UpdatePurchaseEvaluationDraftCommand(
            pe.Id,
            TenGoiThau: "Gói thầu test",
            DiaDiem: null,
            MoTa: null,
            PaymentTerms: null);   // BudgetPeriodAmount default null → giữ
        var handler = new UpdatePurchaseEvaluationDraftCommandHandler(db, new FakeCurrentUser());
        await handler.Handle(cmd, CancellationToken.None);

        var reload = await db.PurchaseEvaluations.AsNoTracking().FirstAsync(x => x.Id == pe.Id);
        reload.BudgetPeriodAmount.Should().Be(budget,
            "BudgetPeriodAmount null-safe (S61) — null request GIỮ giá trị cũ; cùng pattern ApprovalWorkflowId (S85)");
    }
}
