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

namespace SolutionErp.Application.Hrm;

// CRUD CQRS cho 4 HRM catalog (LeaveTypes/Holidays/Shifts/OtPolicies).
// Pattern 12-bis mega mirror Master/Catalogs/CatalogsFeatures.cs (S29 cumulative 2×).
// Endpoints expose qua HrmConfigsController:
//   GET    /api/hrm-configs/{kind}                — list (filter q + isActive / year)
//   POST   /api/hrm-configs/{kind}                — create
//   PUT    /api/hrm-configs/{kind}/{id}           — update
//   DELETE /api/hrm-configs/{kind}/{id}           — soft delete via AuditingInterceptor
// kind ∈ {leave-types, holidays, shifts, ot-policies}
//
// MaxLength validator MATCH EF config exact (LeaveTypeConfiguration etc) — avoid runtime
// truncation. HRM entities KHÔNG có global HasQueryFilter (khác Master/Catalogs) → list
// query phải Where(!IsDeleted) thủ công.

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

public record LeaveTypeDto(Guid Id, string Code, string Name, decimal DaysPerYear, bool IsPaid, bool RequiresAttachment, bool IsActive, string? Description);
public record HolidayDto(Guid Id, int Year, DateOnly Date, string Name, bool IsRecurring, bool IsPaid, bool IsActive, string? Description);
public record ShiftPatternDto(Guid Id, string Code, string Name, TimeOnly StartTime, TimeOnly EndTime, int BreakMinutes, string WorkDays, bool IsActive, string? Description);
public record OtPolicyDto(Guid Id, string Code, string Name, decimal MultiplierWeekday, decimal MultiplierWeekend, decimal MultiplierHoliday, int MaxHoursPerDay, int MaxHoursPerMonth, int MaxHoursPerYear, bool IsActive, string? Description);
public record VehicleDto(Guid Id, string Code, string Name, string LicensePlate, int SeatCount, bool IsActive, string? Description);
public record DriverDto(Guid Id, string Code, string Name, string PhoneNumber, string LicenseNumber, string LicenseClass, bool IsActive, string? Description);

// ===== Region 1: LeaveType =====

public record ListLeaveTypesQuery(string? Q = null, bool? IsActive = null) : IRequest<List<LeaveTypeDto>>;
public class ListLeaveTypesHandler(IApplicationDbContext db) : IRequestHandler<ListLeaveTypesQuery, List<LeaveTypeDto>>
{
    public async Task<List<LeaveTypeDto>> Handle(ListLeaveTypesQuery req, CancellationToken ct)
    {
        var q = db.LeaveTypes.AsNoTracking().Where(x => !x.IsDeleted);
        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 (req.IsActive.HasValue) q = q.Where(x => x.IsActive == req.IsActive.Value);
        return await q.OrderBy(x => x.Code)
            .Select(x => new LeaveTypeDto(x.Id, x.Code, x.Name, x.DaysPerYear, x.IsPaid, x.RequiresAttachment, x.IsActive, x.Description))
            .ToListAsync(ct);
    }
}

public record CreateLeaveTypeCommand(string Code, string Name, decimal DaysPerYear, bool IsPaid, bool RequiresAttachment, string? Description) : IRequest<Guid>;
public class CreateLeaveTypeValidator : AbstractValidator<CreateLeaveTypeCommand>
{
    public CreateLeaveTypeValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.DaysPerYear).GreaterThanOrEqualTo(0);
        RuleFor(x => x.Description).MaximumLength(500);
    }
}
public class CreateLeaveTypeHandler(IApplicationDbContext db) : IRequestHandler<CreateLeaveTypeCommand, Guid>
{
    public async Task<Guid> Handle(CreateLeaveTypeCommand req, CancellationToken ct)
    {
        if (await db.LeaveTypes.AnyAsync(x => x.Code == req.Code && !x.IsDeleted, ct))
            throw new ConflictException($"Mã loại phép '{req.Code}' đã tồn tại.");
        var entity = new LeaveType
        {
            Code = req.Code,
            Name = req.Name,
            DaysPerYear = req.DaysPerYear,
            IsPaid = req.IsPaid,
            RequiresAttachment = req.RequiresAttachment,
            Description = req.Description,
            IsActive = true,
        };
        db.LeaveTypes.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateLeaveTypeCommand(Guid Id, string Code, string Name, decimal DaysPerYear, bool IsPaid, bool RequiresAttachment, bool IsActive, string? Description) : IRequest;
public class UpdateLeaveTypeValidator : AbstractValidator<UpdateLeaveTypeCommand>
{
    public UpdateLeaveTypeValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.DaysPerYear).GreaterThanOrEqualTo(0);
        RuleFor(x => x.Description).MaximumLength(500);
    }
}
public class UpdateLeaveTypeHandler(IApplicationDbContext db) : IRequestHandler<UpdateLeaveTypeCommand>
{
    public async Task Handle(UpdateLeaveTypeCommand req, CancellationToken ct)
    {
        var entity = await db.LeaveTypes.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("LeaveType", req.Id);
        if (entity.Code != req.Code && await db.LeaveTypes.AnyAsync(x => x.Code == req.Code && !x.IsDeleted, ct))
            throw new ConflictException($"Mã loại phép '{req.Code}' đã tồn tại.");
        entity.Code = req.Code;
        entity.Name = req.Name;
        entity.DaysPerYear = req.DaysPerYear;
        entity.IsPaid = req.IsPaid;
        entity.RequiresAttachment = req.RequiresAttachment;
        entity.IsActive = req.IsActive;
        entity.Description = req.Description;
        await db.SaveChangesAsync(ct);
    }
}

public record DeleteLeaveTypeCommand(Guid Id) : IRequest;
public class DeleteLeaveTypeHandler(IApplicationDbContext db) : IRequestHandler<DeleteLeaveTypeCommand>
{
    public async Task Handle(DeleteLeaveTypeCommand req, CancellationToken ct)
    {
        var entity = await db.LeaveTypes.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("LeaveType", req.Id);
        db.LeaveTypes.Remove(entity);  // Soft delete via AuditingInterceptor
        await db.SaveChangesAsync(ct);
    }
}

// ===== Region 2: Holiday =====
// UNIQUE composite (Year, Date) — Code field không có.
// List filter Year nullable: null = trả về all year, có value = chỉ year đó.

public record ListHolidaysQuery(string? Q = null, int? Year = null) : IRequest<List<HolidayDto>>;
public class ListHolidaysHandler(IApplicationDbContext db) : IRequestHandler<ListHolidaysQuery, List<HolidayDto>>
{
    public async Task<List<HolidayDto>> Handle(ListHolidaysQuery req, CancellationToken ct)
    {
        var q = db.Holidays.AsNoTracking().Where(x => !x.IsDeleted);
        if (req.Year.HasValue) q = q.Where(x => x.Year == req.Year.Value);
        if (!string.IsNullOrWhiteSpace(req.Q))
        {
            var s = req.Q.ToLower();
            q = q.Where(x => x.Name.ToLower().Contains(s));
        }
        return await q.OrderBy(x => x.Year).ThenBy(x => x.Date)
            .Select(x => new HolidayDto(x.Id, x.Year, x.Date, x.Name, x.IsRecurring, x.IsPaid, x.IsActive, x.Description))
            .ToListAsync(ct);
    }
}

public record CreateHolidayCommand(int Year, DateOnly Date, string Name, bool IsRecurring, bool IsPaid, string? Description) : IRequest<Guid>;
public class CreateHolidayValidator : AbstractValidator<CreateHolidayCommand>
{
    public CreateHolidayValidator()
    {
        RuleFor(x => x.Year).InclusiveBetween(2000, 2100);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.Description).MaximumLength(500);
    }
}
public class CreateHolidayHandler(IApplicationDbContext db) : IRequestHandler<CreateHolidayCommand, Guid>
{
    public async Task<Guid> Handle(CreateHolidayCommand req, CancellationToken ct)
    {
        if (await db.Holidays.AnyAsync(x => x.Year == req.Year && x.Date == req.Date && !x.IsDeleted, ct))
            throw new ConflictException($"Ngày lễ năm {req.Year} ngày {req.Date:dd/MM/yyyy} đã tồn tại.");
        var entity = new Holiday
        {
            Year = req.Year,
            Date = req.Date,
            Name = req.Name,
            IsRecurring = req.IsRecurring,
            IsPaid = req.IsPaid,
            Description = req.Description,
            IsActive = true,
        };
        db.Holidays.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateHolidayCommand(Guid Id, int Year, DateOnly Date, string Name, bool IsRecurring, bool IsPaid, bool IsActive, string? Description) : IRequest;
public class UpdateHolidayValidator : AbstractValidator<UpdateHolidayCommand>
{
    public UpdateHolidayValidator()
    {
        RuleFor(x => x.Year).InclusiveBetween(2000, 2100);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.Description).MaximumLength(500);
    }
}
public class UpdateHolidayHandler(IApplicationDbContext db) : IRequestHandler<UpdateHolidayCommand>
{
    public async Task Handle(UpdateHolidayCommand req, CancellationToken ct)
    {
        var entity = await db.Holidays.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("Holiday", req.Id);
        // Check conflict composite (Year, Date) khi 1 trong 2 đổi
        if ((entity.Year != req.Year || entity.Date != req.Date)
            && await db.Holidays.AnyAsync(x => x.Year == req.Year && x.Date == req.Date && !x.IsDeleted && x.Id != req.Id, ct))
            throw new ConflictException($"Ngày lễ năm {req.Year} ngày {req.Date:dd/MM/yyyy} đã tồn tại.");
        entity.Year = req.Year;
        entity.Date = req.Date;
        entity.Name = req.Name;
        entity.IsRecurring = req.IsRecurring;
        entity.IsPaid = req.IsPaid;
        entity.IsActive = req.IsActive;
        entity.Description = req.Description;
        await db.SaveChangesAsync(ct);
    }
}

public record DeleteHolidayCommand(Guid Id) : IRequest;
public class DeleteHolidayHandler(IApplicationDbContext db) : IRequestHandler<DeleteHolidayCommand>
{
    public async Task Handle(DeleteHolidayCommand req, CancellationToken ct)
    {
        var entity = await db.Holidays.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("Holiday", req.Id);
        db.Holidays.Remove(entity);
        await db.SaveChangesAsync(ct);
    }
}

// ===== Region 3: ShiftPattern =====
// Code UNIQUE (entity MaxLen 20 — khác LeaveType/OtPolicy 50).
// Validator extra: StartTime != EndTime (denote ca làm việc khác giờ thật).
// WorkDays NotEmpty đủ — FE Designer multi-select sẽ format "Mon,Tue,..."

public record ListShiftsQuery(string? Q = null, bool? IsActive = null) : IRequest<List<ShiftPatternDto>>;
public class ListShiftsHandler(IApplicationDbContext db) : IRequestHandler<ListShiftsQuery, List<ShiftPatternDto>>
{
    public async Task<List<ShiftPatternDto>> Handle(ListShiftsQuery req, CancellationToken ct)
    {
        var q = db.ShiftPatterns.AsNoTracking().Where(x => !x.IsDeleted);
        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 (req.IsActive.HasValue) q = q.Where(x => x.IsActive == req.IsActive.Value);
        return await q.OrderBy(x => x.Code)
            .Select(x => new ShiftPatternDto(x.Id, x.Code, x.Name, x.StartTime, x.EndTime, x.BreakMinutes, x.WorkDays, x.IsActive, x.Description))
            .ToListAsync(ct);
    }
}

public record CreateShiftCommand(string Code, string Name, TimeOnly StartTime, TimeOnly EndTime, int BreakMinutes, string WorkDays, string? Description) : IRequest<Guid>;
public class CreateShiftValidator : AbstractValidator<CreateShiftCommand>
{
    public CreateShiftValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(20);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.WorkDays).NotEmpty().MaximumLength(100);
        RuleFor(x => x.BreakMinutes).GreaterThanOrEqualTo(0);
        RuleFor(x => x.Description).MaximumLength(500);
        RuleFor(x => x).Must(c => c.StartTime != c.EndTime)
            .WithMessage("Giờ bắt đầu và giờ kết thúc không được trùng nhau.");
    }
}
public class CreateShiftHandler(IApplicationDbContext db) : IRequestHandler<CreateShiftCommand, Guid>
{
    public async Task<Guid> Handle(CreateShiftCommand req, CancellationToken ct)
    {
        if (await db.ShiftPatterns.AnyAsync(x => x.Code == req.Code && !x.IsDeleted, ct))
            throw new ConflictException($"Mã ca làm việc '{req.Code}' đã tồn tại.");
        var entity = new ShiftPattern
        {
            Code = req.Code,
            Name = req.Name,
            StartTime = req.StartTime,
            EndTime = req.EndTime,
            BreakMinutes = req.BreakMinutes,
            WorkDays = req.WorkDays,
            Description = req.Description,
            IsActive = true,
        };
        db.ShiftPatterns.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateShiftCommand(Guid Id, string Code, string Name, TimeOnly StartTime, TimeOnly EndTime, int BreakMinutes, string WorkDays, bool IsActive, string? Description) : IRequest;
public class UpdateShiftValidator : AbstractValidator<UpdateShiftCommand>
{
    public UpdateShiftValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(20);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.WorkDays).NotEmpty().MaximumLength(100);
        RuleFor(x => x.BreakMinutes).GreaterThanOrEqualTo(0);
        RuleFor(x => x.Description).MaximumLength(500);
        RuleFor(x => x).Must(c => c.StartTime != c.EndTime)
            .WithMessage("Giờ bắt đầu và giờ kết thúc không được trùng nhau.");
    }
}
public class UpdateShiftHandler(IApplicationDbContext db) : IRequestHandler<UpdateShiftCommand>
{
    public async Task Handle(UpdateShiftCommand req, CancellationToken ct)
    {
        var entity = await db.ShiftPatterns.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("ShiftPattern", req.Id);
        if (entity.Code != req.Code && await db.ShiftPatterns.AnyAsync(x => x.Code == req.Code && !x.IsDeleted, ct))
            throw new ConflictException($"Mã ca làm việc '{req.Code}' đã tồn tại.");
        entity.Code = req.Code;
        entity.Name = req.Name;
        entity.StartTime = req.StartTime;
        entity.EndTime = req.EndTime;
        entity.BreakMinutes = req.BreakMinutes;
        entity.WorkDays = req.WorkDays;
        entity.IsActive = req.IsActive;
        entity.Description = req.Description;
        await db.SaveChangesAsync(ct);
    }
}

public record DeleteShiftCommand(Guid Id) : IRequest;
public class DeleteShiftHandler(IApplicationDbContext db) : IRequestHandler<DeleteShiftCommand>
{
    public async Task Handle(DeleteShiftCommand req, CancellationToken ct)
    {
        var entity = await db.ShiftPatterns.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("ShiftPattern", req.Id);
        db.ShiftPatterns.Remove(entity);
        await db.SaveChangesAsync(ct);
    }
}

// ===== Region 4: OtPolicy =====
// Code UNIQUE. KHÔNG auto-deactivate existing IsActive=true (admin manually toggle qua Update).
// Validator: Multipliers >= 1.0 (không thể nhỏ hơn giờ bình thường) + MaxHours > 0.

public record ListOtPoliciesQuery(string? Q = null, bool? IsActive = null) : IRequest<List<OtPolicyDto>>;
public class ListOtPoliciesHandler(IApplicationDbContext db) : IRequestHandler<ListOtPoliciesQuery, List<OtPolicyDto>>
{
    public async Task<List<OtPolicyDto>> Handle(ListOtPoliciesQuery req, CancellationToken ct)
    {
        var q = db.OtPolicies.AsNoTracking().Where(x => !x.IsDeleted);
        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 (req.IsActive.HasValue) q = q.Where(x => x.IsActive == req.IsActive.Value);
        return await q.OrderBy(x => x.Code)
            .Select(x => new OtPolicyDto(x.Id, x.Code, x.Name, x.MultiplierWeekday, x.MultiplierWeekend, x.MultiplierHoliday, x.MaxHoursPerDay, x.MaxHoursPerMonth, x.MaxHoursPerYear, x.IsActive, x.Description))
            .ToListAsync(ct);
    }
}

public record CreateOtPolicyCommand(string Code, string Name, decimal MultiplierWeekday, decimal MultiplierWeekend, decimal MultiplierHoliday, int MaxHoursPerDay, int MaxHoursPerMonth, int MaxHoursPerYear, string? Description) : IRequest<Guid>;
public class CreateOtPolicyValidator : AbstractValidator<CreateOtPolicyCommand>
{
    public CreateOtPolicyValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.MultiplierWeekday).GreaterThanOrEqualTo(1.0m);
        RuleFor(x => x.MultiplierWeekend).GreaterThanOrEqualTo(1.0m);
        RuleFor(x => x.MultiplierHoliday).GreaterThanOrEqualTo(1.0m);
        RuleFor(x => x.MaxHoursPerDay).GreaterThan(0);
        RuleFor(x => x.MaxHoursPerMonth).GreaterThan(0);
        RuleFor(x => x.MaxHoursPerYear).GreaterThan(0);
        RuleFor(x => x.Description).MaximumLength(500);
    }
}
public class CreateOtPolicyHandler(IApplicationDbContext db) : IRequestHandler<CreateOtPolicyCommand, Guid>
{
    public async Task<Guid> Handle(CreateOtPolicyCommand req, CancellationToken ct)
    {
        if (await db.OtPolicies.AnyAsync(x => x.Code == req.Code && !x.IsDeleted, ct))
            throw new ConflictException($"Mã chính sách OT '{req.Code}' đã tồn tại.");
        var entity = new OtPolicy
        {
            Code = req.Code,
            Name = req.Name,
            MultiplierWeekday = req.MultiplierWeekday,
            MultiplierWeekend = req.MultiplierWeekend,
            MultiplierHoliday = req.MultiplierHoliday,
            MaxHoursPerDay = req.MaxHoursPerDay,
            MaxHoursPerMonth = req.MaxHoursPerMonth,
            MaxHoursPerYear = req.MaxHoursPerYear,
            Description = req.Description,
            IsActive = true,
        };
        db.OtPolicies.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateOtPolicyCommand(Guid Id, string Code, string Name, decimal MultiplierWeekday, decimal MultiplierWeekend, decimal MultiplierHoliday, int MaxHoursPerDay, int MaxHoursPerMonth, int MaxHoursPerYear, bool IsActive, string? Description) : IRequest;
public class UpdateOtPolicyValidator : AbstractValidator<UpdateOtPolicyCommand>
{
    public UpdateOtPolicyValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.MultiplierWeekday).GreaterThanOrEqualTo(1.0m);
        RuleFor(x => x.MultiplierWeekend).GreaterThanOrEqualTo(1.0m);
        RuleFor(x => x.MultiplierHoliday).GreaterThanOrEqualTo(1.0m);
        RuleFor(x => x.MaxHoursPerDay).GreaterThan(0);
        RuleFor(x => x.MaxHoursPerMonth).GreaterThan(0);
        RuleFor(x => x.MaxHoursPerYear).GreaterThan(0);
        RuleFor(x => x.Description).MaximumLength(500);
    }
}
public class UpdateOtPolicyHandler(IApplicationDbContext db) : IRequestHandler<UpdateOtPolicyCommand>
{
    public async Task Handle(UpdateOtPolicyCommand req, CancellationToken ct)
    {
        var entity = await db.OtPolicies.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("OtPolicy", req.Id);
        if (entity.Code != req.Code && await db.OtPolicies.AnyAsync(x => x.Code == req.Code && !x.IsDeleted, ct))
            throw new ConflictException($"Mã chính sách OT '{req.Code}' đã tồn tại.");
        entity.Code = req.Code;
        entity.Name = req.Name;
        entity.MultiplierWeekday = req.MultiplierWeekday;
        entity.MultiplierWeekend = req.MultiplierWeekend;
        entity.MultiplierHoliday = req.MultiplierHoliday;
        entity.MaxHoursPerDay = req.MaxHoursPerDay;
        entity.MaxHoursPerMonth = req.MaxHoursPerMonth;
        entity.MaxHoursPerYear = req.MaxHoursPerYear;
        entity.IsActive = req.IsActive;
        entity.Description = req.Description;
        await db.SaveChangesAsync(ct);
    }
}

public record DeleteOtPolicyCommand(Guid Id) : IRequest;
public class DeleteOtPolicyHandler(IApplicationDbContext db) : IRequestHandler<DeleteOtPolicyCommand>
{
    public async Task Handle(DeleteOtPolicyCommand req, CancellationToken ct)
    {
        var entity = await db.OtPolicies.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("OtPolicy", req.Id);
        db.OtPolicies.Remove(entity);
        await db.SaveChangesAsync(ct);
    }
}

// ===== Region 5: Vehicle (Mig 44 P11-C — S51) =====
// Code UNIQUE filtered. Catalog xe công reference VehicleBooking workflow app.
// Mirror Region 1 LeaveType EXACT (list Where !IsDeleted + filter q Code/Name + IsActive).

public record ListVehiclesQuery(string? Q = null, bool? IsActive = null) : IRequest<List<VehicleDto>>;
public class ListVehiclesHandler(IApplicationDbContext db) : IRequestHandler<ListVehiclesQuery, List<VehicleDto>>
{
    public async Task<List<VehicleDto>> Handle(ListVehiclesQuery req, CancellationToken ct)
    {
        var q = db.Vehicles.AsNoTracking().Where(x => !x.IsDeleted);
        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 (req.IsActive.HasValue) q = q.Where(x => x.IsActive == req.IsActive.Value);
        return await q.OrderBy(x => x.Code)
            .Select(x => new VehicleDto(x.Id, x.Code, x.Name, x.LicensePlate, x.SeatCount, x.IsActive, x.Description))
            .ToListAsync(ct);
    }
}

public record CreateVehicleCommand(string Code, string Name, string LicensePlate, int SeatCount, string? Description) : IRequest<Guid>;
public class CreateVehicleValidator : AbstractValidator<CreateVehicleCommand>
{
    public CreateVehicleValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.LicensePlate).NotEmpty().MaximumLength(20);
        RuleFor(x => x.SeatCount).GreaterThanOrEqualTo(0);
        RuleFor(x => x.Description).MaximumLength(500);
    }
}
public class CreateVehicleHandler(IApplicationDbContext db) : IRequestHandler<CreateVehicleCommand, Guid>
{
    public async Task<Guid> Handle(CreateVehicleCommand req, CancellationToken ct)
    {
        if (await db.Vehicles.AnyAsync(x => x.Code == req.Code && !x.IsDeleted, ct))
            throw new ConflictException($"Mã xe '{req.Code}' đã tồn tại.");
        var entity = new Vehicle
        {
            Code = req.Code,
            Name = req.Name,
            LicensePlate = req.LicensePlate,
            SeatCount = req.SeatCount,
            Description = req.Description,
            IsActive = true,
        };
        db.Vehicles.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateVehicleCommand(Guid Id, string Code, string Name, string LicensePlate, int SeatCount, bool IsActive, string? Description) : IRequest;
public class UpdateVehicleValidator : AbstractValidator<UpdateVehicleCommand>
{
    public UpdateVehicleValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.LicensePlate).NotEmpty().MaximumLength(20);
        RuleFor(x => x.SeatCount).GreaterThanOrEqualTo(0);
        RuleFor(x => x.Description).MaximumLength(500);
    }
}
public class UpdateVehicleHandler(IApplicationDbContext db) : IRequestHandler<UpdateVehicleCommand>
{
    public async Task Handle(UpdateVehicleCommand req, CancellationToken ct)
    {
        var entity = await db.Vehicles.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("Vehicle", req.Id);
        if (entity.Code != req.Code && await db.Vehicles.AnyAsync(x => x.Code == req.Code && !x.IsDeleted, ct))
            throw new ConflictException($"Mã xe '{req.Code}' đã tồn tại.");
        entity.Code = req.Code;
        entity.Name = req.Name;
        entity.LicensePlate = req.LicensePlate;
        entity.SeatCount = req.SeatCount;
        entity.IsActive = req.IsActive;
        entity.Description = req.Description;
        await db.SaveChangesAsync(ct);
    }
}

public record DeleteVehicleCommand(Guid Id) : IRequest;
public class DeleteVehicleHandler(IApplicationDbContext db) : IRequestHandler<DeleteVehicleCommand>
{
    public async Task Handle(DeleteVehicleCommand req, CancellationToken ct)
    {
        var entity = await db.Vehicles.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("Vehicle", req.Id);
        db.Vehicles.Remove(entity);  // Soft delete via AuditingInterceptor
        await db.SaveChangesAsync(ct);
    }
}

// ===== Region 6: Driver (Mig 44 P11-C — S51) =====
// Code UNIQUE filtered. Catalog tài xế reference VehicleBooking workflow app.
// Mirror Region 5 Vehicle EXACT. Validator: PhoneNumber Max20 + LicenseNumber Max50 + LicenseClass Max20.

public record ListDriversQuery(string? Q = null, bool? IsActive = null) : IRequest<List<DriverDto>>;
public class ListDriversHandler(IApplicationDbContext db) : IRequestHandler<ListDriversQuery, List<DriverDto>>
{
    public async Task<List<DriverDto>> Handle(ListDriversQuery req, CancellationToken ct)
    {
        var q = db.Drivers.AsNoTracking().Where(x => !x.IsDeleted);
        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 (req.IsActive.HasValue) q = q.Where(x => x.IsActive == req.IsActive.Value);
        return await q.OrderBy(x => x.Code)
            .Select(x => new DriverDto(x.Id, x.Code, x.Name, x.PhoneNumber, x.LicenseNumber, x.LicenseClass, x.IsActive, x.Description))
            .ToListAsync(ct);
    }
}

public record CreateDriverCommand(string Code, string Name, string PhoneNumber, string LicenseNumber, string LicenseClass, string? Description) : IRequest<Guid>;
public class CreateDriverValidator : AbstractValidator<CreateDriverCommand>
{
    public CreateDriverValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.PhoneNumber).NotEmpty().MaximumLength(20);
        RuleFor(x => x.LicenseNumber).NotEmpty().MaximumLength(50);
        RuleFor(x => x.LicenseClass).NotEmpty().MaximumLength(20);
        RuleFor(x => x.Description).MaximumLength(500);
    }
}
public class CreateDriverHandler(IApplicationDbContext db) : IRequestHandler<CreateDriverCommand, Guid>
{
    public async Task<Guid> Handle(CreateDriverCommand req, CancellationToken ct)
    {
        if (await db.Drivers.AnyAsync(x => x.Code == req.Code && !x.IsDeleted, ct))
            throw new ConflictException($"Mã tài xế '{req.Code}' đã tồn tại.");
        var entity = new Driver
        {
            Code = req.Code,
            Name = req.Name,
            PhoneNumber = req.PhoneNumber,
            LicenseNumber = req.LicenseNumber,
            LicenseClass = req.LicenseClass,
            Description = req.Description,
            IsActive = true,
        };
        db.Drivers.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateDriverCommand(Guid Id, string Code, string Name, string PhoneNumber, string LicenseNumber, string LicenseClass, bool IsActive, string? Description) : IRequest;
public class UpdateDriverValidator : AbstractValidator<UpdateDriverCommand>
{
    public UpdateDriverValidator()
    {
        RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.PhoneNumber).NotEmpty().MaximumLength(20);
        RuleFor(x => x.LicenseNumber).NotEmpty().MaximumLength(50);
        RuleFor(x => x.LicenseClass).NotEmpty().MaximumLength(20);
        RuleFor(x => x.Description).MaximumLength(500);
    }
}
public class UpdateDriverHandler(IApplicationDbContext db) : IRequestHandler<UpdateDriverCommand>
{
    public async Task Handle(UpdateDriverCommand req, CancellationToken ct)
    {
        var entity = await db.Drivers.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("Driver", req.Id);
        if (entity.Code != req.Code && await db.Drivers.AnyAsync(x => x.Code == req.Code && !x.IsDeleted, ct))
            throw new ConflictException($"Mã tài xế '{req.Code}' đã tồn tại.");
        entity.Code = req.Code;
        entity.Name = req.Name;
        entity.PhoneNumber = req.PhoneNumber;
        entity.LicenseNumber = req.LicenseNumber;
        entity.LicenseClass = req.LicenseClass;
        entity.IsActive = req.IsActive;
        entity.Description = req.Description;
        await db.SaveChangesAsync(ct);
    }
}

public record DeleteDriverCommand(Guid Id) : IRequest;
public class DeleteDriverHandler(IApplicationDbContext db) : IRequestHandler<DeleteDriverCommand>
{
    public async Task Handle(DeleteDriverCommand req, CancellationToken ct)
    {
        var entity = await db.Drivers.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("Driver", req.Id);
        db.Drivers.Remove(entity);  // Soft delete via AuditingInterceptor
        await db.SaveChangesAsync(ct);
    }
}
