using FluentValidation;
using MediatR;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using SolutionErp.Application.Common.Interfaces;
using SolutionErp.Domain.Contracts;   // WorkflowApproverKind reuse
using SolutionErp.Domain.Identity;
using SolutionErp.Domain.PurchaseEvaluations;

namespace SolutionErp.Application.PurchaseEvaluations;

// Versioned workflow management cho module Duyệt NCC (PE) — mirror Contract
// `WorkflowAdminFeatures` pattern. Phiếu PE đã pin WorkflowDefinitionId tại
// thời điểm tạo → vẫn chạy version cũ kể cả khi admin activate version mới.

public record PeWorkflowStepApproverDto(
    int Kind,               // 1=Role, 2=User (reuse WorkflowApproverKind)
    string AssignmentValue,
    string? DisplayName);

// Mig 21 — flat workflow step. Mỗi step = 1 (Phòng × Cấp + Approvers users).
public record PeWorkflowStepDto(
    Guid Id,
    int Order,
    int Phase,                  // [DEPRECATED post-Mig 21] dùng ChoDuyet=10
    string PhaseLabel,
    string Name,                // "Phòng A — Cấp 1"
    int? SlaDays,
    Guid? DepartmentId,         // Mig 21
    string? DepartmentName,
    int? PositionLevel,         // Mig 21 — 1=NV, 2=PP, 3=TP
    List<PeWorkflowStepApproverDto> Approvers);

public record PeWorkflowDefinitionDto(
    Guid Id,
    string Code,
    int Version,
    int EvaluationType,
    string EvaluationTypeLabel,
    string Name,
    string? Description,
    bool IsActive,
    DateTime? ActivatedAt,
    DateTime CreatedAt,
    int EvaluationsUsingCount,
    List<PeWorkflowStepDto> Steps);

public record PeWorkflowTypeSummaryDto(
    int EvaluationType,
    string EvaluationTypeLabel,
    PeWorkflowDefinitionDto? Active,
    List<PeWorkflowDefinitionDto> History);

public record PeWorkflowAdminOverviewDto(List<PeWorkflowTypeSummaryDto> Types);

// ========== GET overview ==========

public record GetPeWorkflowAdminOverviewQuery : IRequest<PeWorkflowAdminOverviewDto>;

public class GetPeWorkflowAdminOverviewQueryHandler(
    IApplicationDbContext db,
    UserManager<User> userManager) : IRequestHandler<GetPeWorkflowAdminOverviewQuery, PeWorkflowAdminOverviewDto>
{
    private static readonly Dictionary<PurchaseEvaluationType, string> TypeLabels = new()
    {
        [PurchaseEvaluationType.DuyetNcc] = "Duyệt NCC",
        [PurchaseEvaluationType.DuyetNccPhuongAn] = "Duyệt NCC + Giải pháp",
    };

    private static readonly Dictionary<PurchaseEvaluationPhase, string> PhaseLabels = new()
    {
        [PurchaseEvaluationPhase.DangSoanThao] = "Nháp",
        [PurchaseEvaluationPhase.ChoPurchasing] = "Chờ Purchasing",
        [PurchaseEvaluationPhase.ChoDuAn] = "Chờ Dự án",
        [PurchaseEvaluationPhase.ChoCCM] = "Chờ CCM",
        [PurchaseEvaluationPhase.ChoCEODuyetPA] = "Chờ CEO duyệt PA",
        [PurchaseEvaluationPhase.ChoCEODuyetNCC] = "Chờ CEO duyệt NCC",
        [PurchaseEvaluationPhase.ChoDuyet] = "Đã gửi duyệt",
        [PurchaseEvaluationPhase.DaDuyet] = "Đã duyệt",
        [PurchaseEvaluationPhase.TraLai] = "Trả lại",
        [PurchaseEvaluationPhase.TuChoi] = "Từ chối",
    };

    public async Task<PeWorkflowAdminOverviewDto> Handle(GetPeWorkflowAdminOverviewQuery request, CancellationToken ct)
    {
        var definitions = await db.PurchaseEvaluationWorkflowDefinitions.AsNoTracking()
            .Include(d => d.Steps.OrderBy(s => s.Order))
                .ThenInclude(s => s.Approvers)
            .OrderByDescending(d => d.Version)
            .ToListAsync(ct);

        // Resolve dept names cho step.DepartmentId display (Mig 21 flat)
        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 cho User-kind approvers
        var userIds = definitions
            .SelectMany(d => d.Steps)
            .SelectMany(s => s.Approvers)
            .Where(a => a.Kind == WorkflowApproverKind.User && Guid.TryParse(a.AssignmentValue, out _))
            .Select(a => Guid.Parse(a.AssignmentValue))
            .Distinct()
            .ToList();
        var userNames = userIds.Count == 0
            ? new Dictionary<Guid, string>()
            : await userManager.Users.AsNoTracking()
                .Where(u => userIds.Contains(u.Id))
                .ToDictionaryAsync(u => u.Id, u => u.FullName, ct);

        // Count phiếu PE per definition
        var usageCounts = await db.PurchaseEvaluations.AsNoTracking()
            .Where(p => p.WorkflowDefinitionId != null)
            .GroupBy(p => p.WorkflowDefinitionId!.Value)
            .Select(g => new { Id = g.Key, Count = g.Count() })
            .ToDictionaryAsync(x => x.Id, x => x.Count, ct);

        PeWorkflowDefinitionDto ToDto(PurchaseEvaluationWorkflowDefinition d) => new(
            d.Id,
            d.Code,
            d.Version,
            (int)d.EvaluationType,
            TypeLabels.GetValueOrDefault(d.EvaluationType, d.EvaluationType.ToString()),
            d.Name,
            d.Description,
            d.IsActive,
            d.ActivatedAt,
            d.CreatedAt,
            usageCounts.GetValueOrDefault(d.Id, 0),
            d.Steps.OrderBy(s => s.Order).Select(s => new PeWorkflowStepDto(
                s.Id,
                s.Order,
                (int)s.Phase,
                PhaseLabels.GetValueOrDefault(s.Phase, s.Phase.ToString()),
                s.Name,
                s.SlaDays,
                s.DepartmentId,
                s.DepartmentId != null ? deptNames.GetValueOrDefault(s.DepartmentId.Value) : null,
                s.PositionLevel != null ? (int?)s.PositionLevel : null,
                s.Approvers.Select(a => new PeWorkflowStepApproverDto(
                    (int)a.Kind,
                    a.AssignmentValue,
                    ResolveDisplay(a, userNames))).ToList()
            )).ToList());

        var types = Enum.GetValues<PurchaseEvaluationType>()
            .Select(type =>
            {
                var versions = definitions.Where(d => d.EvaluationType == type).Select(ToDto).ToList();
                return new PeWorkflowTypeSummaryDto(
                    (int)type,
                    TypeLabels.GetValueOrDefault(type, type.ToString()),
                    versions.FirstOrDefault(v => v.IsActive),
                    versions);
            })
            .ToList();

        return new PeWorkflowAdminOverviewDto(types);
    }

    private static string? ResolveDisplay(PurchaseEvaluationWorkflowStepApprover a, Dictionary<Guid, string> userNames)
    {
        if (a.Kind == WorkflowApproverKind.Role) return a.AssignmentValue;
        if (Guid.TryParse(a.AssignmentValue, out var uid) && userNames.TryGetValue(uid, out var n)) return n;
        return a.AssignmentValue;
    }
}

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

public record CreatePeWorkflowStepApproverInput(int Kind, string AssignmentValue);

// Mig 21 — flat workflow step input. DeptId + PositionLevel = Phòng × Cấp.
public record CreatePeWorkflowStepInput(
    int Order,
    int Phase,                  // [DEPRECATED] caller có thể truyền 10=ChoDuyet hoặc legacy
    string Name,
    int? SlaDays,
    Guid? DepartmentId,         // Mig 21
    int? PositionLevel,         // Mig 21 — 1=NV, 2=PP, 3=TP
    List<CreatePeWorkflowStepApproverInput> Approvers);

public record CreatePeWorkflowDefinitionCommand(
    PurchaseEvaluationType EvaluationType,
    string Code,
    string Name,
    string? Description,
    List<CreatePeWorkflowStepInput> Steps) : IRequest<Guid>;

public class CreatePeWorkflowDefinitionCommandValidator : AbstractValidator<CreatePeWorkflowDefinitionCommand>
{
    public CreatePeWorkflowDefinitionCommandValidator()
    {
        RuleFor(x => x.EvaluationType).IsInEnum();
        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.");
        RuleForEach(x => x.Steps).ChildRules(step =>
        {
            step.RuleFor(s => s.Order).GreaterThanOrEqualTo(1);
            // Phase 1..7 thường, 99 = TuChoi (không nên dùng làm step)
            step.RuleFor(s => s.Phase).Must(p => p >= 1 && p <= 7)
                .WithMessage("Phase phải nằm trong 1..7 (state thường, không bao gồm Từ chối=99).");
            step.RuleFor(s => s.Name).NotEmpty().MaximumLength(200);
            step.RuleFor(s => s.SlaDays).GreaterThanOrEqualTo(0)
                .When(s => s.SlaDays != null);
            step.RuleForEach(s => s.Approvers).ChildRules(app =>
            {
                app.RuleFor(a => a.Kind).InclusiveBetween(1, 2);
                app.RuleFor(a => a.AssignmentValue).NotEmpty().MaximumLength(100);
            });
            step.RuleFor(s => s.PositionLevel).InclusiveBetween(1, 3)
                .When(s => s.PositionLevel != null)
                .WithMessage("PositionLevel: 1=NV, 2=PP, 3=TP.");
        });
    }
}

public class CreatePeWorkflowDefinitionCommandHandler(IApplicationDbContext db)
    : IRequestHandler<CreatePeWorkflowDefinitionCommand, Guid>
{
    public async Task<Guid> Handle(CreatePeWorkflowDefinitionCommand request, CancellationToken ct)
    {
        var nextVersion = await db.PurchaseEvaluationWorkflowDefinitions
            .Where(w => w.Code == request.Code)
            .MaxAsync(w => (int?)w.Version, ct) ?? 0;
        nextVersion++;

        // Deactivate active version cho EvaluationType này (only ONE active per type)
        var activeVersions = await db.PurchaseEvaluationWorkflowDefinitions
            .Where(w => w.EvaluationType == request.EvaluationType && w.IsActive)
            .ToListAsync(ct);
        foreach (var old in activeVersions) old.IsActive = false;

        var def = new PurchaseEvaluationWorkflowDefinition
        {
            Code = request.Code,
            Version = nextVersion,
            EvaluationType = request.EvaluationType,
            Name = request.Name,
            Description = request.Description,
            IsActive = true,
            ActivatedAt = DateTime.UtcNow,
            Steps = request.Steps
                .OrderBy(s => s.Order)
                .Select(s => new PurchaseEvaluationWorkflowStep
                {
                    Order = s.Order,
                    Phase = (PurchaseEvaluationPhase)s.Phase,
                    Name = s.Name,
                    SlaDays = s.SlaDays,
                    DepartmentId = s.DepartmentId,
                    PositionLevel = s.PositionLevel != null ? (PositionLevel)s.PositionLevel : null,
                    Approvers = s.Approvers.Select(a => new PurchaseEvaluationWorkflowStepApprover
                    {
                        Kind = (WorkflowApproverKind)a.Kind,
                        AssignmentValue = a.AssignmentValue,
                    }).ToList(),
                })
                .ToList(),
        };
        db.PurchaseEvaluationWorkflowDefinitions.Add(def);
        await db.SaveChangesAsync(ct);
        return def.Id;
    }
}
