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

namespace SolutionErp.Application.Master.Projects;

public record ProjectDto(
    Guid Id,
    string Code,
    string Name,
    DateTime? StartDate,
    DateTime? EndDate,
    Guid? ManagerUserId,
    decimal? BudgetTotal,
    string? Note,
    DateTime CreatedAt,
    DateTime? UpdatedAt,
    int? Year,
    string? Investor,
    string? Location,
    string? Package);

// ===================== LIST =====================
public record ListProjectsQuery : PagedRequest, IRequest<PagedResult<ProjectDto>>;

public class ListProjectsQueryHandler(IApplicationDbContext db) : IRequestHandler<ListProjectsQuery, PagedResult<ProjectDto>>
{
    public async Task<PagedResult<ProjectDto>> Handle(ListProjectsQuery request, CancellationToken ct)
    {
        var query = db.Projects.AsNoTracking();
        if (!string.IsNullOrWhiteSpace(request.Search))
        {
            var s = request.Search.Trim();
            query = query.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
        }
        query = (request.SortBy, request.SortDesc) switch
        {
            ("code", true) => query.OrderByDescending(x => x.Code),
            ("code", false) => query.OrderBy(x => x.Code),
            ("name", true) => query.OrderByDescending(x => x.Name),
            ("name", false) => query.OrderBy(x => x.Name),
            _ => query.OrderByDescending(x => x.CreatedAt),
        };
        var total = await query.CountAsync(ct);
        var items = await query
            .Skip((request.Page - 1) * request.PageSize).Take(request.PageSize)
            .Select(x => new ProjectDto(x.Id, x.Code, x.Name, x.StartDate, x.EndDate, x.ManagerUserId, x.BudgetTotal, x.Note, x.CreatedAt, x.UpdatedAt, x.Year, x.Investor, x.Location, x.Package))
            .ToListAsync(ct);
        return new PagedResult<ProjectDto>(items, total, request.Page, request.PageSize);
    }
}

// ===================== GET =====================
public record GetProjectQuery(Guid Id) : IRequest<ProjectDto>;

public class GetProjectQueryHandler(IApplicationDbContext db) : IRequestHandler<GetProjectQuery, ProjectDto>
{
    public async Task<ProjectDto> Handle(GetProjectQuery request, CancellationToken ct)
    {
        var x = await db.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == request.Id, ct)
            ?? throw new NotFoundException("Project", request.Id);
        return new ProjectDto(x.Id, x.Code, x.Name, x.StartDate, x.EndDate, x.ManagerUserId, x.BudgetTotal, x.Note, x.CreatedAt, x.UpdatedAt, x.Year, x.Investor, x.Location, x.Package);
    }
}

// ===================== CREATE =====================
public record CreateProjectCommand(
    string Code, string Name, DateTime? StartDate, DateTime? EndDate,
    Guid? ManagerUserId, decimal? BudgetTotal, string? Note,
    int? Year, string? Investor, string? Location, string? Package) : IRequest<Guid>;

public class CreateProjectCommandValidator : AbstractValidator<CreateProjectCommand>
{
    public CreateProjectCommandValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.BudgetTotal).GreaterThanOrEqualTo(0).When(x => x.BudgetTotal.HasValue);
        RuleFor(x => x.Investor).MaximumLength(250);
        RuleFor(x => x.Location).MaximumLength(500);
        RuleFor(x => x.Package).MaximumLength(300);
        RuleFor(x => x).Must(x => !x.StartDate.HasValue || !x.EndDate.HasValue || x.EndDate >= x.StartDate)
            .WithMessage("Ngày kết thúc phải sau ngày bắt đầu");
    }
}

public class CreateProjectCommandHandler(IApplicationDbContext db) : IRequestHandler<CreateProjectCommand, Guid>
{
    public async Task<Guid> Handle(CreateProjectCommand request, CancellationToken ct)
    {
        // [Fix UAT — dự án trùng do khác dấu-cách trong mã: "FLOCK 01" vs "FLOCK01"]
        // Owner (chốt): LƯU chỉ cắt đầu/cuối (Trim) — giữ mã như người dùng gõ; nhưng
        // KIỂM-TRÙNG bỏ-qua-dấu-cách để chặn tạo bản trùng chỉ khác khoảng trắng.
        // (Collation SQL mặc định CI → đã bỏ-qua hoa/thường; đây thêm bỏ-qua dấu-cách.)
        var code = request.Code.Trim();
        var codeKey = code.Replace(" ", "");
        if (await db.Projects.AnyAsync(x => x.Code.Replace(" ", "") == codeKey, ct))
            throw new ConflictException($"Mã dự án '{code}' đã tồn tại (trùng mã sau khi bỏ dấu cách).");
        var entity = new Project
        {
            Code = code, Name = request.Name,
            StartDate = request.StartDate, EndDate = request.EndDate,
            ManagerUserId = request.ManagerUserId, BudgetTotal = request.BudgetTotal, Note = request.Note,
            Year = request.Year, Investor = request.Investor, Location = request.Location, Package = request.Package,
        };
        db.Projects.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

// ===================== UPDATE =====================
public record UpdateProjectCommand(
    Guid Id, string Code, string Name, DateTime? StartDate, DateTime? EndDate,
    Guid? ManagerUserId, decimal? BudgetTotal, string? Note,
    int? Year, string? Investor, string? Location, string? Package) : IRequest;

public class UpdateProjectCommandValidator : AbstractValidator<UpdateProjectCommand>
{
    public UpdateProjectCommandValidator()
    {
        RuleFor(x => x.Id).NotEmpty();
        RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.BudgetTotal).GreaterThanOrEqualTo(0).When(x => x.BudgetTotal.HasValue);
        RuleFor(x => x.Investor).MaximumLength(250);
        RuleFor(x => x.Location).MaximumLength(500);
        RuleFor(x => x.Package).MaximumLength(300);
        RuleFor(x => x).Must(x => !x.StartDate.HasValue || !x.EndDate.HasValue || x.EndDate >= x.StartDate)
            .WithMessage("Ngày kết thúc phải sau ngày bắt đầu");
    }
}

public class UpdateProjectCommandHandler(IApplicationDbContext db) : IRequestHandler<UpdateProjectCommand>
{
    public async Task Handle(UpdateProjectCommand request, CancellationToken ct)
    {
        var entity = await db.Projects.FirstOrDefaultAsync(x => x.Id == request.Id, ct)
            ?? throw new NotFoundException("Project", request.Id);
        // Chuẩn hóa mã như CREATE: Trim khi lưu + kiểm-trùng bỏ-qua-dấu-cách.
        var code = request.Code.Trim();
        var codeKey = code.Replace(" ", "");
        if (entity.Code != code && await db.Projects.AnyAsync(x => x.Id != request.Id && x.Code.Replace(" ", "") == codeKey, ct))
            throw new ConflictException($"Mã dự án '{code}' đã tồn tại (trùng mã sau khi bỏ dấu cách).");
        entity.Code = code;
        entity.Name = request.Name;
        entity.StartDate = request.StartDate;
        entity.EndDate = request.EndDate;
        entity.ManagerUserId = request.ManagerUserId;
        entity.BudgetTotal = request.BudgetTotal;
        entity.Note = request.Note;
        entity.Year = request.Year;
        entity.Investor = request.Investor;
        entity.Location = request.Location;
        entity.Package = request.Package;
        await db.SaveChangesAsync(ct);
    }
}

// ===================== DELETE =====================
public record DeleteProjectCommand(Guid Id) : IRequest;

public class DeleteProjectCommandHandler(IApplicationDbContext db) : IRequestHandler<DeleteProjectCommand>
{
    public async Task Handle(DeleteProjectCommand request, CancellationToken ct)
    {
        var entity = await db.Projects.FirstOrDefaultAsync(x => x.Id == request.Id, ct)
            ?? throw new NotFoundException("Project", request.Id);
        db.Projects.Remove(entity);
        await db.SaveChangesAsync(ct);
    }
}
