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;

// Plan G-H1 Phase 1.5 Item 3 (S34) — 5 satellite CRUD endpoint scaffold.
// Cookie-cutter 5× pattern: Create/Update/Delete cho WorkHistory/Education/
// FamilyRelation/Skill/Document. Mirror parent EmployeeFeatures.cs pattern
// (Reviewer minor S33+S34 — bool? safe partial update KHÔNG áp dụng satellite,
// satellite create/update full overwrite).
//
// FE inline form satellite DEFER S35 (em main capacity).
// File upload IFileStorage wire cho Document body DEFER S35.

#region WorkHistory

public record CreateEmployeeWorkHistoryCommand(
    Guid EmployeeProfileId,
    string CompanyName,
    string? CompanyAddress = null,
    string? Industry = null,
    DateOnly? FromDate = null,
    DateOnly? ToDate = null,
    string? JobTitle = null,
    string? JobDescription = null,
    string? ResignReason = null) : IRequest<Guid>;

public class CreateEmployeeWorkHistoryCommandValidator : AbstractValidator<CreateEmployeeWorkHistoryCommand>
{
    public CreateEmployeeWorkHistoryCommandValidator()
    {
        RuleFor(x => x.EmployeeProfileId).NotEmpty();
        RuleFor(x => x.CompanyName).NotEmpty().MaximumLength(200);
        RuleFor(x => x.CompanyAddress).MaximumLength(500);
        RuleFor(x => x.Industry).MaximumLength(100);
        RuleFor(x => x.JobTitle).MaximumLength(200);
        RuleFor(x => x.JobDescription).MaximumLength(2000);
        RuleFor(x => x.ResignReason).MaximumLength(500);
    }
}

public class CreateEmployeeWorkHistoryCommandHandler(
    IApplicationDbContext db) : IRequestHandler<CreateEmployeeWorkHistoryCommand, Guid>
{
    public async Task<Guid> Handle(CreateEmployeeWorkHistoryCommand request, CancellationToken ct)
    {
        var parentExists = await db.EmployeeProfiles
            .AnyAsync(x => x.Id == request.EmployeeProfileId && !x.IsDeleted, ct);
        if (!parentExists)
            throw new NotFoundException("EmployeeProfile", request.EmployeeProfileId);

        var entity = new EmployeeWorkHistory
        {
            EmployeeProfileId = request.EmployeeProfileId,
            CompanyName = request.CompanyName,
            CompanyAddress = request.CompanyAddress,
            Industry = request.Industry,
            FromDate = request.FromDate,
            ToDate = request.ToDate,
            JobTitle = request.JobTitle,
            JobDescription = request.JobDescription,
            ResignReason = request.ResignReason,
        };

        db.EmployeeWorkHistories.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateEmployeeWorkHistoryCommand(
    Guid Id,
    string CompanyName,
    string? CompanyAddress,
    string? Industry,
    DateOnly? FromDate,
    DateOnly? ToDate,
    string? JobTitle,
    string? JobDescription,
    string? ResignReason) : IRequest;

public class UpdateEmployeeWorkHistoryCommandValidator : AbstractValidator<UpdateEmployeeWorkHistoryCommand>
{
    public UpdateEmployeeWorkHistoryCommandValidator()
    {
        RuleFor(x => x.Id).NotEmpty();
        RuleFor(x => x.CompanyName).NotEmpty().MaximumLength(200);
        RuleFor(x => x.CompanyAddress).MaximumLength(500);
        RuleFor(x => x.Industry).MaximumLength(100);
        RuleFor(x => x.JobTitle).MaximumLength(200);
        RuleFor(x => x.JobDescription).MaximumLength(2000);
        RuleFor(x => x.ResignReason).MaximumLength(500);
    }
}

public class UpdateEmployeeWorkHistoryCommandHandler(
    IApplicationDbContext db) : IRequestHandler<UpdateEmployeeWorkHistoryCommand>
{
    public async Task Handle(UpdateEmployeeWorkHistoryCommand request, CancellationToken ct)
    {
        var entity = await db.EmployeeWorkHistories
            .FirstOrDefaultAsync(x => x.Id == request.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("EmployeeWorkHistory", request.Id);

        entity.CompanyName = request.CompanyName;
        entity.CompanyAddress = request.CompanyAddress;
        entity.Industry = request.Industry;
        entity.FromDate = request.FromDate;
        entity.ToDate = request.ToDate;
        entity.JobTitle = request.JobTitle;
        entity.JobDescription = request.JobDescription;
        entity.ResignReason = request.ResignReason;

        await db.SaveChangesAsync(ct);
    }
}

public record DeleteEmployeeWorkHistoryCommand(Guid Id) : IRequest;

public class DeleteEmployeeWorkHistoryCommandHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser) : IRequestHandler<DeleteEmployeeWorkHistoryCommand>
{
    public async Task Handle(DeleteEmployeeWorkHistoryCommand request, CancellationToken ct)
    {
        var entity = await db.EmployeeWorkHistories
            .FirstOrDefaultAsync(x => x.Id == request.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("EmployeeWorkHistory", request.Id);

        entity.IsDeleted = true;
        entity.DeletedAt = DateTime.UtcNow;
        entity.DeletedBy = currentUser.UserId;

        await db.SaveChangesAsync(ct);
    }
}

#endregion

#region Education

public record CreateEmployeeEducationCommand(
    Guid EmployeeProfileId,
    string SchoolName,
    string? Major = null,
    DegreeLevel? DegreeLevel = null,
    EducationMode? EducationMode = null,
    GradeLevel? GradeLevel = null,
    DateOnly? FromDate = null,
    DateOnly? ToDate = null,
    DateOnly? CertificateIssueDate = null,
    string? Notes = null) : IRequest<Guid>;

public class CreateEmployeeEducationCommandValidator : AbstractValidator<CreateEmployeeEducationCommand>
{
    public CreateEmployeeEducationCommandValidator()
    {
        RuleFor(x => x.EmployeeProfileId).NotEmpty();
        RuleFor(x => x.SchoolName).NotEmpty().MaximumLength(200);
        RuleFor(x => x.Major).MaximumLength(200);
        RuleFor(x => x.DegreeLevel).IsInEnum().When(x => x.DegreeLevel.HasValue);
        RuleFor(x => x.EducationMode).IsInEnum().When(x => x.EducationMode.HasValue);
        RuleFor(x => x.GradeLevel).IsInEnum().When(x => x.GradeLevel.HasValue);
        RuleFor(x => x.Notes).MaximumLength(1000);
    }
}

public class CreateEmployeeEducationCommandHandler(
    IApplicationDbContext db) : IRequestHandler<CreateEmployeeEducationCommand, Guid>
{
    public async Task<Guid> Handle(CreateEmployeeEducationCommand request, CancellationToken ct)
    {
        var parentExists = await db.EmployeeProfiles
            .AnyAsync(x => x.Id == request.EmployeeProfileId && !x.IsDeleted, ct);
        if (!parentExists)
            throw new NotFoundException("EmployeeProfile", request.EmployeeProfileId);

        var entity = new EmployeeEducation
        {
            EmployeeProfileId = request.EmployeeProfileId,
            SchoolName = request.SchoolName,
            Major = request.Major,
            DegreeLevel = request.DegreeLevel,
            EducationMode = request.EducationMode,
            GradeLevel = request.GradeLevel,
            FromDate = request.FromDate,
            ToDate = request.ToDate,
            CertificateIssueDate = request.CertificateIssueDate,
            Notes = request.Notes,
        };

        db.EmployeeEducations.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateEmployeeEducationCommand(
    Guid Id,
    string SchoolName,
    string? Major,
    DegreeLevel? DegreeLevel,
    EducationMode? EducationMode,
    GradeLevel? GradeLevel,
    DateOnly? FromDate,
    DateOnly? ToDate,
    DateOnly? CertificateIssueDate,
    string? Notes) : IRequest;

public class UpdateEmployeeEducationCommandValidator : AbstractValidator<UpdateEmployeeEducationCommand>
{
    public UpdateEmployeeEducationCommandValidator()
    {
        RuleFor(x => x.Id).NotEmpty();
        RuleFor(x => x.SchoolName).NotEmpty().MaximumLength(200);
        RuleFor(x => x.Major).MaximumLength(200);
        RuleFor(x => x.DegreeLevel).IsInEnum().When(x => x.DegreeLevel.HasValue);
        RuleFor(x => x.EducationMode).IsInEnum().When(x => x.EducationMode.HasValue);
        RuleFor(x => x.GradeLevel).IsInEnum().When(x => x.GradeLevel.HasValue);
        RuleFor(x => x.Notes).MaximumLength(1000);
    }
}

public class UpdateEmployeeEducationCommandHandler(
    IApplicationDbContext db) : IRequestHandler<UpdateEmployeeEducationCommand>
{
    public async Task Handle(UpdateEmployeeEducationCommand request, CancellationToken ct)
    {
        var entity = await db.EmployeeEducations
            .FirstOrDefaultAsync(x => x.Id == request.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("EmployeeEducation", request.Id);

        entity.SchoolName = request.SchoolName;
        entity.Major = request.Major;
        entity.DegreeLevel = request.DegreeLevel;
        entity.EducationMode = request.EducationMode;
        entity.GradeLevel = request.GradeLevel;
        entity.FromDate = request.FromDate;
        entity.ToDate = request.ToDate;
        entity.CertificateIssueDate = request.CertificateIssueDate;
        entity.Notes = request.Notes;

        await db.SaveChangesAsync(ct);
    }
}

public record DeleteEmployeeEducationCommand(Guid Id) : IRequest;

public class DeleteEmployeeEducationCommandHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser) : IRequestHandler<DeleteEmployeeEducationCommand>
{
    public async Task Handle(DeleteEmployeeEducationCommand request, CancellationToken ct)
    {
        var entity = await db.EmployeeEducations
            .FirstOrDefaultAsync(x => x.Id == request.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("EmployeeEducation", request.Id);

        entity.IsDeleted = true;
        entity.DeletedAt = DateTime.UtcNow;
        entity.DeletedBy = currentUser.UserId;

        await db.SaveChangesAsync(ct);
    }
}

#endregion

#region FamilyRelation

public record CreateEmployeeFamilyRelationCommand(
    Guid EmployeeProfileId,
    string FullName,
    FamilyRelationKind Relationship,
    int? BirthYear = null,
    string? Occupation = null,
    string? CurrentAddress = null,
    string? Phone = null) : IRequest<Guid>;

public class CreateEmployeeFamilyRelationCommandValidator : AbstractValidator<CreateEmployeeFamilyRelationCommand>
{
    public CreateEmployeeFamilyRelationCommandValidator()
    {
        RuleFor(x => x.EmployeeProfileId).NotEmpty();
        RuleFor(x => x.FullName).NotEmpty().MaximumLength(200);
        RuleFor(x => x.Relationship).IsInEnum();
        RuleFor(x => x.BirthYear).InclusiveBetween(1900, 2026).When(x => x.BirthYear.HasValue);
        RuleFor(x => x.Occupation).MaximumLength(200);
        RuleFor(x => x.CurrentAddress).MaximumLength(500);
        RuleFor(x => x.Phone).MaximumLength(20);
    }
}

public class CreateEmployeeFamilyRelationCommandHandler(
    IApplicationDbContext db) : IRequestHandler<CreateEmployeeFamilyRelationCommand, Guid>
{
    public async Task<Guid> Handle(CreateEmployeeFamilyRelationCommand request, CancellationToken ct)
    {
        var parentExists = await db.EmployeeProfiles
            .AnyAsync(x => x.Id == request.EmployeeProfileId && !x.IsDeleted, ct);
        if (!parentExists)
            throw new NotFoundException("EmployeeProfile", request.EmployeeProfileId);

        var entity = new EmployeeFamilyRelation
        {
            EmployeeProfileId = request.EmployeeProfileId,
            FullName = request.FullName,
            Relationship = request.Relationship,
            BirthYear = request.BirthYear,
            Occupation = request.Occupation,
            CurrentAddress = request.CurrentAddress,
            Phone = request.Phone,
        };

        db.EmployeeFamilyRelations.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateEmployeeFamilyRelationCommand(
    Guid Id,
    string FullName,
    FamilyRelationKind Relationship,
    int? BirthYear,
    string? Occupation,
    string? CurrentAddress,
    string? Phone) : IRequest;

public class UpdateEmployeeFamilyRelationCommandValidator : AbstractValidator<UpdateEmployeeFamilyRelationCommand>
{
    public UpdateEmployeeFamilyRelationCommandValidator()
    {
        RuleFor(x => x.Id).NotEmpty();
        RuleFor(x => x.FullName).NotEmpty().MaximumLength(200);
        RuleFor(x => x.Relationship).IsInEnum();
        RuleFor(x => x.BirthYear).InclusiveBetween(1900, 2026).When(x => x.BirthYear.HasValue);
        RuleFor(x => x.Occupation).MaximumLength(200);
        RuleFor(x => x.CurrentAddress).MaximumLength(500);
        RuleFor(x => x.Phone).MaximumLength(20);
    }
}

public class UpdateEmployeeFamilyRelationCommandHandler(
    IApplicationDbContext db) : IRequestHandler<UpdateEmployeeFamilyRelationCommand>
{
    public async Task Handle(UpdateEmployeeFamilyRelationCommand request, CancellationToken ct)
    {
        var entity = await db.EmployeeFamilyRelations
            .FirstOrDefaultAsync(x => x.Id == request.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("EmployeeFamilyRelation", request.Id);

        entity.FullName = request.FullName;
        entity.Relationship = request.Relationship;
        entity.BirthYear = request.BirthYear;
        entity.Occupation = request.Occupation;
        entity.CurrentAddress = request.CurrentAddress;
        entity.Phone = request.Phone;

        await db.SaveChangesAsync(ct);
    }
}

public record DeleteEmployeeFamilyRelationCommand(Guid Id) : IRequest;

public class DeleteEmployeeFamilyRelationCommandHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser) : IRequestHandler<DeleteEmployeeFamilyRelationCommand>
{
    public async Task Handle(DeleteEmployeeFamilyRelationCommand request, CancellationToken ct)
    {
        var entity = await db.EmployeeFamilyRelations
            .FirstOrDefaultAsync(x => x.Id == request.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("EmployeeFamilyRelation", request.Id);

        entity.IsDeleted = true;
        entity.DeletedAt = DateTime.UtcNow;
        entity.DeletedBy = currentUser.UserId;

        await db.SaveChangesAsync(ct);
    }
}

#endregion

#region Skill

public record CreateEmployeeSkillCommand(
    Guid EmployeeProfileId,
    SkillKind Kind,
    string Name,
    string? LanguageId = null,
    string? Level = null) : IRequest<Guid>;

public class CreateEmployeeSkillCommandValidator : AbstractValidator<CreateEmployeeSkillCommand>
{
    public CreateEmployeeSkillCommandValidator()
    {
        RuleFor(x => x.EmployeeProfileId).NotEmpty();
        RuleFor(x => x.Kind).IsInEnum();
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.LanguageId).MaximumLength(10);
        RuleFor(x => x.Level).MaximumLength(200);
    }
}

public class CreateEmployeeSkillCommandHandler(
    IApplicationDbContext db) : IRequestHandler<CreateEmployeeSkillCommand, Guid>
{
    public async Task<Guid> Handle(CreateEmployeeSkillCommand request, CancellationToken ct)
    {
        var parentExists = await db.EmployeeProfiles
            .AnyAsync(x => x.Id == request.EmployeeProfileId && !x.IsDeleted, ct);
        if (!parentExists)
            throw new NotFoundException("EmployeeProfile", request.EmployeeProfileId);

        var entity = new EmployeeSkill
        {
            EmployeeProfileId = request.EmployeeProfileId,
            Kind = request.Kind,
            Name = request.Name,
            LanguageId = request.LanguageId,
            Level = request.Level,
        };

        db.EmployeeSkills.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateEmployeeSkillCommand(
    Guid Id,
    SkillKind Kind,
    string Name,
    string? LanguageId,
    string? Level) : IRequest;

public class UpdateEmployeeSkillCommandValidator : AbstractValidator<UpdateEmployeeSkillCommand>
{
    public UpdateEmployeeSkillCommandValidator()
    {
        RuleFor(x => x.Id).NotEmpty();
        RuleFor(x => x.Kind).IsInEnum();
        RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
        RuleFor(x => x.LanguageId).MaximumLength(10);
        RuleFor(x => x.Level).MaximumLength(200);
    }
}

public class UpdateEmployeeSkillCommandHandler(
    IApplicationDbContext db) : IRequestHandler<UpdateEmployeeSkillCommand>
{
    public async Task Handle(UpdateEmployeeSkillCommand request, CancellationToken ct)
    {
        var entity = await db.EmployeeSkills
            .FirstOrDefaultAsync(x => x.Id == request.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("EmployeeSkill", request.Id);

        entity.Kind = request.Kind;
        entity.Name = request.Name;
        entity.LanguageId = request.LanguageId;
        entity.Level = request.Level;

        await db.SaveChangesAsync(ct);
    }
}

public record DeleteEmployeeSkillCommand(Guid Id) : IRequest;

public class DeleteEmployeeSkillCommandHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser) : IRequestHandler<DeleteEmployeeSkillCommand>
{
    public async Task Handle(DeleteEmployeeSkillCommand request, CancellationToken ct)
    {
        var entity = await db.EmployeeSkills
            .FirstOrDefaultAsync(x => x.Id == request.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("EmployeeSkill", request.Id);

        entity.IsDeleted = true;
        entity.DeletedAt = DateTime.UtcNow;
        entity.DeletedBy = currentUser.UserId;

        await db.SaveChangesAsync(ct);
    }
}

#endregion

#region Document

// Phase 1.5 S34 — metadata-only CRUD. File upload body (multipart) DEFER S35
// khi wire IFileStorage. Tạm thời admin paste sẵn FilePath/FileSize/ContentType
// từ chỗ khác (UI form input chấp nhận thủ công).
public record CreateEmployeeDocumentCommand(
    Guid EmployeeProfileId,
    EmployeeDocumentType DocumentType,
    string FileName,
    string FilePath,
    long FileSize,
    string ContentType,
    DateOnly? IssueDate = null,
    DateOnly? ExpiryDate = null,
    string? Notes = null) : IRequest<Guid>;

public class CreateEmployeeDocumentCommandValidator : AbstractValidator<CreateEmployeeDocumentCommand>
{
    public CreateEmployeeDocumentCommandValidator()
    {
        RuleFor(x => x.EmployeeProfileId).NotEmpty();
        RuleFor(x => x.DocumentType).IsInEnum();
        RuleFor(x => x.FileName).NotEmpty().MaximumLength(500);
        RuleFor(x => x.FilePath).NotEmpty().MaximumLength(1000);
        RuleFor(x => x.FileSize).GreaterThanOrEqualTo(0);
        RuleFor(x => x.ContentType).NotEmpty().MaximumLength(100);
        RuleFor(x => x.Notes).MaximumLength(1000);
    }
}

public class CreateEmployeeDocumentCommandHandler(
    IApplicationDbContext db) : IRequestHandler<CreateEmployeeDocumentCommand, Guid>
{
    public async Task<Guid> Handle(CreateEmployeeDocumentCommand request, CancellationToken ct)
    {
        var parentExists = await db.EmployeeProfiles
            .AnyAsync(x => x.Id == request.EmployeeProfileId && !x.IsDeleted, ct);
        if (!parentExists)
            throw new NotFoundException("EmployeeProfile", request.EmployeeProfileId);

        var entity = new EmployeeDocument
        {
            EmployeeProfileId = request.EmployeeProfileId,
            DocumentType = request.DocumentType,
            FileName = request.FileName,
            FilePath = request.FilePath,
            FileSize = request.FileSize,
            ContentType = request.ContentType,
            IssueDate = request.IssueDate,
            ExpiryDate = request.ExpiryDate,
            Notes = request.Notes,
        };

        db.EmployeeDocuments.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateEmployeeDocumentCommand(
    Guid Id,
    EmployeeDocumentType DocumentType,
    string FileName,
    string FilePath,
    long FileSize,
    string ContentType,
    DateOnly? IssueDate,
    DateOnly? ExpiryDate,
    string? Notes) : IRequest;

public class UpdateEmployeeDocumentCommandValidator : AbstractValidator<UpdateEmployeeDocumentCommand>
{
    public UpdateEmployeeDocumentCommandValidator()
    {
        RuleFor(x => x.Id).NotEmpty();
        RuleFor(x => x.DocumentType).IsInEnum();
        RuleFor(x => x.FileName).NotEmpty().MaximumLength(500);
        RuleFor(x => x.FilePath).NotEmpty().MaximumLength(1000);
        RuleFor(x => x.FileSize).GreaterThanOrEqualTo(0);
        RuleFor(x => x.ContentType).NotEmpty().MaximumLength(100);
        RuleFor(x => x.Notes).MaximumLength(1000);
    }
}

public class UpdateEmployeeDocumentCommandHandler(
    IApplicationDbContext db) : IRequestHandler<UpdateEmployeeDocumentCommand>
{
    public async Task Handle(UpdateEmployeeDocumentCommand request, CancellationToken ct)
    {
        var entity = await db.EmployeeDocuments
            .FirstOrDefaultAsync(x => x.Id == request.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("EmployeeDocument", request.Id);

        entity.DocumentType = request.DocumentType;
        entity.FileName = request.FileName;
        entity.FilePath = request.FilePath;
        entity.FileSize = request.FileSize;
        entity.ContentType = request.ContentType;
        entity.IssueDate = request.IssueDate;
        entity.ExpiryDate = request.ExpiryDate;
        entity.Notes = request.Notes;

        await db.SaveChangesAsync(ct);
    }
}

public record DeleteEmployeeDocumentCommand(Guid Id) : IRequest;

public class DeleteEmployeeDocumentCommandHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser) : IRequestHandler<DeleteEmployeeDocumentCommand>
{
    public async Task Handle(DeleteEmployeeDocumentCommand request, CancellationToken ct)
    {
        var entity = await db.EmployeeDocuments
            .FirstOrDefaultAsync(x => x.Id == request.Id && !x.IsDeleted, ct)
            ?? throw new NotFoundException("EmployeeDocument", request.Id);

        entity.IsDeleted = true;
        entity.DeletedAt = DateTime.UtcNow;
        entity.DeletedBy = currentUser.UserId;

        await db.SaveChangesAsync(ct);
    }
}

#endregion
