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;

// CRUD CQRS cho 4 master catalogs (Units, Materials, Services, WorkItems).
// Pattern lặp đi lặp lại với mỗi catalog — gộp 1 file để dễ maintain, mỗi
// catalog 5 handlers (List + GetById + Create + Update + Delete).
//
// Endpoints expose qua CatalogsController:
//   GET    /api/catalogs/{kind}                — list (filter q + category)
//   GET    /api/catalogs/{kind}/{id}           — get by id
//   POST   /api/catalogs/{kind}                — create
//   PUT    /api/catalogs/{kind}/{id}           — update
//   DELETE /api/catalogs/{kind}/{id}           — soft delete
// kind ∈ {units, materials, services, work-items}

// ===== DTOs =====

public record UnitOfMeasureDto(Guid Id, string Code, string Name, string? Description);
public record MaterialItemDto(Guid Id, string Code, string Name, string? Category, string? DefaultUnit, string? Specification, string? OriginCountry, bool IsActive);
public record ServiceItemDto(Guid Id, string Code, string Name, string? Category, string? DefaultUnit, string? Description, bool IsActive);
public record WorkItemDto(Guid Id, string Code, string Name, string? Category, string? DefaultUnit, string? Description, bool IsActive);

// ===== UnitOfMeasure =====

public record ListUnitsQuery(string? Q = null) : IRequest<List<UnitOfMeasureDto>>;
public class ListUnitsHandler(IApplicationDbContext db) : IRequestHandler<ListUnitsQuery, List<UnitOfMeasureDto>>
{
    public async Task<List<UnitOfMeasureDto>> Handle(ListUnitsQuery req, CancellationToken ct)
    {
        var q = db.UnitsOfMeasure.AsNoTracking().AsQueryable();
        if (!string.IsNullOrWhiteSpace(req.Q))
        {
            var s = req.Q.ToLower();
            q = q.Where(x => x.Code.ToLower().Contains(s) || x.Name.ToLower().Contains(s));
        }
        return await q.OrderBy(x => x.Code)
            .Select(x => new UnitOfMeasureDto(x.Id, x.Code, x.Name, x.Description))
            .ToListAsync(ct);
    }
}

public record CreateUnitCommand(string Code, string Name, string? Description) : IRequest<Guid>;
public class CreateUnitValidator : AbstractValidator<CreateUnitCommand>
{
    public CreateUnitValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(20);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(100);
    }
}
public class CreateUnitHandler(IApplicationDbContext db) : IRequestHandler<CreateUnitCommand, Guid>
{
    public async Task<Guid> Handle(CreateUnitCommand req, CancellationToken ct)
    {
        if (await db.UnitsOfMeasure.AnyAsync(x => x.Code == req.Code, ct))
            throw new ConflictException($"Mã '{req.Code}' đã tồn tại.");
        var entity = new UnitOfMeasure { Code = req.Code, Name = req.Name, Description = req.Description };
        db.UnitsOfMeasure.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateUnitCommand(Guid Id, string Code, string Name, string? Description) : IRequest;
public class UpdateUnitHandler(IApplicationDbContext db) : IRequestHandler<UpdateUnitCommand>
{
    public async Task Handle(UpdateUnitCommand req, CancellationToken ct)
    {
        var entity = await db.UnitsOfMeasure.FirstOrDefaultAsync(x => x.Id == req.Id, ct)
            ?? throw new NotFoundException("UnitOfMeasure", req.Id);
        if (entity.Code != req.Code && await db.UnitsOfMeasure.AnyAsync(x => x.Code == req.Code, ct))
            throw new ConflictException($"Mã '{req.Code}' đã tồn tại.");
        entity.Code = req.Code;
        entity.Name = req.Name;
        entity.Description = req.Description;
        await db.SaveChangesAsync(ct);
    }
}

public record DeleteUnitCommand(Guid Id) : IRequest;
public class DeleteUnitHandler(IApplicationDbContext db) : IRequestHandler<DeleteUnitCommand>
{
    public async Task Handle(DeleteUnitCommand req, CancellationToken ct)
    {
        var entity = await db.UnitsOfMeasure.FirstOrDefaultAsync(x => x.Id == req.Id, ct)
            ?? throw new NotFoundException("UnitOfMeasure", req.Id);
        db.UnitsOfMeasure.Remove(entity);  // Soft delete via AuditingInterceptor
        await db.SaveChangesAsync(ct);
    }
}

// ===== MaterialItem =====

public record ListMaterialsQuery(string? Q = null, string? Category = null) : IRequest<List<MaterialItemDto>>;
public class ListMaterialsHandler(IApplicationDbContext db) : IRequestHandler<ListMaterialsQuery, List<MaterialItemDto>>
{
    public async Task<List<MaterialItemDto>> Handle(ListMaterialsQuery req, CancellationToken ct)
    {
        var q = db.MaterialItems.AsNoTracking().AsQueryable();
        if (!string.IsNullOrWhiteSpace(req.Q))
        {
            var s = req.Q.ToLower();
            q = q.Where(x => x.Code.ToLower().Contains(s) || x.Name.ToLower().Contains(s));
        }
        if (!string.IsNullOrWhiteSpace(req.Category))
            q = q.Where(x => x.Category == req.Category);
        return await q.OrderBy(x => x.Category).ThenBy(x => x.Code)
            .Select(x => new MaterialItemDto(x.Id, x.Code, x.Name, x.Category, x.DefaultUnit, x.Specification, x.OriginCountry, x.IsActive))
            .ToListAsync(ct);
    }
}

public record CreateMaterialCommand(string Code, string Name, string? Category, string? DefaultUnit, string? Specification, string? OriginCountry, bool IsActive) : IRequest<Guid>;
public class CreateMaterialValidator : AbstractValidator<CreateMaterialCommand>
{
    public CreateMaterialValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
    }
}
public class CreateMaterialHandler(IApplicationDbContext db) : IRequestHandler<CreateMaterialCommand, Guid>
{
    public async Task<Guid> Handle(CreateMaterialCommand req, CancellationToken ct)
    {
        if (await db.MaterialItems.AnyAsync(x => x.Code == req.Code, ct))
            throw new ConflictException($"Mã '{req.Code}' đã tồn tại.");
        var entity = new MaterialItem
        {
            Code = req.Code, Name = req.Name, Category = req.Category,
            DefaultUnit = req.DefaultUnit, Specification = req.Specification,
            OriginCountry = req.OriginCountry, IsActive = req.IsActive,
        };
        db.MaterialItems.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateMaterialCommand(Guid Id, string Code, string Name, string? Category, string? DefaultUnit, string? Specification, string? OriginCountry, bool IsActive) : IRequest;
public class UpdateMaterialHandler(IApplicationDbContext db) : IRequestHandler<UpdateMaterialCommand>
{
    public async Task Handle(UpdateMaterialCommand req, CancellationToken ct)
    {
        var entity = await db.MaterialItems.FirstOrDefaultAsync(x => x.Id == req.Id, ct)
            ?? throw new NotFoundException("MaterialItem", req.Id);
        if (entity.Code != req.Code && await db.MaterialItems.AnyAsync(x => x.Code == req.Code, ct))
            throw new ConflictException($"Mã '{req.Code}' đã tồn tại.");
        entity.Code = req.Code;
        entity.Name = req.Name;
        entity.Category = req.Category;
        entity.DefaultUnit = req.DefaultUnit;
        entity.Specification = req.Specification;
        entity.OriginCountry = req.OriginCountry;
        entity.IsActive = req.IsActive;
        await db.SaveChangesAsync(ct);
    }
}

public record DeleteMaterialCommand(Guid Id) : IRequest;
public class DeleteMaterialHandler(IApplicationDbContext db) : IRequestHandler<DeleteMaterialCommand>
{
    public async Task Handle(DeleteMaterialCommand req, CancellationToken ct)
    {
        var entity = await db.MaterialItems.FirstOrDefaultAsync(x => x.Id == req.Id, ct)
            ?? throw new NotFoundException("MaterialItem", req.Id);
        db.MaterialItems.Remove(entity);
        await db.SaveChangesAsync(ct);
    }
}

// ===== ServiceItem =====

public record ListServicesQuery(string? Q = null, string? Category = null) : IRequest<List<ServiceItemDto>>;
public class ListServicesHandler(IApplicationDbContext db) : IRequestHandler<ListServicesQuery, List<ServiceItemDto>>
{
    public async Task<List<ServiceItemDto>> Handle(ListServicesQuery req, CancellationToken ct)
    {
        var q = db.ServiceItems.AsNoTracking().AsQueryable();
        if (!string.IsNullOrWhiteSpace(req.Q))
        {
            var s = req.Q.ToLower();
            q = q.Where(x => x.Code.ToLower().Contains(s) || x.Name.ToLower().Contains(s))
;
        }
        if (!string.IsNullOrWhiteSpace(req.Category))
            q = q.Where(x => x.Category == req.Category)
;
        return await q.OrderBy(x => x.Category).ThenBy(x => x.Code)
            .Select(x => new ServiceItemDto(x.Id, x.Code, x.Name, x.Category, x.DefaultUnit, x.Description, x.IsActive))
            .ToListAsync(ct);
    }
}

public record CreateServiceCommand(string Code, string Name, string? Category, string? DefaultUnit, string? Description, bool IsActive) : IRequest<Guid>;
public class CreateServiceValidator : AbstractValidator<CreateServiceCommand>
{
    public CreateServiceValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
    }
}
public class CreateServiceHandler(IApplicationDbContext db) : IRequestHandler<CreateServiceCommand, Guid>
{
    public async Task<Guid> Handle(CreateServiceCommand req, CancellationToken ct)
    {
        if (await db.ServiceItems.AnyAsync(x => x.Code == req.Code, ct))
            throw new ConflictException($"Mã '{req.Code}' đã tồn tại.");
        var entity = new ServiceItem
        {
            Code = req.Code, Name = req.Name, Category = req.Category,
            DefaultUnit = req.DefaultUnit, Description = req.Description, IsActive = req.IsActive,
        };
        db.ServiceItems.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateServiceCommand(Guid Id, string Code, string Name, string? Category, string? DefaultUnit, string? Description, bool IsActive) : IRequest;
public class UpdateServiceHandler(IApplicationDbContext db) : IRequestHandler<UpdateServiceCommand>
{
    public async Task Handle(UpdateServiceCommand req, CancellationToken ct)
    {
        var entity = await db.ServiceItems.FirstOrDefaultAsync(x => x.Id == req.Id, ct)
            ?? throw new NotFoundException("ServiceItem", req.Id);
        if (entity.Code != req.Code && await db.ServiceItems.AnyAsync(x => x.Code == req.Code, ct))
            throw new ConflictException($"Mã '{req.Code}' đã tồn tại.");
        entity.Code = req.Code;
        entity.Name = req.Name;
        entity.Category = req.Category;
        entity.DefaultUnit = req.DefaultUnit;
        entity.Description = req.Description;
        entity.IsActive = req.IsActive;
        await db.SaveChangesAsync(ct);
    }
}

public record DeleteServiceCommand(Guid Id) : IRequest;
public class DeleteServiceHandler(IApplicationDbContext db) : IRequestHandler<DeleteServiceCommand>
{
    public async Task Handle(DeleteServiceCommand req, CancellationToken ct)
    {
        var entity = await db.ServiceItems.FirstOrDefaultAsync(x => x.Id == req.Id, ct)
            ?? throw new NotFoundException("ServiceItem", req.Id);
        db.ServiceItems.Remove(entity);
        await db.SaveChangesAsync(ct);
    }
}

// ===== WorkItem =====

public record ListWorkItemsQuery(string? Q = null, string? Category = null) : IRequest<List<WorkItemDto>>;
public class ListWorkItemsHandler(IApplicationDbContext db) : IRequestHandler<ListWorkItemsQuery, List<WorkItemDto>>
{
    public async Task<List<WorkItemDto>> Handle(ListWorkItemsQuery req, CancellationToken ct)
    {
        var q = db.WorkItems.AsNoTracking().AsQueryable();
        if (!string.IsNullOrWhiteSpace(req.Q))
        {
            var s = req.Q.ToLower();
            q = q.Where(x => x.Code.ToLower().Contains(s) || x.Name.ToLower().Contains(s));
        }
        if (!string.IsNullOrWhiteSpace(req.Category))
            q = q.Where(x => x.Category == req.Category);
        return await q.OrderBy(x => x.Category).ThenBy(x => x.Code)
            .Select(x => new WorkItemDto(x.Id, x.Code, x.Name, x.Category, x.DefaultUnit, x.Description, x.IsActive))
            .ToListAsync(ct);
    }
}

public record CreateWorkItemCommand(string Code, string Name, string? Category, string? DefaultUnit, string? Description, bool IsActive) : IRequest<Guid>;
public class CreateWorkItemValidator : AbstractValidator<CreateWorkItemCommand>
{
    public CreateWorkItemValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
    }
}
public class CreateWorkItemHandler(IApplicationDbContext db) : IRequestHandler<CreateWorkItemCommand, Guid>
{
    public async Task<Guid> Handle(CreateWorkItemCommand req, CancellationToken ct)
    {
        if (await db.WorkItems.AnyAsync(x => x.Code == req.Code, ct))
            throw new ConflictException($"Mã '{req.Code}' đã tồn tại.");
        var entity = new WorkItem
        {
            Code = req.Code, Name = req.Name, Category = req.Category,
            DefaultUnit = req.DefaultUnit, Description = req.Description, IsActive = req.IsActive,
        };
        db.WorkItems.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateWorkItemCommand(Guid Id, string Code, string Name, string? Category, string? DefaultUnit, string? Description, bool IsActive) : IRequest;
public class UpdateWorkItemHandler(IApplicationDbContext db) : IRequestHandler<UpdateWorkItemCommand>
{
    public async Task Handle(UpdateWorkItemCommand req, CancellationToken ct)
    {
        var entity = await db.WorkItems.FirstOrDefaultAsync(x => x.Id == req.Id, ct)
            ?? throw new NotFoundException("WorkItem", req.Id);
        if (entity.Code != req.Code && await db.WorkItems.AnyAsync(x => x.Code == req.Code, ct))
            throw new ConflictException($"Mã '{req.Code}' đã tồn tại.");
        entity.Code = req.Code;
        entity.Name = req.Name;
        entity.Category = req.Category;
        entity.DefaultUnit = req.DefaultUnit;
        entity.Description = req.Description;
        entity.IsActive = req.IsActive;
        await db.SaveChangesAsync(ct);
    }
}

public record DeleteWorkItemCommand(Guid Id) : IRequest;
public class DeleteWorkItemHandler(IApplicationDbContext db) : IRequestHandler<DeleteWorkItemCommand>
{
    public async Task Handle(DeleteWorkItemCommand req, CancellationToken ct)
    {
        var entity = await db.WorkItems.FirstOrDefaultAsync(x => x.Id == req.Id, ct)
            ?? throw new NotFoundException("WorkItem", req.Id);
        db.WorkItems.Remove(entity);
        await db.SaveChangesAsync(ct);
    }
}
