<?php

namespace App\Modules\HR\Loans\Services;

use App\Modules\HR\Loans\Models\Loan;
use App\Modules\HR\Loans\Models\LoanInstallment;
use App\Modules\Payroll\Models\PayrollPeriod;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;

/**
 * Marks loan installments as paid when a payroll period is finalized.
 */
class LoanPayrollSettlementService
{
    public function settleForFinalizedPeriod(PayrollPeriod $period): int
    {
        $tenantId = (int) $period->tenant_id;
        $periodId = (int) $period->id;
        $start = Carbon::parse((string) $period->start_date)->toDateString();
        $end = Carbon::parse((string) $period->end_date)->toDateString();

        return (int) DB::transaction(function () use ($tenantId, $periodId, $start, $end): int {
            $installments = LoanInstallment::query()
                ->where('status', 'pending')
                ->whereBetween('due_date', [$start, $end])
                ->where(function ($q) use ($periodId): void {
                    $q->whereNull('payroll_period_id')->orWhere('payroll_period_id', $periodId);
                })
                ->whereHas('loan', function ($q) use ($tenantId): void {
                    $q->where('tenant_id', $tenantId)
                        ->whereIn('status', ['approved', 'active', 'partially_paid']);
                })
                ->lockForUpdate()
                ->get();

            $loanIds = [];
            foreach ($installments as $installment) {
                $installment->update([
                    'status' => 'paid',
                    'paid_amount' => $installment->amount,
                    'payroll_period_id' => $periodId,
                ]);
                $loanIds[(int) $installment->loan_id] = true;
            }

            foreach (array_keys($loanIds) as $loanId) {
                $this->refreshLoanStatus((int) $loanId);
            }

            return $installments->count();
        });
    }

    private function refreshLoanStatus(int $loanId): void
    {
        $loan = Loan::query()->with('installments')->find($loanId);
        if ($loan === null) {
            return;
        }

        $pending = $loan->installments->where('status', 'pending');
        $paid = $loan->installments->where('status', 'paid');
        $remaining = (float) $pending->sum(fn (LoanInstallment $i): float => (float) $i->amount);

        $status = $loan->status;
        if ($pending->isEmpty() && $paid->isNotEmpty()) {
            $status = 'settled';
            $remaining = 0.0;
        } elseif ($paid->isNotEmpty() && $pending->isNotEmpty()) {
            $status = 'partially_paid';
        } elseif ($paid->isEmpty() && in_array($loan->status, ['approved', 'active'], true)) {
            $status = 'active';
        }

        $loan->update([
            'status' => $status,
            'remaining_balance' => round($remaining, 2),
        ]);
    }
}
