<?php

namespace App\Modules\Payroll\Services;

use App\Modules\HR\Attendance\Repositories\AttendanceRecordRepository;
use App\Modules\HR\Compensation\Repositories\CompensationRepository;
use App\Modules\HR\Employees\Models\Employee;
use App\Modules\HR\Leave\Services\LeavePayrollDaysService;
use App\Modules\HR\Loans\Repositories\LoanRepository;
use App\Modules\HR\Overtime\Repositories\OvertimeRequestRepository;
use App\Modules\Payroll\Repositories\PayrollAdjustmentRepository;

class PayrollInputAggregationService
{
    public function __construct(
        private readonly AttendanceRecordRepository $attendance,
        private readonly LeavePayrollDaysService $leaveDays,
        private readonly OvertimeRequestRepository $overtime,
        private readonly PayrollAdjustmentRepository $adjustments,
        private readonly PayrollLeaveDeductionCalculator $leaveDeduction,
        private readonly CompensationRepository $compensation,
        private readonly LoanRepository $loans,
    ) {}

    /**
     * @return array{
     *   base_salary: string,
     *   attendance_days: int,
     *   absent_days: int,
     *   late_days: int,
     *   leave_days: float,
     *   overtime_minutes: int,
     *   adjustments_total: string,
     *   snapshot_data: array<string, mixed>,
     *   gross_amount: string
     * }
     */
    public function aggregateForEmployee(
        int $tenantId,
        int $employeeId,
        string $periodStart,
        string $periodEnd,
        int $payrollPeriodId,
    ): array {
        $emp = Employee::query()
            ->where('tenant_id', $tenantId)
            ->whereKey($employeeId)
            ->with('salaryStructure.components')
            ->first(['id', 'salary', 'salary_structure_id']);
        $base = $this->baseSalary($emp);
        $counts = $this->attendance->payrollStatusCountsForEmployeeInRange(
            $tenantId,
            $employeeId,
            $periodStart,
            $periodEnd,
        );
        $attendanceDays = $counts['present'] + $counts['late'] + $counts['half_day'] + $counts['holiday'];
        $leaveDays = $this->leaveDays->sumApprovedDaysInRange(
            $tenantId,
            $employeeId,
            $periodStart,
            $periodEnd,
        );
        $unpaidLeaveDays = $this->leaveDays->sumApprovedDaysInRange(
            $tenantId,
            $employeeId,
            $periodStart,
            $periodEnd,
            false,
        );
        $leaveDeductionAmount = $this->leaveDeduction->amountFromDays((float) $base, $unpaidLeaveDays);
        $adj = $this->adjustments->sumSignedForEmployeePeriod($tenantId, $employeeId, $payrollPeriodId);
        $recurring = $this->compensation->recurringTotals(
            $tenantId, $employeeId, (float) $base, $periodStart, $periodEnd,
        );
        $loanDeduction = $this->loans->deductionForPeriod(
            $tenantId, $employeeId, $periodStart, $periodEnd, $payrollPeriodId,
        );
        $gross = (string) round(
            (float) $base + (float) $adj + $recurring['allowance']
            - $recurring['deduction'] - (float) $leaveDeductionAmount - $loanDeduction,
            2,
        );
        $approvedOt = $this->overtime->approvedWeightedMinutesForEmployeeInRange(
            $tenantId,
            $employeeId,
            $periodStart,
            $periodEnd,
        );

        return [
            'base_salary' => $base,
            'attendance_days' => $attendanceDays,
            'absent_days' => $counts['absent'],
            'late_days' => $counts['late'],
            'leave_days' => $leaveDays,
            'overtime_minutes' => $approvedOt ?? $counts['overtime_minutes'],
            'adjustments_total' => $adj,
            'snapshot_data' => [
                'attendance_status_breakdown' => $counts,
                'period_start' => $periodStart,
                'period_end' => $periodEnd,
                'overtime_source' => $approvedOt !== null ? 'approved_requests_weighted' : 'attendance_records',
                'unpaid_leave_days' => $unpaidLeaveDays,
                'leave_deduction_amount' => $leaveDeductionAmount,
                'recurring_allowances' => $recurring['allowance'],
                'recurring_deductions' => $recurring['deduction'],
                'loan_installment_deduction' => $loanDeduction,
            ],
            'gross_amount' => $gross,
        ];
    }

    private function baseSalary(?Employee $employee): string
    {
        if ($employee === null) {
            return '0.00';
        }
        if ((float) $employee->salary > 0) {
            return (string) $employee->salary;
        }

        return number_format($employee->salaryStructure?->fixedBaseAmount() ?? 0, 2, '.', '');
    }
}
