using ERPCore.Domain.Entities;
using ERPCore.Dtos.Hrm;
using ERPCore.Infra.UoW;
using ERPCore.Repositories.Interfaces;
using ERPCore.Services.Interfaces;
using Microsoft.EntityFrameworkCore;

namespace ERPCore.Services.Hrm;

/// <summary>Effective-dated EPF/ETF settings (FR-HR-PAY-04, docs/13-BACKEND-HRM-API.md §6).</summary>
public sealed class PayrollStatutorySettingService : IPayrollStatutorySettingService
{
    private readonly IRepository<PayrollStatutorySetting> _settings;
    private readonly IUnitOfWork _uow;

    public PayrollStatutorySettingService(IRepository<PayrollStatutorySetting> settings, IUnitOfWork uow)
    {
        _settings = settings;
        _uow = uow;
    }

    public async Task<List<PayrollStatutorySettingDto>> ListAsync(CancellationToken ct = default)
    {
        var rows = await _settings.Query().AsNoTracking().OrderByDescending(s => s.EffectiveFrom).ToListAsync(ct);
        return rows.Select(Map).ToList();
    }

    public async Task<PayrollStatutorySettingDto> CreateAsync(UpsertPayrollStatutorySettingRequest request, int actorUserId, CancellationToken ct = default)
    {
        var previous = await _settings.Query()
            .Where(s => s.EffectiveTo == null)
            .OrderByDescending(s => s.EffectiveFrom)
            .FirstOrDefaultAsync(ct);
        if (previous is not null) previous.EffectiveTo = request.EffectiveFrom.Date.AddDays(-1);

        var setting = new PayrollStatutorySetting
        {
            EpfEmployeeRate = request.EpfEmployeeRate,
            EpfEmployerRate = request.EpfEmployerRate,
            EtfEmployerRate = request.EtfEmployerRate,
            OtMultiplierDefault = request.OtMultiplierDefault,
            EffectiveFrom = request.EffectiveFrom.Date,
            CreatedBy = actorUserId,
            CreatedAt = DateTime.UtcNow
        };

        await _settings.AddAsync(setting, ct);
        await _uow.SaveChangesAsync(ct);

        return Map(setting);
    }

    public async Task<PayrollStatutorySetting?> GetEffectiveAsync(DateTime asOf, CancellationToken ct = default)
    {
        return await _settings.Query().AsNoTracking()
            .Where(s => s.EffectiveFrom <= asOf && (s.EffectiveTo == null || s.EffectiveTo >= asOf))
            .OrderByDescending(s => s.EffectiveFrom)
            .FirstOrDefaultAsync(ct);
    }

    private static PayrollStatutorySettingDto Map(PayrollStatutorySetting s) => new(
        s.PayrollStatutorySettingId, s.EpfEmployeeRate, s.EpfEmployerRate, s.EtfEmployerRate, s.OtMultiplierDefault, s.EffectiveFrom, s.EffectiveTo);
}
