using FluentValidation;
using MediatR;
using Microsoft.EntityFrameworkCore;
using SolutionErp.Application.Common.Exceptions;
using SolutionErp.Application.Common.Interfaces;
using SolutionErp.Domain.Master.Catalogs;

namespace SolutionErp.Application.Master.Catalogs;

// [K1 — S164, Mig 70] CRUD CQRS cho "Danh mục Hợp đồng" (SOL-CCM-SP-002, 86 dòng).
// File RIÊNG cạnh CatalogsFeatures.cs (mega-file 4 catalog cũ) vì shape khác hẳn:
// có GroupCode + ApprovalGroup (1-8 = nhóm duyệt N1..N8) + 2 cột vai-ký dạng TEXT.
//
// Endpoints (ContractCatalogController, route `api/catalogs/contract-catalog`):
//   GET    /api/catalogs/contract-catalog              — list (filter groupCode/approvalGroup/q;
//          mặc định CHỈ trả IsActive=true — spec K1.c `IncludeInactive?` mặc-định-loại;
//          truyền includeInactive=true để thấy cả dòng ngừng hiệu lực — gate-K1 F4 @S165)
//   GET    /api/catalogs/contract-catalog/{id}         — get by id
//   POST   /api/catalogs/contract-catalog              — create (Admin)
//   PUT    /api/catalogs/contract-catalog/{id}         — update (Admin)
//   DELETE /api/catalogs/contract-catalog/{id}         — soft delete (Admin)
//
// KHÔNG paging: 86 dòng, dùng chung cho trang quản trị + picker chọn hạng mục (K2).
// Dup-Code check chạy trên ACTIVE-set (query filter !IsDeleted) — khớp UNIQUE index
// filtered `[IsDeleted] = 0` ⇒ Code của row đã xoá mềm được tái sử dụng (gotcha #57).

// ===== DTO =====

public record ContractCatalogEntryDto(
    Guid Id,
    string Code,
    string TenVi,
    string? TenEn,
    string GroupCode,
    int ApprovalGroup,
    string SignerRole,
    string? InitialsChain,
    string? NduqNote,
    bool IsActive,
    int SortOrder);

// ===== List =====

public record ListContractCatalogQuery(
    string? GroupCode = null,
    int? ApprovalGroup = null,
    bool IncludeInactive = false,
    string? Q = null) : IRequest<List<ContractCatalogEntryDto>>;

public class ListContractCatalogHandler(IApplicationDbContext db)
    : IRequestHandler<ListContractCatalogQuery, List<ContractCatalogEntryDto>>
{
    public async Task<List<ContractCatalogEntryDto>> Handle(ListContractCatalogQuery req, CancellationToken ct)
    {
        var q = db.ContractCatalogEntries.AsNoTracking().AsQueryable();

        if (!string.IsNullOrWhiteSpace(req.GroupCode))
            q = q.Where(x => x.GroupCode == req.GroupCode);
        if (req.ApprovalGroup is int g)
            q = q.Where(x => x.ApprovalGroup == g);
        if (!req.IncludeInactive)
            q = q.Where(x => x.IsActive);
        if (!string.IsNullOrWhiteSpace(req.Q))
        {
            var s = req.Q.ToLower();
            q = q.Where(x => x.Code.ToLower().Contains(s)
                          || x.TenVi.ToLower().Contains(s)
                          || (x.TenEn != null && x.TenEn.ToLower().Contains(s)));
        }

        return await q.OrderBy(x => x.GroupCode).ThenBy(x => x.SortOrder).ThenBy(x => x.Code)
            .Select(x => new ContractCatalogEntryDto(
                x.Id, x.Code, x.TenVi, x.TenEn, x.GroupCode, x.ApprovalGroup,
                x.SignerRole, x.InitialsChain, x.NduqNote, x.IsActive, x.SortOrder))
            .ToListAsync(ct);
    }
}

// ===== Get by id =====

public record GetContractCatalogEntryQuery(Guid Id) : IRequest<ContractCatalogEntryDto>;

public class GetContractCatalogEntryHandler(IApplicationDbContext db)
    : IRequestHandler<GetContractCatalogEntryQuery, ContractCatalogEntryDto>
{
    public async Task<ContractCatalogEntryDto> Handle(GetContractCatalogEntryQuery req, CancellationToken ct)
        => await db.ContractCatalogEntries.AsNoTracking()
            .Where(x => x.Id == req.Id)
            .Select(x => new ContractCatalogEntryDto(
                x.Id, x.Code, x.TenVi, x.TenEn, x.GroupCode, x.ApprovalGroup,
                x.SignerRole, x.InitialsChain, x.NduqNote, x.IsActive, x.SortOrder))
            .FirstOrDefaultAsync(ct)
           ?? throw new NotFoundException("ContractCatalogEntry", req.Id);
}

// ===== Create =====

public record CreateContractCatalogEntryCommand(
    string Code,
    string TenVi,
    string? TenEn,
    string GroupCode,
    int ApprovalGroup,
    string SignerRole,
    string? InitialsChain,
    string? NduqNote,
    bool IsActive,
    int SortOrder) : IRequest<Guid>;

// MaxLength khớp ĐÚNG EF config (ContractCatalogEntryConfiguration.cs:18-24) —
// EF là source of truth, KHÔNG lấy số từ spec.
public class CreateContractCatalogEntryValidator : AbstractValidator<CreateContractCatalogEntryCommand>
{
    public CreateContractCatalogEntryValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(20);
        RuleFor(x => x.TenVi).NotEmpty().MaximumLength(500);
        RuleFor(x => x.TenEn).MaximumLength(500);
        RuleFor(x => x.GroupCode).NotEmpty().MaximumLength(10);
        RuleFor(x => x.ApprovalGroup).InclusiveBetween(1, 8)
            .WithMessage("Nhóm duyệt phải từ 1 đến 8 (N1..N8).");
        RuleFor(x => x.SignerRole).NotEmpty().MaximumLength(100);
        RuleFor(x => x.InitialsChain).MaximumLength(300);
        RuleFor(x => x.NduqNote).MaximumLength(500);
    }
}

public class CreateContractCatalogEntryHandler(IApplicationDbContext db)
    : IRequestHandler<CreateContractCatalogEntryCommand, Guid>
{
    public async Task<Guid> Handle(CreateContractCatalogEntryCommand req, CancellationToken ct)
    {
        if (await db.ContractCatalogEntries.AnyAsync(x => x.Code == req.Code, ct))
            throw new ConflictException($"Mã '{req.Code}' đã tồn tại.");

        var entity = new ContractCatalogEntry
        {
            Code = req.Code,
            TenVi = req.TenVi,
            TenEn = req.TenEn,
            GroupCode = req.GroupCode,
            ApprovalGroup = req.ApprovalGroup,
            SignerRole = req.SignerRole,
            InitialsChain = req.InitialsChain,
            NduqNote = req.NduqNote,
            IsActive = req.IsActive,
            SortOrder = req.SortOrder,
        };
        db.ContractCatalogEntries.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

// ===== Update =====

public record UpdateContractCatalogEntryCommand(
    Guid Id,
    string Code,
    string TenVi,
    string? TenEn,
    string GroupCode,
    int ApprovalGroup,
    string SignerRole,
    string? InitialsChain,
    string? NduqNote,
    bool IsActive,
    int SortOrder) : IRequest;

public class UpdateContractCatalogEntryValidator : AbstractValidator<UpdateContractCatalogEntryCommand>
{
    public UpdateContractCatalogEntryValidator()
    {
        RuleFor(x => x.Id).NotEmpty();
        RuleFor(x => x.Code).NotEmpty().MaximumLength(20);
        RuleFor(x => x.TenVi).NotEmpty().MaximumLength(500);
        RuleFor(x => x.TenEn).MaximumLength(500);
        RuleFor(x => x.GroupCode).NotEmpty().MaximumLength(10);
        RuleFor(x => x.ApprovalGroup).InclusiveBetween(1, 8)
            .WithMessage("Nhóm duyệt phải từ 1 đến 8 (N1..N8).");
        RuleFor(x => x.SignerRole).NotEmpty().MaximumLength(100);
        RuleFor(x => x.InitialsChain).MaximumLength(300);
        RuleFor(x => x.NduqNote).MaximumLength(500);
    }
}

public class UpdateContractCatalogEntryHandler(IApplicationDbContext db)
    : IRequestHandler<UpdateContractCatalogEntryCommand>
{
    public async Task Handle(UpdateContractCatalogEntryCommand req, CancellationToken ct)
    {
        var entity = await db.ContractCatalogEntries.FirstOrDefaultAsync(x => x.Id == req.Id, ct)
            ?? throw new NotFoundException("ContractCatalogEntry", req.Id);

        if (entity.Code != req.Code && await db.ContractCatalogEntries.AnyAsync(x => x.Code == req.Code, ct))
            throw new ConflictException($"Mã '{req.Code}' đã tồn tại.");

        entity.Code = req.Code;
        entity.TenVi = req.TenVi;
        entity.TenEn = req.TenEn;
        entity.GroupCode = req.GroupCode;
        entity.ApprovalGroup = req.ApprovalGroup;
        entity.SignerRole = req.SignerRole;
        entity.InitialsChain = req.InitialsChain;
        entity.NduqNote = req.NduqNote;
        entity.IsActive = req.IsActive;
        entity.SortOrder = req.SortOrder;
        await db.SaveChangesAsync(ct);
    }
}

// ===== Delete (soft) =====

public record DeleteContractCatalogEntryCommand(Guid Id) : IRequest;

public class DeleteContractCatalogEntryHandler(IApplicationDbContext db)
    : IRequestHandler<DeleteContractCatalogEntryCommand>
{
    public async Task Handle(DeleteContractCatalogEntryCommand req, CancellationToken ct)
    {
        var entity = await db.ContractCatalogEntries.FirstOrDefaultAsync(x => x.Id == req.Id, ct)
            ?? throw new NotFoundException("ContractCatalogEntry", req.Id);
        db.ContractCatalogEntries.Remove(entity);   // Soft delete via AuditingInterceptor
        await db.SaveChangesAsync(ct);
    }
}
