using Microsoft.Extensions.DependencyInjection;
using SolutionErp.Application.PurchaseEvaluations;
using SolutionErp.Domain.ApprovalWorkflowsV2;
using SolutionErp.Domain.Identity;
using SolutionErp.Domain.Master;
using SolutionErp.Domain.PurchaseEvaluations;
using SolutionErp.Infrastructure.Tests.Common;

namespace SolutionErp.Infrastructure.Tests.Application;

// [S89 anh Kiệt FDC] PE finalize-note (Mig 58 AllowApproverFinalize) — TRANSLATION-SMOKE
// cho subquery FinalizeStepName / FinalizeLevelName trong list projection. Subquery:
// (from lv in db.ApprovalWorkflowLevels where lv.AllowApproverFinalize
// && lv.Step!.ApprovalWorkflowId == e.ApprovalWorkflowId orderby lv.Step!.Order, lv.Order
// select lv.Step!.Name / lv.Name).FirstOrDefault() — PHẢI translate trên EF SQLite (rủi ro
// reviewer cảnh báo). Target = ListApprovedPurchaseEvaluationsQuery (sạch nhất: chỉ
// Phase==DaDuyet, KHÔNG role-paging).
//
// ⚠️ [S97 2026-07-01 SPEC CHANGE — anh Kiệt FDC] EndsBeforeCeo ĐỔI CONFIG-driven → RUNTIME-driven.
//   Bug: workflow CÓ cấp AllowApproverFinalize NHƯNG người duyệt chọn trình tiếp → phiếu LÊN CEO
//   thật → sơ đồ vẫn báo sai "Duyệt là KẾT THÚC, không qua CEO" (vì suy từ config = luôn true).
//   Fix (em-main BE): field PurchaseEvaluation.EndedByLevelFinalize (bool, Mig 60) set true DUY
//   NHẤT ở nhánh level-finalize của ApproveV2Async. ListApproved (DaDuyet-only) đọc THẲNG field
//   này thay 3-subquery-Any cũ (CreateContractFromEvaluationFeatures.cs:196). FinalizeStep/LevelName
//   VẪN từ config-subquery (chỉ nhãn hiển-thị tên cấp finalize, không phải cờ true/false).
//   → test cũ (finalize-config nhưng KHÔNG set field) giờ EndsBeforeCeo=FALSE → seed field=true.
//   → repro case MỚI: config-có-finalize + field=false (đi hết lên CEO) → PHẢI false (chính bug).
// Verify: EF translate (không ném) + config-name first-reachable (Step.Order↑→Level.Order↑) +
//   runtime-field độc-lập-config + V1 null→false.
// IdentityFixture: ApproverUserId là Guid non-null (FK) → cần 1 User row thật.
public class PeFinalizeProjectionTests
{
    private static ApprovalWorkflow BuildWorkflow(
        string code, Guid approverUserId,
        params (string stepName, (string levelName, bool finalize)[] levels)[] steps)
    {
        var wf = new ApprovalWorkflow
        {
            Code = code,
            Version = 1,
            ApplicableType = ApprovalWorkflowApplicableType.DuyetNcc,
            Name = code,
            IsActive = true,
            IsUserSelectable = true,
        };
        for (var s = 0; s < steps.Length; s++)
        {
            var step = new ApprovalWorkflowStep
            {
                ApprovalWorkflowId = wf.Id,
                Order = s + 1,
                Name = steps[s].stepName,
            };
            for (var l = 0; l < steps[s].levels.Length; l++)
            {
                var (lvName, fin) = steps[s].levels[l];
                step.Levels.Add(new ApprovalWorkflowLevel
                {
                    ApprovalWorkflowStepId = step.Id,
                    Order = l + 1,
                    Name = lvName,
                    ApproverUserId = approverUserId,
                    AllowApproverFinalize = fin,
                });
            }
            wf.Steps.Add(step);
        }
        return wf;
    }

    // [S97] endedByLevelFinalize = RUNTIME fact phiếu ĐÃ finalize sớm tại cấp "Duyệt thay CEO"
    // (short-circuit TRƯỚC CEO). ListApproved đọc THẲNG field này (DaDuyet-only) → EndsBeforeCeo.
    // Config "workflow CÓ cấp finalize" (wfId có slot AllowApproverFinalize) KHÔNG còn quyết định.
    private static Guid SeedApprovedPe(
        TestApplicationDbContext db, Guid projectId, Guid? wfId, string code,
        bool endedByLevelFinalize = false)
    {
        var id = Guid.NewGuid();
        db.PurchaseEvaluations.Add(new PurchaseEvaluation
        {
            Id = id,
            MaPhieu = code,
            Type = PurchaseEvaluationType.DuyetNcc,
            Phase = PurchaseEvaluationPhase.DaDuyet,   // lọt ListApproved filter
            TenGoiThau = $"Gói {code}",
            ProjectId = projectId,
            ContractId = null,                          // chưa gen HĐ → lọt picker
            DrafterUserId = null,
            ApprovalWorkflowId = wfId,
            EndedByLevelFinalize = endedByLevelFinalize,
        });
        return id;
    }

    [Fact]
    public async Task ListApproved_FinalizeFields_Translate_RuntimeDriven_ConfigNamesOnly_V1False()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var approver = await fix.CreateUserAsync("fin-approver@test.local", "Approver", null, new[] { AppRoles.CostControl });

        var project = new Project { Id = Guid.NewGuid(), Code = "FINP", Name = "Dự án Finalize" };
        db.Projects.Add(project);

        // Bước 1 (Cấp 1, no-flag) → Bước 2 (Cấp 1, FINALIZE). first-reachable = Bước 2 · Cấp 1.
        // 1 workflow CÓ cấp finalize dùng cho 2 phiếu (khác nhau CHỈ ở runtime field) → chứng
        // minh EndsBeforeCeo lấy từ EndedByLevelFinalize, KHÔNG suy từ config workflow.
        var wfFin = BuildWorkflow("QT-FIN", approver.Id,
            ("Bước 1", new[] { ("Cấp 1", false) }),
            ("Bước 2", new[] { ("Cấp 1", true) }));
        db.ApprovalWorkflows.Add(wfFin);
        // Không cấp nào finalize → config-name null + field mặc-định false.
        var wfPlain = BuildWorkflow("QT-PLAIN", approver.Id, ("Bước 1", new[] { ("Cấp 1", false) }));
        db.ApprovalWorkflows.Add(wfPlain);

        // [S97] peFin: config-có-finalize + phiếu ĐÃ finalize sớm (EndedByLevelFinalize=true) → true.
        var peFin = SeedApprovedPe(db, project.Id, wfFin.Id, "PE-FIN", endedByLevelFinalize: true);
        // 🔴 [S97 REPRO — bug anh Kiệt] CÙNG workflow-có-finalize NHƯNG người duyệt trình tiếp →
        // phiếu LÊN CEO thật (EndedByLevelFinalize=false) → EndsBeforeCeo PHẢI false (không được
        // suy từ config = true như trước fix). Đây là contrast quyết định runtime-vs-config.
        var peFinButWentCeo = SeedApprovedPe(db, project.Id, wfFin.Id, "PE-FIN-CEO", endedByLevelFinalize: false);
        var pePlain = SeedApprovedPe(db, project.Id, wfPlain.Id, "PE-PLAIN");
        var peV1 = SeedApprovedPe(db, project.Id, null, "PE-V1");   // V1 legacy: no workflow
        await db.SaveChangesAsync(CancellationToken.None);

        var handler = new ListApprovedPurchaseEvaluationsQueryHandler(db);
        // Nếu FinalizeStep/LevelName subquery KHÔNG translate → ném InvalidOperationException = FAIL.
        var res = await handler.Handle(new ListApprovedPurchaseEvaluationsQuery(), CancellationToken.None);

        // Phiếu finalize sớm THẬT → true; FinalizeStep/LevelName vẫn từ config (nhãn cấp finalize).
        var fin = res.Single(x => x.Id == peFin);
        fin.EndsBeforeCeo.Should().BeTrue("phiếu ĐÃ kết thúc sớm tại cấp finalize (runtime field true)");
        fin.FinalizeStepName.Should().Be("Bước 2");   // first-reachable bỏ qua Bước 1 no-flag
        fin.FinalizeLevelName.Should().Be("Cấp 1");

        // 🔴 REPRO: cùng config finalize nhưng phiếu đi hết lên CEO → EndsBeforeCeo=false (bug fix).
        var finCeo = res.Single(x => x.Id == peFinButWentCeo);
        finCeo.EndsBeforeCeo.Should().BeFalse(
            "config CÓ cấp finalize nhưng phiếu lên CEO thật (EndedByLevelFinalize=false) → KHÔNG hiện 'không qua CEO'");
        // FinalizeStep/LevelName vẫn phản ánh config (workflow này có cấp finalize) — chỉ EndsBeforeCeo runtime.
        finCeo.FinalizeStepName.Should().Be("Bước 2");
        finCeo.FinalizeLevelName.Should().Be("Cấp 1");

        res.Single(x => x.Id == pePlain).EndsBeforeCeo.Should().BeFalse();
        res.Single(x => x.Id == peV1).EndsBeforeCeo.Should().BeFalse();   // V1 null-workflow tự đúng
    }

    // [S117 anh Kiệt/Kenny — bug "duyệt rồi không bắt lũy kế"] Phiếu CHƯA DaDuyet (ChoDuyet) nay
    // đọc EndsBeforeCeo POINTER-AWARE: chỉ true khi CÒN cấp finalize ở-hoặc-SAU con-trỏ hiện tại
    // (phiếu còn có thể kết thúc sớm). Phiếu đã đi QUA cấp finalize (approver opt-out S97 → trình
    // tiếp CEO) → false. Repro A/018: workflow có cấp finalize (Bước 2) nhưng người duyệt KHÔNG tick
    // "Cấp này KẾT THÚC" → con-trỏ trôi lên Bước 3 (CEO) → TRƯỚC fix config .Any()=true → sơ đồ nói
    // dối "Kết thúc tại CCM" khi phiếu đang nằm inbox CEO. Test qua ListPurchaseEvaluationsQuery
    // (bao gồm ChoDuyet) = cũng verify EF subquery :647 (Step.Order-1 vs CurrentWorkflowStepIndex)
    // translate trên SQLite (rủi ro reviewer). rank cấp finalize = Step.Order-1 (Order tuần-tự s+1).
    private static Guid SeedChoDuyetPe(
        TestApplicationDbContext db, Guid projectId, Guid wfId, string code,
        int stepIdx, int levelOrder)
    {
        var id = Guid.NewGuid();
        db.PurchaseEvaluations.Add(new PurchaseEvaluation
        {
            Id = id,
            MaPhieu = code,
            Type = PurchaseEvaluationType.DuyetNcc,
            Phase = PurchaseEvaluationPhase.ChoDuyet,
            TenGoiThau = $"Gói {code}",
            ProjectId = projectId,
            ApprovalWorkflowId = wfId,
            CurrentWorkflowStepIndex = stepIdx,      // 0-based rank vào steps.OrderBy(Order)
            CurrentApprovalLevelOrder = levelOrder,
            EndedByLevelFinalize = false,            // chưa finalize (đang đi trong quy trình)
        });
        return id;
    }

    [Fact]
    public async Task ListPe_EndsBeforeCeo_ChoDuyet_PointerAware_PastFinalizeIsFalse()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var approver = await fix.CreateUserAsync("s117-appr@test.local", "Approver", null, new[] { AppRoles.CostControl });

        var project = new Project { Id = Guid.NewGuid(), Code = "S117P", Name = "Dự án S117" };
        db.Projects.Add(project);

        // 3 Bước: Bước 1 (no-flag) → Bước 2 (Cấp 1 FINALIZE) → Bước 3 (no-flag = CEO).
        // finalize = step-index 1 (Bước 2), level Order 1.
        var wf = BuildWorkflow("QT-S117", approver.Id,
            ("Bước 1", new[] { ("Cấp 1", false) }),
            ("Bước 2", new[] { ("Cấp 1", true) }),
            ("Bước 3", new[] { ("Cấp 1", false) }));
        db.ApprovalWorkflows.Add(wf);

        // con-trỏ AT Bước 1 (index 0) — TRƯỚC finalize → còn có thể kết thúc sớm → TRUE (heads-up giữ).
        var peBefore = SeedChoDuyetPe(db, project.Id, wf.Id, "PE-BEFORE", stepIdx: 0, levelOrder: 1);
        // con-trỏ AT cấp finalize (Bước 2 index 1 · Cấp 1) — còn có thể finalize → TRUE.
        var peAtFin = SeedChoDuyetPe(db, project.Id, wf.Id, "PE-AT", stepIdx: 1, levelOrder: 1);
        // 🔴 REPRO A/018: con-trỏ QUA finalize (Bước 3 index 2) — approver opt-out → lên CEO → FALSE.
        var pePast = SeedChoDuyetPe(db, project.Id, wf.Id, "PE-PAST", stepIdx: 2, levelOrder: 1);
        await db.SaveChangesAsync(CancellationToken.None);

        // Admin bypass IDOR scope (ListPurchaseEvaluations :576). UserId bất-kỳ (không persist FK).
        var admin = new TestCurrentUser(Guid.NewGuid(), "Admin", null, AppRoles.Admin);
        var handler = new ListPurchaseEvaluationsQueryHandler(db, admin);
        // EF subquery :647 (Step.Order-1 vs CurrentWorkflowStepIndex) PHẢI translate — không ném = pass smoke.
        var res = await handler.Handle(new ListPurchaseEvaluationsQuery(), CancellationToken.None);

        res.Items.Single(x => x.Id == peBefore).EndsBeforeCeo.Should().BeTrue(
            "con-trỏ TRƯỚC cấp finalize → phiếu còn có thể kết thúc sớm → giữ heads-up");
        res.Items.Single(x => x.Id == peAtFin).EndsBeforeCeo.Should().BeTrue(
            "con-trỏ ĐANG ở cấp finalize → còn có thể tick KẾT THÚC → true");
        res.Items.Single(x => x.Id == pePast).EndsBeforeCeo.Should().BeFalse(
            "🔴 A/018: con-trỏ đã QUA cấp finalize (đang chờ CEO) → KHÔNG được nói dối 'Kết thúc tại CCM'");
    }
}
