<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;

// Loan model represents a loan application in the system
class Loan extends Model
{
    use HasFactory, SoftDeletes;

    // Fields that can be mass-assigned
    protected $fillable = [
        // Basic loan info
        'loan_number', 'client_id', 'client_group_id', 'savings_account_id',
        'loan_type', 'amount', 'interest_rate', 'duration_months', 'purpose', 'status',

        // Dates
        'application_date', 'approval_date', 'disbursement_date', 'repayment_date',

        // Payment details
        'repayment_frequency', 'monthly_payment', 'total_repayment', 'total_interest', 'outstanding_balance',

        // Collateral and guarantor
        'collateral_type', 'collateral_value', 'guarantor_name', 'guarantor_phone', 'guarantor_address',

        // Client details
        'emergency_contact_relationship', 'monthly_income', 'employment_status',
        'employer_name', 'employer_phone', 'emergency_contact_name', 'emergency_contact_phone',

        // Additional notes
        'remarks', 'rejection_reason', 'disbursement_method', 'disbursement_reference',

        // Audit fields
        'created_by', 'approved_by', 'approved_at', 'rejected_by', 'rejected_at',
        'disbursed_by', 'disbursed_at', 'paused_by', 'paused_at', 'closed_by', 'closed_at',
        'wait_reason', 'wait_requested_at', 'wait_requested_by',
    ];

    // Data type casting for fields
    protected $casts = [
        // Monetary values as decimals
        'amount' => 'decimal:2',
        'interest_rate' => 'decimal:2',
        'monthly_payment' => 'decimal:2',
        'total_repayment' => 'decimal:2',
        'total_interest' => 'decimal:2',
        'outstanding_balance' => 'decimal:2',
        'collateral_value' => 'decimal:2',

        // Dates
        'application_date' => 'date',
        'approval_date' => 'date',
        'disbursement_date' => 'date',
        'repayment_date' => 'date',

        // Datetimes for audit
        'approved_at' => 'datetime',
        'rejected_at' => 'datetime',
        'disbursed_at' => 'datetime',
        'paused_at' => 'datetime',
        'closed_at' => 'datetime',
        'wait_requested_at' => 'datetime',
    ];

    // Relationships - links to other models
    public function client() // Get the client who applied for this loan
    {
        return $this->belongsTo(Client::class, 'client_id');
    }

    public function clientGroup() // Get the group this loan belongs to (if group loan)
    {
        return $this->belongsTo(ClientGroup::class, 'client_group_id');
    }

    public function loanProduct() // Get the loan product details
    {
        return $this->belongsTo(LoanProduct::class, 'loan_product_id');
    }

    public function savingsAccount() // Get linked savings account
    {
        return $this->belongsTo(SavingsAccount::class, 'savings_account_id');
    }

    public function creator() // Get user who created this loan
    {
        return $this->belongsTo(User::class, 'created_by');
    }

    public function approver() // Get user who approved this loan
    {
        return $this->belongsTo(User::class, 'approved_by');
    }

    public function disburser() // Get user who disbursed this loan
    {
        return $this->belongsTo(User::class, 'disbursed_by');
    }

    public function repayments() // Get all repayments for this loan
    {
        return $this->hasMany(LoanRepayment::class, 'loan_id');
    }

    public function reschedules() // Get reschedule history
    {
        return $this->hasMany(LoanReschedule::class, 'loan_id');
    }

    public function collaterals() // Get collateral details
    {
        return $this->hasMany(LoanCollateral::class, 'loan_id');
    }

    public function guarantors() // Get guarantor details
    {
        return $this->hasMany(LoanGuarantor::class, 'loan_id');
    }

    // Scopes - filter queries by status or type
    public function scopePending($query) // Filter loans that are pending approval
    {
        return $query->where('status', 'pending');
    }

    public function scopeProcessing($query) // Filter loans being processed
    {
        return $query->where('status', 'processing');
    }

    public function scopeApproved($query) // Filter approved loans
    {
        return $query->where('status', 'approved');
    }

    public function scopeDisbursed($query) // Filter disbursed loans
    {
        return $query->where('status', 'disbursed');
    }

    public function scopeActive($query) // Filter active loans (being repaid)
    {
        return $query->where('status', 'active');
    }

    public function scopeArrears($query) // Filter loans in arrears (overdue)
    {
        return $query->where('status', 'arrears');
    }

    public function scopeDefaulted($query) // Filter defaulted loans
    {
        return $query->where('status', 'defaulted');
    }

    public function scopeCompleted($query) // Filter fully repaid loans
    {
        return $query->where('status', 'completed');
    }

    public function scopeCashLoans($query) // Filter cash loans
    {
        return $query->where('loan_type', 'cash');
    }

    public function scopeProductLoans($query) // Filter product loans
    {
        return $query->where('loan_type', 'product');
    }

    public function scopeGroupLoans($query) // Filter group loans
    {
        return $query->where('loan_type', 'group');
    }

    public function scopeEmergencyLoans($query) // Filter emergency loans
    {
        return $query->where('loan_type', 'emergency');
    }

    public function scopePersonalLoans($query) // Filter personal loans
    {
        return $query->where('loan_type', 'personal');
    }

    public function scopeEducationLoans($query) // Filter education loans
    {
        return $query->where('loan_type', 'education');
    }

    public function scopeMortgageLoans($query) // Filter mortgage loans
    {
        return $query->where('loan_type', 'mortgage');
    }

    public function scopeAgricultureLoans($query) // Filter agriculture loans
    {
        return $query->where('loan_type', 'agriculture');
    }

    public function scopeAutoLoans($query) // Filter auto loans
    {
        return $query->where('loan_type', 'auto');
    }

    // Accessors - computed properties
    public function getNotesAttribute() // Alias for remarks column
    {
        return $this->remarks;
    }

    public function setNotesAttribute($value) // Alias for remarks column
    {
        $this->attributes['remarks'] = $value;
    }

    public function getTotalPaidAttribute() // Total amount paid so far
    {
        return $this->repayments()->sum('amount');
    }

    public function getRemainingBalanceAttribute() // Amount still owed
    {
        return max(0, $this->total_repayment - $this->total_paid);
    }

    public function getDaysOverdueAttribute() // Days past due date (if overdue)
    {
        if ($this->status !== 'arrears' || !$this->repayment_date) {
            return 0;
        }

        return now()->diffInDays($this->repayment_date, false);
    }

    public function getRepaymentProgressAttribute() // Percentage of loan repaid
    {
        if ($this->total_repayment <= 0) {
            return 0;
        }

        return min(100, ($this->total_paid / $this->total_repayment) * 100);
    }

    // Methods
    public function isPending()
    {
        return $this->status === 'pending';
    }

    public function isApproved()
    {
        return $this->status === 'approved';
    }

    public function isDisbursed()
    {
        return $this->status === 'disbursed';
    }

    public function isActive()
    {
        return $this->status === 'active';
    }

    public function isCompleted()
    {
        return $this->status === 'completed';
    }

    public function isOverdue()
    {
        return $this->status === 'arrears' && $this->repayment_date < now();
    }

    /**
     * Calculate loan repayment details using amortizing loan formula
     * Returns total interest, total repayment, and periodic payment amount
     */
    public function calculateRepayment()
    {
        $loanAmount = $this->amount; // Principal amount borrowed
        $yearlyInterestRate = $this->interest_rate / 100; // Convert percentage to decimal
        $repaymentFrequency = $this->repayment_frequency ?? 'monthly'; // How often payments are made

        // Number of payments per year based on frequency
        $paymentsPerYear = match($repaymentFrequency) {
            'weekly' => 52,
            'biweekly' => 26,
            'monthly' => 12,
            'quarterly' => 4,
            default => 12,
        };

        $interestRatePerPeriod = $yearlyInterestRate / $paymentsPerYear; // Interest per payment period
        $totalNumberOfPayments = $this->duration_months * ($paymentsPerYear / 12); // Total payments over loan term

        if ($interestRatePerPeriod == 0) {
            // No interest loan - simple division
            $paymentPerPeriod = $loanAmount / $totalNumberOfPayments;
            $totalAmountToRepay = $loanAmount;
            $totalInterestPaid = 0;
        } else {
            // Standard amortizing loan formula
            $paymentPerPeriod = ($loanAmount * $interestRatePerPeriod * pow(1 + $interestRatePerPeriod, $totalNumberOfPayments)) /
                               (pow(1 + $interestRatePerPeriod, $totalNumberOfPayments) - 1);
            $totalAmountToRepay = $paymentPerPeriod * $totalNumberOfPayments;
            $totalInterestPaid = $totalAmountToRepay - $loanAmount;
        }

        return [
            'interest' => round($totalInterestPaid, 2),
            'total_repayment' => round($totalAmountToRepay, 2),
            'monthly_payment' => round($paymentPerPeriod, 2),
        ];
    }

    /**
     * Update loan calculations - recalculates and saves repayment details
     */
    public function updateCalculations()
    {
        $repaymentDetails = $this->calculateRepayment(); // Get calculated amounts

        $this->update([
            'total_repayment' => $repaymentDetails['total_repayment'],
            'monthly_payment' => $repaymentDetails['monthly_payment'],
            'total_interest' => $repaymentDetails['interest'],
            'outstanding_balance' => $repaymentDetails['total_repayment'], // Initial balance is total repayment
        ]);
    }

    /**
     * Approve loan - change status to approved and record who approved it
     */
    public function approve($approvedBy)
    {
        if (!$this->isPending()) {
            throw new \Exception('Only pending loans can be approved');
        }

        $this->update([
            'status' => 'approved',
            'approval_date' => now(),
            'approved_by' => $approvedBy,
            'approved_at' => now(),
        ]);
    }

    /**
     * Reject loan - change status to rejected and record reason
     */
    public function reject($reason = null)
    {
        if (!$this->isPending()) {
            throw new \Exception('Only pending loans can be rejected');
        }

        $this->update([
            'status' => 'rejected',
            'rejection_reason' => $reason,
            'rejected_at' => now(),
        ]);
    }

    /**
     * Disburse loan - release funds to client and update status
     */
    public function disburse($disbursedBy, $disbursementDate = null)
    {
        return DB::transaction(function () use ($disbursedBy, $disbursementDate) {
            if (!$this->isApproved()) {
                throw new \App\Exceptions\BusinessLogicException('Only approved loans can be disbursed.');
            }

            // Update loan status to disbursed
            $this->update([
                'status' => 'disbursed',
                'disbursement_date' => $disbursementDate ?? now(),
                'disbursed_by' => $disbursedBy,
                'disbursed_at' => now(),
            ]);

            return $this;
        });
    }

    /**
     * Process repayment
     */
    public function processRepayment($amount, $paymentMethod = 'cash', $reference = null)
    {
        if (!$this->isDisbursed() && $this->status !== 'arrears') {
            throw new \Exception('Loan must be disbursed before processing repayments');
        }

        if ($amount <= 0) {
            throw new \Exception('Repayment amount must be greater than zero');
        }

        return DB::transaction(function () use ($amount, $paymentMethod, $reference) {
            // Create repayment record
            $repayment = LoanRepayment::create([
                'loan_id' => $this->id,
                'amount' => $amount,
                'payment_date' => now(),
                'payment_method' => $paymentMethod,
                'reference' => $reference,
                'created_by' => auth()->id(),
            ]);

            // Update outstanding balance
            $newBalance = max(0, $this->outstanding_balance - $amount);

            // Update loan status
            $status = $this->status;
            if ($newBalance <= 0) {
                $status = 'completed';
            } elseif ($this->status === 'approved' || $this->status === 'disbursed') {
                $status = 'active';
            }

            $this->update([
                'outstanding_balance' => $newBalance,
                'status' => $status,
            ]);

            return $repayment;
        });
    }

    /**
     * Generate loan schedule
     */
    public function generateSchedule()
    {
        $schedule = [];
        $monthlyPayment = $this->monthly_payment;
        $balance = $this->total_repayment;
        $startDate = $this->disbursement_date ?? $this->application_date;
        $frequency = $this->repayment_frequency ?? 'monthly';

        $paymentsPerYear = match($frequency) {
            'weekly' => 52,
            'biweekly' => 26,
            'monthly' => 12,
            'quarterly' => 4,
            default => 12,
        };

        $totalPayments = $this->duration_months * ($paymentsPerYear / 12);

        for ($i = 1; $i <= $totalPayments; $i++) {
            $paymentDate = match($frequency) {
                'weekly' => Carbon::parse($startDate)->addWeeks($i),
                'biweekly' => Carbon::parse($startDate)->addWeeks($i * 2),
                'monthly' => Carbon::parse($startDate)->addMonths($i),
                'quarterly' => Carbon::parse($startDate)->addMonths($i * 3),
                default => Carbon::parse($startDate)->addMonths($i),
            };

            $payment = min($monthlyPayment, $balance);

            $schedule[] = [
                'installment' => $i,
                'due_date' => $paymentDate->format('Y-m-d'),
                'amount' => $payment,
                'balance' => $balance - $payment,
            ];

            $balance -= $payment;
        }

        return $schedule;
    }

    /**
     * Check if loan is eligible for rescheduling
     */
    public function canBeRescheduled(): bool
    {
        return $this->isDisbursed() || $this->status === 'active' || $this->status === 'arrears';
    }

    public function notifications()
    {
        return $this->hasMany(Notification::class);
    }
}
