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

namespace SolutionErp.Application.Contracts;

// Versioned workflow management: admin xem + tạo version mới per ContractType.
// HĐ cũ đã 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 WorkflowStepApproverDto(
    int Kind,               // 1=Role, 2=User
    string AssignmentValue,
    string? DisplayName);   // resolved role label or user fullName

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

public record WorkflowDefinitionDto(
    Guid Id,
    string Code,
    int Version,
    int ContractType,
    string ContractTypeLabel,
    string Name,
    string? Description,
    bool IsActive,
    DateTime? ActivatedAt,
    DateTime CreatedAt,
    int ContractsUsingCount,
    List<WorkflowStepDto> Steps);

public record WorkflowTypeSummaryDto(
    int ContractType,
    string ContractTypeLabel,
    WorkflowDefinitionDto? Active,
    List<WorkflowDefinitionDto> History);

public record WorkflowAdminOverviewDto(List<WorkflowTypeSummaryDto> Types);

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

public record GetWorkflowAdminOverviewQuery : IRequest<WorkflowAdminOverviewDto>;

public class GetWorkflowAdminOverviewQueryHandler(
    IApplicationDbContext db,
    UserManager<User> userManager) : IRequestHandler<GetWorkflowAdminOverviewQuery, WorkflowAdminOverviewDto>
{
    private static readonly Dictionary<ContractType, string> TypeLabels = new()
    {
        [ContractType.HopDongThauPhu] = "HĐ Thầu phụ",
        [ContractType.HopDongGiaoKhoan] = "HĐ Giao khoán",
        [ContractType.HopDongNhaCungCap] = "HĐ Nhà cung cấp",
        [ContractType.HopDongDichVu] = "HĐ Dịch vụ",
        [ContractType.HopDongMuaBan] = "HĐ Mua bán",
        [ContractType.HopDongNguyenTacNCC] = "HĐ Nguyên tắc NCC",
        [ContractType.HopDongNguyenTacDichVu] = "HĐ Nguyên tắc Dịch vụ",
    };

    private static readonly Dictionary<ContractPhase, string> PhaseLabels = new()
    {
        [ContractPhase.DangSoanThao] = "Nháp",
        [ContractPhase.DangGopY] = "Đang góp ý",
        [ContractPhase.DangDamPhan] = "Đang đàm phán",
        [ContractPhase.DangInKy] = "Đang in ký",
        [ContractPhase.DangKiemTraCCM] = "CCM kiểm tra",
        [ContractPhase.DangTrinhKy] = "Đang trình ký",
        [ContractPhase.DangDongDau] = "Đang đóng dấu",
        [ContractPhase.DaPhatHanh] = "Đã phát hành",
        [ContractPhase.ChoDuyet] = "Đã gửi duyệt",
        [ContractPhase.TraLai] = "Trả lại",
        [ContractPhase.TuChoi] = "Từ chối",
    };

    public async Task<WorkflowAdminOverviewDto> Handle(GetWorkflowAdminOverviewQuery request, CancellationToken ct)
    {
        var definitions = await db.WorkflowDefinitions.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)
        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 for 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 contracts per definition — admin sees which versions are still live
        var usageCounts = await db.Contracts.AsNoTracking()
            .Where(c => c.WorkflowDefinitionId != null)
            .GroupBy(c => c.WorkflowDefinitionId!.Value)
            .Select(g => new { Id = g.Key, Count = g.Count() })
            .ToDictionaryAsync(x => x.Id, x => x.Count, ct);

        WorkflowDefinitionDto ToDto(WorkflowDefinition d) => new(
            d.Id,
            d.Code,
            d.Version,
            (int)d.ContractType,
            TypeLabels.GetValueOrDefault(d.ContractType, d.ContractType.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 WorkflowStepDto(
                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 WorkflowStepApproverDto(
                    (int)a.Kind,
                    a.AssignmentValue,
                    ResolveDisplay(a, userNames))).ToList()
            )).ToList());

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

        return new WorkflowAdminOverviewDto(types);
    }

    private static string? ResolveDisplay(WorkflowStepApprover 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 CreateWorkflowStepApproverInput(int Kind, string AssignmentValue);

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

public record CreateWorkflowDefinitionCommand(
    ContractType ContractType,
    string Code,
    string Name,
    string? Description,
    List<CreateWorkflowStepInput> Steps) : IRequest<Guid>;

public class CreateWorkflowDefinitionCommandValidator : AbstractValidator<CreateWorkflowDefinitionCommand>
{
    public CreateWorkflowDefinitionCommandValidator()
    {
        RuleFor(x => x.ContractType).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);
            step.RuleFor(s => s.Phase).InclusiveBetween(1, 99);   // Mig 21 accept ChoDuyet=10
            step.RuleFor(s => s.Name).NotEmpty().MaximumLength(200);
            step.RuleFor(s => s.SlaDays).GreaterThanOrEqualTo(0)
                .When(s => s.SlaDays != null);
            step.RuleFor(s => s.PositionLevel).InclusiveBetween(1, 3)
                .When(s => s.PositionLevel != null)
                .WithMessage("PositionLevel: 1=NV, 2=PP, 3=TP.");
            step.RuleForEach(s => s.Approvers).ChildRules(app =>
            {
                app.RuleFor(a => a.Kind).InclusiveBetween(1, 2);
                app.RuleFor(a => a.AssignmentValue).NotEmpty().MaximumLength(100);
            });
        });
    }
}

public class CreateWorkflowDefinitionCommandHandler(IApplicationDbContext db)
    : IRequestHandler<CreateWorkflowDefinitionCommand, Guid>
{
    public async Task<Guid> Handle(CreateWorkflowDefinitionCommand request, CancellationToken ct)
    {
        // Next version = max(existing) + 1. Versions monotonically increase per Code.
        var nextVersion = await db.WorkflowDefinitions
            .Where(w => w.Code == request.Code)
            .MaxAsync(w => (int?)w.Version, ct) ?? 0;
        nextVersion++;

        // Deactivate currently active version for this type — only ONE active
        // per ContractType at a time (app invariant).
        var activeVersions = await db.WorkflowDefinitions
            .Where(w => w.ContractType == request.ContractType && w.IsActive)
            .ToListAsync(ct);
        foreach (var old in activeVersions) old.IsActive = false;

        var def = new WorkflowDefinition
        {
            Code = request.Code,
            Version = nextVersion,
            ContractType = request.ContractType,
            Name = request.Name,
            Description = request.Description,
            IsActive = true,
            ActivatedAt = DateTime.UtcNow,
            Steps = request.Steps
                .OrderBy(s => s.Order)
                .Select(s => new WorkflowStep
                {
                    Order = s.Order,
                    Phase = (ContractPhase)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 WorkflowStepApprover
                    {
                        Kind = (WorkflowApproverKind)a.Kind,
                        AssignmentValue = a.AssignmentValue,
                    }).ToList(),
                })
                .ToList(),
        };
        db.WorkflowDefinitions.Add(def);
        await db.SaveChangesAsync(ct);
        return def.Id;
    }
}
