using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using SolutionErp.Application.Common.Interfaces;
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;

// Plan N S23 t4 (2026-05-15) — Regression test cho BE bug per-NV lookup site
// PurchaseEvaluationFeatures.cs:765. Schema Mig 29 (S21 t5) refactor:
// 1 ApprovalWorkflowLevel row per ApproverUserId (OR-of-N cùng Order).
// Bug: handler chỉ FirstOrDefault(Order==X) thiếu discriminator ApproverUserId
// → luôn lấy row đầu DB → bỏ qua admin tick per-NV của actor thật.
//
// Fix line 765: thêm `l.ApproverUserId == currentUser.UserId` match +
// fallback row đầu cho admin / non-approver view detail.
public class GetPurchaseEvaluationCurrentLevelOptionsTests
{
    private sealed class FakeCurrentUser : ICurrentUser
    {
        public Guid? UserId { get; init; }
        public string? Email { get; init; }
        public string? FullName { get; init; }
        public IReadOnlyList<string> Roles { get; init; } = Array.Empty<string>();
        public bool IsAuthenticated => UserId is not null;
    }

    private static async Task<(ApprovalWorkflow wf, List<ApprovalWorkflowLevel> levels)>
        SeedWorkflowWith4SlotsAsync(
            TestApplicationDbContext db,
            Guid approverAId, Guid approverBId, Guid approverCId, Guid approverDId)
    {
        var wf = new ApprovalWorkflow
        {
            Id = Guid.NewGuid(),
            Code = "QT-N-001",
            Version = 1,
            Name = "Plan N regression workflow",
            ApplicableType = ApprovalWorkflowApplicableType.DuyetNcc,
            IsActive = true,
            IsUserSelectable = true,
        };
        var step = new ApprovalWorkflowStep
        {
            Id = Guid.NewGuid(),
            ApprovalWorkflowId = wf.Id,
            Order = 1,
            Name = "Bước 1",
        };

        // 4 Level cùng Order=1, mỗi Level approverUserId khác nhau, FLAG khác nhau
        // theo matrix bug bro phát hiện 2026-05-15:
        //   Level A (row đầu) — Drafter only TRUE, 6 flag khác FALSE
        //   Level B — OneLevel TRUE
        //   Level C — SkipToFinal TRUE
        //   Level D — EditBudget TRUE
        var levelA = new ApprovalWorkflowLevel
        {
            Id = Guid.NewGuid(),
            ApprovalWorkflowStepId = step.Id,
            Order = 1,
            ApproverUserId = approverAId,
            AllowReturnToDrafter = true,
        };
        // Note: Mig 29 set HasDefaultValue(true) cho AllowReturnToDrafter — phải
        // explicit FALSE cho Level B/C/D để test discrimination chuẩn xác.
        var levelB = new ApprovalWorkflowLevel
        {
            Id = Guid.NewGuid(),
            ApprovalWorkflowStepId = step.Id,
            Order = 1,
            ApproverUserId = approverBId,
            AllowReturnOneLevel = true,
            AllowReturnToDrafter = false,
        };
        var levelC = new ApprovalWorkflowLevel
        {
            Id = Guid.NewGuid(),
            ApprovalWorkflowStepId = step.Id,
            Order = 1,
            ApproverUserId = approverCId,
            AllowApproverSkipToFinal = true,
            AllowReturnToDrafter = false,
        };
        var levelD = new ApprovalWorkflowLevel
        {
            Id = Guid.NewGuid(),
            ApprovalWorkflowStepId = step.Id,
            Order = 1,
            ApproverUserId = approverDId,
            AllowApproverEditBudget = true,
            AllowReturnToDrafter = false,
        };

        db.ApprovalWorkflows.Add(wf);
        db.ApprovalWorkflowSteps.Add(step);
        db.ApprovalWorkflowLevels.AddRange(levelA, levelB, levelC, levelD);
        await db.SaveChangesAsync(CancellationToken.None);

        return (wf, new List<ApprovalWorkflowLevel> { levelA, levelB, levelC, levelD });
    }

    private static async Task<PurchaseEvaluation> SeedPeAsync(
        TestApplicationDbContext db, Guid drafterId, Guid awId)
    {
        var project = new Project
        {
            Id = Guid.NewGuid(),
            Code = "PRJ-N-001",
            Name = "Plan N test project",
        };
        db.Projects.Add(project);

        var pe = new PurchaseEvaluation
        {
            Id = Guid.NewGuid(),
            Type = PurchaseEvaluationType.DuyetNcc,
            Phase = PurchaseEvaluationPhase.ChoDuyet,
            MaPhieu = "PE-N-001",
            TenGoiThau = "Plan N regression test",
            ProjectId = project.Id,
            DrafterUserId = drafterId,
            ApprovalWorkflowId = awId,
            CurrentWorkflowStepIndex = 0,
            CurrentApprovalLevelOrder = 1,
        };
        db.PurchaseEvaluations.Add(pe);
        await db.SaveChangesAsync(CancellationToken.None);
        return pe;
    }

    private static GetPurchaseEvaluationQueryHandler BuildHandler(
        IdentityFixture fix, FakeCurrentUser actor)
    {
        return new GetPurchaseEvaluationQueryHandler(
            fix.Services.GetRequiredService<IApplicationDbContext>(),
            fix.Services.GetRequiredService<UserManager<User>>(),
            actor);
    }

    [Fact]
    public async Task GetPe_PerNvLookup_ActorMatchesSlot_ReturnsActorSpecificFlags()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var drafter = await fix.CreateUserAsync("drafter-n@test.local", "Drafter N", null, new[] { AppRoles.Drafter });
        var a = await fix.CreateUserAsync("a@test.local", "Approver A", null, new[] { AppRoles.CostControl });
        var b = await fix.CreateUserAsync("b@test.local", "Approver B", null, new[] { AppRoles.CostControl });
        var c = await fix.CreateUserAsync("c@test.local", "Approver C", null, new[] { AppRoles.CostControl });
        var d = await fix.CreateUserAsync("d@test.local", "Approver D", null, new[] { AppRoles.CostControl });
        var (wf, _) = await SeedWorkflowWith4SlotsAsync(db, a.Id, b.Id, c.Id, d.Id);
        var pe = await SeedPeAsync(db, drafter.Id, wf.Id);

        // Actor A → flag-set A: AllowReturnToDrafter=true only
        var resA = await BuildHandler(fix, new FakeCurrentUser
        { UserId = a.Id, Roles = new[] { AppRoles.CostControl } })
            .Handle(new GetPurchaseEvaluationQuery(pe.Id), CancellationToken.None);
        resA.CurrentLevelOptions.Should().NotBeNull();
        resA.CurrentLevelOptions!.AllowReturnToDrafter.Should().BeTrue("Actor A tick AllowReturnToDrafter");
        resA.CurrentLevelOptions.AllowReturnOneLevel.Should().BeFalse();
        resA.CurrentLevelOptions.AllowApproverSkipToFinal.Should().BeFalse();
        resA.CurrentLevelOptions.AllowApproverEditBudget.Should().BeFalse();

        // Actor B → flag-set B: AllowReturnOneLevel=true (KHÔNG phải A's flag)
        var resB = await BuildHandler(fix, new FakeCurrentUser
        { UserId = b.Id, Roles = new[] { AppRoles.CostControl } })
            .Handle(new GetPurchaseEvaluationQuery(pe.Id), CancellationToken.None);
        resB.CurrentLevelOptions!.AllowReturnOneLevel.Should().BeTrue("Actor B tick AllowReturnOneLevel");
        resB.CurrentLevelOptions.AllowReturnToDrafter.Should().BeFalse();
        resB.CurrentLevelOptions.AllowApproverSkipToFinal.Should().BeFalse();

        // Actor C → flag-set C: AllowApproverSkipToFinal=true (regression bug bro UAT chính)
        var resC = await BuildHandler(fix, new FakeCurrentUser
        { UserId = c.Id, Roles = new[] { AppRoles.CostControl } })
            .Handle(new GetPurchaseEvaluationQuery(pe.Id), CancellationToken.None);
        resC.CurrentLevelOptions!.AllowApproverSkipToFinal.Should().BeTrue(
            "Actor C tick AllowApproverSkipToFinal — regression bug bro phát hiện 2026-05-15 UAT");
        resC.CurrentLevelOptions.AllowReturnToDrafter.Should().BeFalse();

        // Actor D → flag-set D: AllowApproverEditBudget=true
        var resD = await BuildHandler(fix, new FakeCurrentUser
        { UserId = d.Id, Roles = new[] { AppRoles.CostControl } })
            .Handle(new GetPurchaseEvaluationQuery(pe.Id), CancellationToken.None);
        resD.CurrentLevelOptions!.AllowApproverEditBudget.Should().BeTrue("Actor D tick AllowApproverEditBudget");
        resD.CurrentLevelOptions.AllowApproverSkipToFinal.Should().BeFalse();
    }

    [Fact]
    public async Task GetPe_PerNvLookup_AdminNonApprover_FallsBackToFirstRow()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var drafter = await fix.CreateUserAsync("drafter-n2@test.local", "Drafter N2", null, new[] { AppRoles.Drafter });
        var a = await fix.CreateUserAsync("a2@test.local", "Approver A2", null, new[] { AppRoles.CostControl });
        var b = await fix.CreateUserAsync("b2@test.local", "Approver B2", null, new[] { AppRoles.CostControl });
        var c = await fix.CreateUserAsync("c2@test.local", "Approver C2", null, new[] { AppRoles.CostControl });
        var d = await fix.CreateUserAsync("d2@test.local", "Approver D2", null, new[] { AppRoles.CostControl });
        var admin = await fix.CreateUserAsync("admin-n@test.local", "Admin N", null, new[] { AppRoles.Admin });
        var (wf, levels) = await SeedWorkflowWith4SlotsAsync(db, a.Id, b.Id, c.Id, d.Id);
        var pe = await SeedPeAsync(db, drafter.Id, wf.Id);

        // Admin actor (KHÔNG match approver slot nào) → fallback FirstOrDefault
        // theo internal EF SQLite ordering (non-deterministic Guid Id).
        // Verify: fallback hoạt động (KHÔNG null) + match 1 trong 4 profile
        // distinct (mỗi Level distinct flag → fallback pick exactly 1 profile).
        var resAdmin = await BuildHandler(fix, new FakeCurrentUser
        { UserId = admin.Id, Roles = new[] { AppRoles.Admin } })
            .Handle(new GetPurchaseEvaluationQuery(pe.Id), CancellationToken.None);
        resAdmin.CurrentLevelOptions.Should().NotBeNull(
            "Admin/non-approver fallback row đầu — giữ behavior view detail (KHÔNG null)");
        // Verify fallback đúng 1 profile (mỗi Level distinct flag):
        //   Level A: AllowReturnToDrafter=true
        //   Level B: AllowReturnOneLevel=true
        //   Level C: AllowApproverSkipToFinal=true
        //   Level D: AllowApproverEditBudget=true
        var flagCount = (resAdmin.CurrentLevelOptions!.AllowReturnToDrafter ? 1 : 0)
            + (resAdmin.CurrentLevelOptions.AllowReturnOneLevel ? 1 : 0)
            + (resAdmin.CurrentLevelOptions.AllowApproverSkipToFinal ? 1 : 0)
            + (resAdmin.CurrentLevelOptions.AllowApproverEditBudget ? 1 : 0);
        flagCount.Should().Be(1,
            "Admin fallback pick exactly 1 trong 4 distinct Level profile");
    }
}
