using FluentValidation;
using MediatR;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using SolutionErp.Application.Common.Interfaces;
using SolutionErp.Domain.ApprovalWorkflowsV2;
using SolutionErp.Domain.Identity;

namespace SolutionErp.Application.ApprovalWorkflowsV2;

// Quy trình duyệt MỚI (Mig 22 — Session 17, 2026-05-08).
// Schema riêng để UAT trước khi drop legacy WorkflowDefinition cũ.
//
// Cấu trúc:
//   Quy trình (Code + Name + ApplicableType)
//     Bước 1 - Phòng A
//       Cấp 1 - NV X (ApproverUserId 1 user cụ thể)
//       Cấp 2 - NV Y
//
// Khác Mig 21: Levels match 1 NV CHÍNH XÁC qua ApproverUserId, không match
// group Dept+PositionLevel/Role/User.

public record AwLevelDto(
    Guid Id,
    int Order,
    string? Name,
    Guid ApproverUserId,
    string? ApproverUserName,
    string? ApproverEmail,
    // Mig 29 (S21 t5) — 5 advanced options per slot Approver (F1 mode Trả lại
    // + F3 Edit Section 2). Mig 30 (S22+5) — F4 +AllowApproverEditBudget.
    // Mig 31 (S23 t1) — F2 +AllowApproverSkipToFinal (refactor sang Approver scope ChoDuyet).
    // Mỗi NV trong workflow có quyền riêng — 7 Allow* cumulative.
    bool AllowReturnOneLevel,
    bool AllowReturnOneStep,
    bool AllowReturnToAssignee,
    bool AllowReturnToDrafter,
    bool AllowApproverEditDetails,
    bool AllowApproverEditBudget,
    bool AllowApproverSkipToFinal,
    bool AllowApproverFinalize,                  // [Mig 58] slot này Approve = KẾT THÚC (không trình CEO)
    // [S76] Hiển thị-only: approver này được nhập/điều chỉnh ngân sách cột nào (suy
    // từ ROLE — KHÔNG đổi quyền). CanEditProBudget = Admin|Procurement (cột PRO);
    // CanEditCcmBudget = Admin|CostControl (cột CCM). Badge "✎ NS PRO/CCM" trong Designer.
    bool CanEditProBudget,
    bool CanEditCcmBudget);

public record AwStepDto(
    Guid Id,
    int Order,
    string Name,
    Guid? DepartmentId,
    string? DepartmentName,
    List<AwLevelDto> Levels);

public record AwDefinitionDto(
    Guid Id,
    string Code,
    int Version,
    int ApplicableType,
    string ApplicableTypeLabel,
    string Name,
    string? Description,
    bool IsActive,
    bool IsUserSelectable,
    // [S69 2026-06-17] Ngưỡng giá trị "gói CEO" — admin Designer hiển thị + edit.
    // Null = không áp ngưỡng (luồng tuyến tính cũ).
    decimal? CeoApprovalThreshold,
    // Mig 29 (S21 t5) — 6 advanced options đã MOVE per-NV: F1+F3 xuống AwLevelDto
    // (per slot Approver). Workflow-level Mig 28 dropped.
    // Mig 31 (S23 t1) — F2 cũng refactor xuống Level slot (AllowApproverSkipToFinal
    // trong AwLevelDto) per-Approver scope ChoDuyet — KHÔNG còn per-Drafter user scope.
    DateTime? ActivatedAt,
    DateTime CreatedAt,
    List<AwStepDto> Steps);

public record AwTypeSummaryDto(
    int ApplicableType,
    string ApplicableTypeLabel,
    AwDefinitionDto? Active,
    List<AwDefinitionDto> History);

public record AwAdminOverviewDto(List<AwTypeSummaryDto> Types);

internal static class AwLabels
{
    public static readonly Dictionary<ApprovalWorkflowApplicableType, string> Type = new()
    {
        [ApprovalWorkflowApplicableType.DuyetNcc] = "Duyệt NCC",
        [ApprovalWorkflowApplicableType.DuyetNccPhuongAn] = "Duyệt NCC + Giải pháp",
        [ApprovalWorkflowApplicableType.Contract] = "Hợp đồng",
    };
}

// ========== GET overview ==========
// Filter `applicableType=null` → return tất cả, `=N` → chỉ type đó.

// [Plan AA S24 t1] `IsUserSelectable` filter cho user read-only matrix view —
// chỉ workflow ghim (Mig 25 admin pin). Admin Designer KHÔNG truyền param →
// return tất cả như cũ.
public record GetAwAdminOverviewQuery(
    int? ApplicableType = null,
    bool? IsUserSelectable = null) : IRequest<AwAdminOverviewDto>;

public class GetAwAdminOverviewQueryHandler(
    IApplicationDbContext db,
    UserManager<User> userManager) : IRequestHandler<GetAwAdminOverviewQuery, AwAdminOverviewDto>
{
    public async Task<AwAdminOverviewDto> Handle(GetAwAdminOverviewQuery request, CancellationToken ct)
    {
        var query = db.ApprovalWorkflows.AsNoTracking()
            .Include(d => d.Steps.OrderBy(s => s.Order))
                .ThenInclude(s => s.Levels.OrderBy(l => l.Order))
            .OrderByDescending(d => d.Version)
            .AsQueryable();

        if (request.ApplicableType is int t)
        {
            var typeEnum = (ApprovalWorkflowApplicableType)t;
            query = query.Where(d => d.ApplicableType == typeEnum);
        }

        if (request.IsUserSelectable is bool ius)
        {
            query = query.Where(d => d.IsUserSelectable == ius);
        }

        var definitions = await query.ToListAsync(ct);

        // Resolve dept names
        var deptIds = definitions
            .SelectMany(d => d.Steps)
            .Where(s => s.DepartmentId != null)
            .Select(s => s.DepartmentId!.Value)
            .Distinct().ToList();
        var deptNames = deptIds.Count == 0
            ? new Dictionary<Guid, string>()
            : await db.Departments.AsNoTracking()
                .Where(d => deptIds.Contains(d.Id))
                .ToDictionaryAsync(d => d.Id, d => d.Name, ct);

        // Resolve user names
        var userIds = definitions
            .SelectMany(d => d.Steps)
            .SelectMany(s => s.Levels)
            .Select(l => l.ApproverUserId)
            .Distinct().ToList();
        var users = userIds.Count == 0
            ? new Dictionary<Guid, (string FullName, string? Email)>()
            : await userManager.Users.AsNoTracking()
                .Where(u => userIds.Contains(u.Id))
                .Select(u => new { u.Id, u.FullName, u.Email })
                .ToDictionaryAsync(u => u.Id, u => (u.FullName, u.Email), ct);

        // [S76] Hiển thị-only: tập user được nhập/điều chỉnh ngân sách (đảo chiều
        // GetUsersInRoleAsync → set-lookup, no N+1). Khớp gate canEditPro/canEditCcm
        // (PurchaseEvaluationFeatures.cs:800-801): PRO = Admin|Procurement, CCM = Admin|CostControl.
        var adminUserIds = (await userManager.GetUsersInRoleAsync(AppRoles.Admin)).Select(u => u.Id).ToHashSet();
        var proBudgetEditors = (await userManager.GetUsersInRoleAsync(AppRoles.Procurement)).Select(u => u.Id).Concat(adminUserIds).ToHashSet();
        var ccmBudgetEditors = (await userManager.GetUsersInRoleAsync(AppRoles.CostControl)).Select(u => u.Id).Concat(adminUserIds).ToHashSet();

        AwDefinitionDto ToDto(ApprovalWorkflow d) => new(
            d.Id,
            d.Code,
            d.Version,
            (int)d.ApplicableType,
            AwLabels.Type.GetValueOrDefault(d.ApplicableType, d.ApplicableType.ToString()),
            d.Name,
            d.Description,
            d.IsActive,
            d.IsUserSelectable,
            d.CeoApprovalThreshold,    // [S69] ngưỡng gói CEO
            d.ActivatedAt,
            d.CreatedAt,
            d.Steps.OrderBy(s => s.Order).Select(s => new AwStepDto(
                s.Id,
                s.Order,
                s.Name,
                s.DepartmentId,
                s.DepartmentId != null ? deptNames.GetValueOrDefault(s.DepartmentId.Value) : null,
                s.Levels.OrderBy(l => l.Order).Select(l =>
                {
                    users.TryGetValue(l.ApproverUserId, out var info);
                    // Mig 29 (S21 t5) + Mig 30 (S22+5) + Mig 31 (S23 t1) — 7 Allow* flag per slot Level
                    return new AwLevelDto(l.Id, l.Order, l.Name, l.ApproverUserId, info.FullName, info.Email,
                        l.AllowReturnOneLevel, l.AllowReturnOneStep, l.AllowReturnToAssignee,
                        l.AllowReturnToDrafter, l.AllowApproverEditDetails, l.AllowApproverEditBudget,
                        l.AllowApproverSkipToFinal, l.AllowApproverFinalize,
                        proBudgetEditors.Contains(l.ApproverUserId), ccmBudgetEditors.Contains(l.ApproverUserId));
                }).ToList()
            )).ToList());

        var typesToReturn = request.ApplicableType is int onlyT
            ? new[] { (ApprovalWorkflowApplicableType)onlyT }
            : Enum.GetValues<ApprovalWorkflowApplicableType>();

        var result = typesToReturn
            .Select(type =>
            {
                var versions = definitions.Where(d => d.ApplicableType == type).Select(ToDto).ToList();
                return new AwTypeSummaryDto(
                    (int)type,
                    AwLabels.Type.GetValueOrDefault(type, type.ToString()),
                    versions.FirstOrDefault(v => v.IsActive),
                    versions);
            })
            .ToList();

        return new AwAdminOverviewDto(result);
    }
}

// ========== POST new version ==========

public record CreateAwLevelInput(
    int Order,
    string? Name,
    Guid ApproverUserId,
    // Mig 29 (S21 t5) + Mig 30 (S22+5) + Mig 31 (S23 t1) — 7 Allow* options per
    // slot. Admin Designer tick per Level row. Default backward compat:
    // AllowReturnToDrafter=true (S17 fallback), 6 còn lại false (admin opt-in
    // từng slot). Pattern proven 3× cumulative.
    bool AllowReturnOneLevel = false,
    bool AllowReturnOneStep = false,
    bool AllowReturnToAssignee = false,
    bool AllowReturnToDrafter = true,
    bool AllowApproverEditDetails = false,
    bool AllowApproverEditBudget = false,
    bool AllowApproverSkipToFinal = false,
    bool AllowApproverFinalize = false);

public record CreateAwStepInput(
    int Order,
    string Name,
    Guid? DepartmentId,
    List<CreateAwLevelInput> Levels);

public record CreateAwDefinitionCommand(
    int ApplicableType,
    string Code,
    string Name,
    string? Description,
    List<CreateAwStepInput> Steps,
    // [S69 2026-06-17] Ngưỡng giá trị "gói CEO" — admin nhập trong Workflow Designer.
    // CCM (CostControl) duyệt-final không cần lên CEO khi tổng giá NCC được chọn <
    // ngưỡng; ≥ ngưỡng → vẫn lên CEO (Director). NULL = không áp ngưỡng (luồng cũ).
    // Nullable passthrough — trailing-optional default backward-compat call-site cũ.
    decimal? CeoApprovalThreshold = null) : IRequest<Guid>;

public class CreateAwDefinitionCommandValidator : AbstractValidator<CreateAwDefinitionCommand>
{
    // Convention Mig 22 + UAT iter 2 (2026-05-08):
    //   Tối đa 3 Cấp/Bước (Order ∈ {1,2,3}). Mỗi Cấp có N approver (multiple
    //   Level rows cùng Order = same Cấp, mỗi row = 1 NV). Sequential gating:
    //   có Cấp k yêu cầu Cấp k-1 đã có ≥1 NV. Không duplicate (Order, UserId)
    //   cùng 1 Bước.
    public const int MaxLevelsPerStep = 3;

    public CreateAwDefinitionCommandValidator()
    {
        RuleFor(x => x.ApplicableType).Must(t => Enum.IsDefined(typeof(ApprovalWorkflowApplicableType), t))
            .WithMessage("ApplicableType không hợp lệ.");
        RuleFor(x => x.Code).NotEmpty().MaximumLength(100)
            .Matches("^[A-Za-z0-9._-]+$")
            .WithMessage("Code chỉ dùng chữ, số, và các ký tự . _ -");
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.Description).MaximumLength(1000);
        RuleFor(x => x.Steps).NotEmpty()
            .WithMessage("Quy trình phải có ít nhất 1 bước.");
        // [S69] Ngưỡng gói CEO — nếu nhập phải >= 0 (NULL = không áp ngưỡng OK).
        RuleFor(x => x.CeoApprovalThreshold).GreaterThanOrEqualTo(0)
            .When(x => x.CeoApprovalThreshold.HasValue)
            .WithMessage("Ngưỡng giá trị gói CEO không được âm.");
        RuleForEach(x => x.Steps).ChildRules(step =>
        {
            step.RuleFor(s => s.Order).GreaterThanOrEqualTo(1);
            step.RuleFor(s => s.Name).NotEmpty().MaximumLength(200);
            step.RuleFor(s => s.Levels).NotEmpty()
                .WithMessage("Mỗi bước phải có ít nhất 1 cấp duyệt.");
            step.RuleForEach(s => s.Levels).ChildRules(level =>
            {
                level.RuleFor(l => l.Order).InclusiveBetween(1, MaxLevelsPerStep)
                    .WithMessage($"Cấp duyệt chỉ trong khoảng 1..{MaxLevelsPerStep}.");
                level.RuleFor(l => l.Name).MaximumLength(200);
                level.RuleFor(l => l.ApproverUserId).NotEmpty()
                    .WithMessage("Mỗi dòng cấp phải chỉ định 1 NV duyệt.");
            });
            // Sequential gating + no-duplicate (Order, UserId).
            step.RuleFor(s => s.Levels).Must(HaveSequentialOrders)
                .WithMessage($"Cấp duyệt phải tuần tự từ 1 (có Cấp k cần Cấp k-1). Tối đa {MaxLevelsPerStep} cấp.");
            step.RuleFor(s => s.Levels).Must(HaveNoDuplicateApproverInSameLevel)
                .WithMessage("Một NV không được duyệt hai lần trong cùng một Cấp.");
        });
    }

    // Cho phép 1 / 1+2 / 1+2+3 — KHÔNG cho 2 (thiếu 1) hoặc 1+3 (thiếu 2).
    private static bool HaveSequentialOrders(List<CreateAwLevelInput> levels)
    {
        if (levels.Count == 0) return false;
        var orders = levels.Select(l => l.Order).Distinct().OrderBy(o => o).ToList();
        if (orders.Count == 0 || orders.Count > MaxLevelsPerStep) return false;
        for (int i = 0; i < orders.Count; i++)
        {
            if (orders[i] != i + 1) return false;
        }
        return true;
    }

    private static bool HaveNoDuplicateApproverInSameLevel(List<CreateAwLevelInput> levels)
    {
        return levels
            .GroupBy(l => new { l.Order, l.ApproverUserId })
            .All(g => g.Count() == 1);
    }
}

public class CreateAwDefinitionCommandHandler(IApplicationDbContext db)
    : IRequestHandler<CreateAwDefinitionCommand, Guid>
{
    public async Task<Guid> Handle(CreateAwDefinitionCommand request, CancellationToken ct)
    {
        var typeEnum = (ApprovalWorkflowApplicableType)request.ApplicableType;

        // Auto-increment version theo Code (cùng Code = cùng "logical" workflow)
        var nextVersion = await db.ApprovalWorkflows
            .Where(w => w.Code == request.Code)
            .MaxAsync(w => (int?)w.Version, ct) ?? 0;
        nextVersion++;

        // Deactivate active version cho ApplicableType này (only ONE active per type)
        var actives = await db.ApprovalWorkflows
            .Where(w => w.ApplicableType == typeEnum && w.IsActive)
            .ToListAsync(ct);
        foreach (var old in actives) old.IsActive = false;

        var def = new ApprovalWorkflow
        {
            Code = request.Code,
            Version = nextVersion,
            ApplicableType = typeEnum,
            Name = request.Name,
            Description = request.Description,
            IsActive = true,
            IsUserSelectable = true,    // Mig 25 — version mới mặc định cho user pick
            // Mig 29 (S21 t5) — Allow* options đã move xuống Level slot (per-NV)
            CeoApprovalThreshold = request.CeoApprovalThreshold,  // [S69] ngưỡng gói CEO (nullable passthrough)
            ActivatedAt = DateTime.UtcNow,
            Steps = request.Steps.OrderBy(s => s.Order)
                .Select(s => new ApprovalWorkflowStep
                {
                    Order = s.Order,
                    Name = s.Name,
                    DepartmentId = s.DepartmentId,
                    Levels = s.Levels.OrderBy(l => l.Order)
                        .Select(l => new ApprovalWorkflowLevel
                        {
                            Order = l.Order,
                            Name = l.Name,
                            ApproverUserId = l.ApproverUserId,
                            // Mig 29 (S21 t5) + Mig 30 (S22+5) + Mig 31 (S23 t1) — 7 Allow* per slot
                            AllowReturnOneLevel = l.AllowReturnOneLevel,
                            AllowReturnOneStep = l.AllowReturnOneStep,
                            AllowReturnToAssignee = l.AllowReturnToAssignee,
                            AllowReturnToDrafter = l.AllowReturnToDrafter,
                            AllowApproverEditDetails = l.AllowApproverEditDetails,
                            AllowApproverEditBudget = l.AllowApproverEditBudget,
                            AllowApproverSkipToFinal = l.AllowApproverSkipToFinal,
                            AllowApproverFinalize = l.AllowApproverFinalize,
                        }).ToList(),
                })
                .ToList(),
        };
        db.ApprovalWorkflows.Add(def);
        await db.SaveChangesAsync(ct);
        return def.Id;
    }
}

// ========== PATCH user-selectable toggle (Mig 25 — admin pin/unpin cho user pick) ==========
// Independent với IsActive: cho phép multiple version cùng selectable. Default
// version mới IsUserSelectable=true (mirror IsActive default), admin có thể
// unstick để dấu khỏi Workspace dropdown, hoặc stick lại version cũ archived.

public record SetAwUserSelectableCommand(Guid Id, bool IsUserSelectable) : IRequest;

public class SetAwUserSelectableCommandHandler(IApplicationDbContext db)
    : IRequestHandler<SetAwUserSelectableCommand>
{
    public async Task Handle(SetAwUserSelectableCommand request, CancellationToken ct)
    {
        var def = await db.ApprovalWorkflows
            .FirstOrDefaultAsync(d => d.Id == request.Id, ct)
            ?? throw new KeyNotFoundException($"ApprovalWorkflow {request.Id} không tồn tại.");

        def.IsUserSelectable = request.IsUserSelectable;
        await db.SaveChangesAsync(ct);
    }
}

// ========== DELETE version (chỉ khi chưa có phiếu pin) ==========
// Hiện chưa có phiếu nào pin schema mới → unconditional delete OK cho UAT.
// Sau UAT khi link với PE/Contract thật cần check usage trước khi delete.

public record DeleteAwDefinitionCommand(Guid Id) : IRequest;

public class DeleteAwDefinitionCommandHandler(IApplicationDbContext db)
    : IRequestHandler<DeleteAwDefinitionCommand>
{
    public async Task Handle(DeleteAwDefinitionCommand request, CancellationToken ct)
    {
        var def = await db.ApprovalWorkflows
            .FirstOrDefaultAsync(d => d.Id == request.Id, ct)
            ?? throw new KeyNotFoundException($"ApprovalWorkflow {request.Id} không tồn tại.");

        db.ApprovalWorkflows.Remove(def);
        await db.SaveChangesAsync(ct);
    }
}
