<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;

class Loan extends Model
{
    use HasFactory;

    public const STATUSES = [
        'draft','application_submitted','waiting_for_documents','contract_generated','contract_signed','under_review',
        'correction_requested','additional_collateral_requested','rejected','ready_for_disbursement','active','completed','fully_paid',
        'closed_through_top_up','written_off',
    ];

    public const REVIEW_CHANGE_REQUEST_STATUSES = [
        'correction_requested','waiting_for_documents','additional_collateral_requested',
    ];

    public const APPROVAL_LOCKED_STATUSES = [
        'rejected','ready_for_disbursement','active','completed','fully_paid','closed_through_top_up','written_off',
    ];

    public const TRANSITIONS = [
        'draft'=>['application_submitted','waiting_for_documents','contract_generated'],
        'application_submitted'=>['waiting_for_documents','contract_generated','under_review'],
        'waiting_for_documents'=>['application_submitted','contract_generated','under_review'],
        'contract_generated'=>['waiting_for_documents','contract_signed','under_review'],
        'contract_signed'=>['under_review'],'under_review'=>[],
        'correction_requested'=>['waiting_for_documents','under_review'],
        'additional_collateral_requested'=>['waiting_for_documents','under_review'],
        'rejected'=>[],'ready_for_disbursement'=>[],'active'=>[],'completed'=>[],'fully_paid'=>[],'closed_through_top_up'=>[],'written_off'=>[],
    ];

    protected $fillable = [
        'branch_id','customer_id','application_credit_score_history_id','loan_product_id','contract_template_id','top_up_source_loan_id','top_up_eligibility_percentage','top_up_paid_percentage_at_application',
        'top_up_override_used','top_up_override_reason','top_up_override_by','top_up_override_at','guarantor_id','sequence_number','sequence_year','loan_number',
        'principal','interest','processing_fee','processing_fee_payment_mode','insurance_fee','other_charges','fees','total_repayment',
        'principal_outstanding','interest_outstanding','fees_outstanding','penalties_outstanding','total_outstanding','total_paid',
        'late_payment_penalty_type','late_payment_penalty_value','overdue_principal','overdue_interest','overdue_fees',
        'overdue_penalties','total_overdue','days_overdue','arrears_bucket','arrears_updated_at',
        'payment_deadline_enabled','payment_deadline_time','deadline_late_penalty_type','deadline_late_penalty_value','deadline_late_penalty_settlement',
        'missed_schedule_penalty_type','missed_schedule_penalty_value','missed_schedule_penalty_settlement',
        'purpose','schedule_start_date','custom_repayment_dates','status','submitted_at','contract_generated_at','contract_generated_by',
        'contract_signed_at','contract_signed_uploaded_by','approved_at','approved_by','disbursed_at','disbursed_by','activated_at',
        'schedule_activated_at','current_schedule_version','last_payment_at','completed_at','written_off_at','written_off_by','created_by','updated_by',
    ];

    protected function casts(): array
    {
        return [
            'principal'=>'decimal:2','interest'=>'decimal:2','processing_fee'=>'decimal:2','insurance_fee'=>'decimal:2','other_charges'=>'decimal:2',
            'fees'=>'decimal:2','total_repayment'=>'decimal:2','principal_outstanding'=>'decimal:2','interest_outstanding'=>'decimal:2',
            'fees_outstanding'=>'decimal:2','penalties_outstanding'=>'decimal:2','total_outstanding'=>'decimal:2','total_paid'=>'decimal:2',
            'top_up_eligibility_percentage'=>'decimal:2','top_up_paid_percentage_at_application'=>'decimal:2','top_up_override_used'=>'boolean','top_up_override_at'=>'datetime',
            'late_payment_penalty_value'=>'decimal:6','overdue_principal'=>'decimal:2','overdue_interest'=>'decimal:2','overdue_fees'=>'decimal:2',
            'overdue_penalties'=>'decimal:2','total_overdue'=>'decimal:2','days_overdue'=>'integer','arrears_updated_at'=>'datetime',
            'payment_deadline_enabled'=>'boolean','deadline_late_penalty_value'=>'decimal:6','missed_schedule_penalty_value'=>'decimal:6',
            'schedule_start_date'=>'date','custom_repayment_dates'=>'array','submitted_at'=>'datetime','contract_generated_at'=>'datetime',
            'contract_signed_at'=>'datetime','approved_at'=>'datetime','disbursed_at'=>'datetime','activated_at'=>'datetime',
            'schedule_activated_at'=>'datetime','current_schedule_version'=>'integer','last_payment_at'=>'datetime','completed_at'=>'datetime',
            'written_off_at'=>'datetime',
        ];
    }

    public function processingFeePaymentModeLabel(): string
    {
        return LoanProduct::PROCESSING_FEE_PAYMENT_MODES[$this->processing_fee_payment_mode ?: 'financed'] ?? 'Include in loan repayments';
    }

    public function netDisbursementAmount(): string
    {
        $principal = (float) $this->principal;
        $fee = (float) $this->processing_fee;
        return number_format($this->processing_fee_payment_mode === 'deducted' ? max(0, $principal - $fee) : $principal, 2, '.', '');
    }

    public function branch(): BelongsTo { return $this->belongsTo(Branch::class); }
    public function customer(): BelongsTo { return $this->belongsTo(Customer::class); }
    public function applicationCreditScoreHistory(): BelongsTo { return $this->belongsTo(CustomerCreditScoreHistory::class, 'application_credit_score_history_id'); }
    public function product(): BelongsTo { return $this->belongsTo(LoanProduct::class, 'loan_product_id'); }
    public function contractTemplate(): BelongsTo { return $this->belongsTo(ContractTemplate::class); }
    public function guarantor(): BelongsTo { return $this->belongsTo(Guarantor::class); }
    public function creator(): BelongsTo { return $this->belongsTo(User::class, 'created_by'); }
    public function approver(): BelongsTo { return $this->belongsTo(User::class, 'approved_by'); }
    public function disburser(): BelongsTo { return $this->belongsTo(User::class, 'disbursed_by'); }
    public function writtenOffBy(): BelongsTo { return $this->belongsTo(User::class, 'written_off_by'); }
    public function topUpOverrideUser(): BelongsTo { return $this->belongsTo(User::class, 'top_up_override_by'); }
    public function topUpSourceLoan(): BelongsTo { return $this->belongsTo(self::class, 'top_up_source_loan_id'); }
    public function topUpReplacementLoans(): HasMany { return $this->hasMany(self::class, 'top_up_source_loan_id'); }
    public function collaterals(): BelongsToMany { return $this->belongsToMany(Collateral::class, 'loan_collateral')->withTimestamps(); }
    public function installments(): HasMany { return $this->hasMany(LoanInstallment::class)->where('is_active', true)->orderBy('installment_number'); }
    public function allInstallments(): HasMany { return $this->hasMany(LoanInstallment::class)->orderBy('schedule_version')->orderBy('installment_number'); }
    public function documents(): HasMany { return $this->hasMany(LoanDocument::class)->latest(); }
    public function statusHistory(): HasMany { return $this->hasMany(LoanStatusHistory::class)->orderByDesc('changed_at')->orderByDesc('id'); }
    public function approvalDecisions(): HasMany { return $this->hasMany(LoanApprovalDecision::class)->orderByDesc('decided_at')->orderByDesc('id'); }
    public function disbursement(): HasOne { return $this->hasOne(LoanDisbursement::class); }
    public function topUpAsPrevious(): HasOne { return $this->hasOne(LoanTopUp::class, 'previous_loan_id'); }
    public function topUpAsNew(): HasOne { return $this->hasOne(LoanTopUp::class, 'new_loan_id'); }
    public function earlySettlement(): HasOne { return $this->hasOne(LoanEarlySettlement::class); }
    public function restructurings(): HasMany { return $this->hasMany(LoanRestructuring::class)->orderByDesc('requested_at')->orderByDesc('id'); }
    public function writeOffs(): HasMany { return $this->hasMany(LoanWriteOff::class)->orderByDesc('requested_at')->orderByDesc('id'); }
    public function writeOff(): HasOne { return $this->hasOne(LoanWriteOff::class)->where('status','approved'); }
    public function payments(): HasMany { return $this->hasMany(LoanPayment::class)->orderByDesc('payment_date')->orderByDesc('id'); }
    public function statementEntries(): HasMany { return $this->hasMany(CustomerStatementEntry::class)->orderBy('entry_date')->orderBy('id'); }
    public function penaltyCharges(): HasMany { return $this->hasMany(LoanPenaltyCharge::class)->orderByDesc('effective_date')->orderByDesc('id'); }
    public function collectionFollowUps(): HasMany { return $this->hasMany(CollectionFollowUp::class)->orderByDesc('action_at')->orderByDesc('id'); }

    public function canTransitionTo(string $status): bool { return in_array($status, self::TRANSITIONS[$this->status] ?? [], true); }
    public function canEditCoreTerms(): bool { return in_array($this->status, ['draft','correction_requested'], true); }
    public function hasSignedContract(): bool { return $this->documents()->where('kind', 'signed_contract')->exists(); }
    public function isReviewChangeRequested(): bool { return in_array($this->status, self::REVIEW_CHANGE_REQUEST_STATUSES, true); }
    public function isApplicationApprovalLocked(): bool { return in_array($this->status, self::APPROVAL_LOCKED_STATUSES, true); }

    public function canGenerateContract(): bool
    {
        return in_array($this->status, [
            'draft','application_submitted','waiting_for_documents','contract_generated','contract_signed',
            'correction_requested','additional_collateral_requested',
        ], true);
    }

    public function canUploadSignedContract(): bool
    {
        if (! $this->contract_generated_at) return false;

        return in_array($this->status, [
            'contract_generated','contract_signed','correction_requested','waiting_for_documents','additional_collateral_requested',
        ], true);
    }

    public function canUploadSupportingDocument(): bool
    {
        return in_array($this->status, [
            'draft','application_submitted','waiting_for_documents','contract_generated','contract_signed',
            'correction_requested','additional_collateral_requested',
        ], true);
    }
}
