using ERPCore.Domain.Entities;
using ERPCore.Domain.Enums;
using ERPCore.Services.Interfaces;

namespace ERPCore.Services.Hrm;

/// <inheritdoc cref="IAttendanceComputationService"/>
public sealed class AttendanceComputationService : IAttendanceComputationService
{
    public void Compute(AttendanceRecord record, WorkShift shift, bool hasApprovedLeave, bool isHoliday, bool isWeekOff)
    {
        if (record.CheckIn is null || record.CheckOut is null)
        {
            record.WorkingMinutes = 0;
            record.LateMinutes = 0;
            record.EarlyLeaveMinutes = 0;
            record.OvertimeMinutes = 0;
            record.AttendanceStatus = isHoliday ? AttendanceStatus.Holiday
                : isWeekOff ? AttendanceStatus.WeekOff
                : hasApprovedLeave ? AttendanceStatus.OnLeave
                : AttendanceStatus.Absent;
            return;
        }

        var checkIn = record.CheckIn.Value;
        var checkOut = record.CheckOut.Value;
        // Overnight shift: checkout numerically earlier than checkin means it rolled past midnight.
        if (shift.IsOvernight && checkOut < checkIn) checkOut = checkOut.Add(TimeSpan.FromHours(24));

        var grossWorkedMinutes = (int)(checkOut - checkIn).TotalMinutes;
        var workingMinutes = Math.Max(0, grossWorkedMinutes - shift.BreakMinutes);

        var shiftStart = shift.StartTime;
        var shiftEnd = shift.IsOvernight ? shift.EndTime.Add(TimeSpan.FromHours(24)) : shift.EndTime;

        var lateMinutes = Math.Max(0, (int)(checkIn - shiftStart).TotalMinutes - shift.GraceMinutes);
        var earlyLeaveMinutes = Math.Max(0, (int)(shiftEnd - checkOut).TotalMinutes);
        var overtimeMinutes = Math.Max(0, workingMinutes - shift.StandardWorkingMinutes);

        record.WorkingMinutes = workingMinutes;
        record.LateMinutes = lateMinutes;
        record.EarlyLeaveMinutes = earlyLeaveMinutes;
        record.OvertimeMinutes = overtimeMinutes;
        record.AttendanceStatus = workingMinutes < shift.StandardWorkingMinutes / 2 ? AttendanceStatus.HalfDay : AttendanceStatus.Present;
    }
}
