<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Str;

class LoanRepayment extends Model
{
    use HasFactory;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'reference_number',
        'loan_application_id',
        'member_id',
        'installment_number',
        'due_date',
        'scheduled_amount',
        'scheduled_principal',
        'scheduled_interest',
        'amount_paid',
        'principal_paid',
        'interest_paid',
        'penalty_paid',
        'extra_payment',
        'status',
        'paid_date',
        'payment_method',
        'payment_reference',
        'transaction_id',
        'payment_details',
        'penalty_amount',
        'penalty_applied',
        'penalty_applied_at',
        'principal_balance',
        'notes',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'scheduled_amount' => 'decimal:2',
        'scheduled_principal' => 'decimal:2',
        'scheduled_interest' => 'decimal:2',
        'amount_paid' => 'decimal:2',
        'principal_paid' => 'decimal:2',
        'interest_paid' => 'decimal:2',
        'penalty_paid' => 'decimal:2',
        'extra_payment' => 'decimal:2',
        'penalty_amount' => 'decimal:2',
        'principal_balance' => 'decimal:2',
        'installment_number' => 'integer',
        'due_date' => 'date',
        'paid_date' => 'date',
        'penalty_applied' => 'boolean',
        'penalty_applied_at' => 'datetime',
        'payment_details' => 'array',
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
    ];

    /**
     * Get the loan application for the repayment.
     */
    public function loanApplication(): BelongsTo
    {
        return $this->belongsTo(LoanApplication::class);
    }

    /**
     * Get the member for the repayment.
     */
    public function member(): BelongsTo
    {
        return $this->belongsTo(Member::class);
    }

    /**
     * Scope a query to only include scheduled repayments.
     */
    public function scopeScheduled($query)
    {
        return $query->where('status', 'scheduled');
    }

    /**
     * Scope a query to only include pending repayments.
     */
    public function scopePending($query)
    {
        return $query->where('status', 'pending');
    }

    /**
     * Scope a query to only include paid repayments.
     */
    public function scopePaid($query)
    {
        return $query->where('status', 'paid');
    }

    /**
     * Scope a query to only include overdue repayments.
     */
    public function scopeOverdue($query)
    {
        return $query->where('status', 'overdue')
            ->orWhere(function ($q) {
                $q->whereIn('status', ['scheduled', 'pending'])
                    ->where('due_date', '<', now());
            });
    }

    /**
     * Generate a unique reference number.
     */
    public static function generateReferenceNumber(): string
    {
        do {
            $reference = 'REP-' . date('Ymd') . '-' . strtoupper(Str::random(6));
        } while (self::where('reference_number', $reference)->exists());

        return $reference;
    }

    /**
     * Check if repayment is overdue.
     */
    public function isOverdue(): bool
    {
        return $this->status === 'overdue' || 
            (in_array($this->status, ['scheduled', 'pending']) && $this->due_date->isPast());
    }

    /**
     * Check if repayment is paid.
     */
    public function isPaid(): bool
    {
        return $this->status === 'paid';
    }

    /**
     * Get days overdue.
     */
    public function getDaysOverdue(): int
    {
        if (!$this->isOverdue()) {
            return 0;
        }

        return now()->diffInDays($this->due_date);
    }

    /**
     * Mark as paid.
     */
    public function markAsPaid(array $paymentData): void
    {
        $this->update([
            'status' => 'paid',
            'paid_date' => $paymentData['paid_date'] ?? now(),
            'amount_paid' => $paymentData['amount_paid'] ?? $this->scheduled_amount,
            'principal_paid' => $paymentData['principal_paid'] ?? $this->scheduled_principal,
            'interest_paid' => $paymentData['interest_paid'] ?? $this->scheduled_interest,
            'payment_method' => $paymentData['payment_method'] ?? null,
            'payment_reference' => $paymentData['payment_reference'] ?? null,
            'transaction_id' => $paymentData['transaction_id'] ?? null,
            'payment_details' => $paymentData['payment_details'] ?? null,
        ]);
    }
}
