<?php

namespace App\Console\Commands;

use App\Models\LoanInstallment;
use App\Models\Transaction;
use App\Notifications\LoanOverdueNotification;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class ProcessLoanInstallments extends Command
{
    protected $signature = 'app:process-loan-installments';
    protected $description = 'Process overdue installments and apply penalties';

    public function handle()
    {
        $this->info('Starting loan installments processing...');

        // Process in chunks to avoid memory issues
        LoanInstallment::pending()
            ->whereDate('due_date', '<', today())
            ->with(['loan.user', 'loan.account']) // eager load relationships
            ->chunk(100, function ($installments) {

                /** @var LoanInstallment $installment */
                foreach ($installments as $installment) {

                    DB::transaction(function () use ($installment) {

                        // Refresh to get latest data
                        $installment->refresh();

                        // Skip if already processed
                        if ($installment->status !== 'pending') {
                            return;
                        }

                        $loan = $installment->loan()->lockForUpdate()->firstOrFail();

                        $graceDays = $loan->grace_period_days ?? 0;

                        // Overdue days (positive if past due)
                        $daysOverdue = today()->diffInDays($installment->due_date);

                        // Skip if within grace period
                        if ($daysOverdue <= $graceDays) {
                            return;
                        }

                        // Skip if penalty already applied
                        if (bccomp($installment->penalty, '0', 2) > 0) {
                            return;
                        }

                        $penaltyRate = (float) config('loan.late_fee_percentage', 0.02);

                        // Base amount = principal + interest
                        $baseAmount = bcadd($installment->principal, $installment->interest, 2);

                        // Daily penalty = baseAmount × rate
                        $dailyPenalty = bcmul($baseAmount, (string) $penaltyRate, 4);

                        // Total penalty = dailyPenalty × overdue days
                        $penalty = bcmul($dailyPenalty, (string) $daysOverdue, 2);

                        // Update installment
                        $installment->update([
                            'penalty' => $penalty,
                            'total' => bcadd($baseAmount, $penalty, 2),
                            'status' => 'overdue',
                        ]);

                        // Update loan
                        $loan->penalty_outstanding = bcadd($loan->penalty_outstanding, $penalty, 2);
                        $loan->status = $loan->remaining_balance > 0 ? 'overdue' : 'paid';
                        $loan->save();

                        // Update account balance
                        $account = $loan->account;
                        $account->balance = bcadd($account->balance, $penalty, 2);
                        $account->save();

                        // Record transaction
                        $transaction = Transaction::create([
                            'user_id' => $loan->user_id,
                            'account_id' => $loan->account_id,
                            'loan_id' => $loan->id,
                            'type' => 'loan_penalty',
                            'amount' => $penalty,
                            'status' => Transaction::STATUS_PENDING,
                            'balance_after' => $account->balance,
                            'description' => 'Late payment penalty',
                        ]);

                        // Notify user after commit
                        DB::afterCommit(function () use ($loan, $installment) {
                            try {
                                $loan->user?->notify(new LoanOverdueNotification($loan, $installment));
                            } catch (\Throwable $e) {
                                Log::error('Failed to notify overdue loan', [
                                    'loan_id' => $loan->id,
                                    'installment_id' => $installment->id,
                                    'exception' => $e,
                                ]);
                            }
                        });
                    }); // End transaction
                } // End foreach
            }); // End chunk

        $this->info('Loan installments processed successfully.');
    }
}
