<?php

namespace App\Models;

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

class Contribution extends Model
{
    use HasFactory, SoftDeletes;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'reference_number',
        'member_id',
        'membership_category_id',
        'contribution_fee_id',
        'amount',
        'penalty_amount',
        'total_amount',
        'currency',
        'due_date',
        'paid_date',
        'period',
        'status',
        'is_penalty_applied',
        'penalty_applied_at',
        'payment_method',
        'payment_reference',
        'transaction_id',
        'payment_details',
        'notes',
        'admin_notes',
        'created_by',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'amount' => 'decimal:2',
        'penalty_amount' => 'decimal:2',
        'total_amount' => 'decimal:2',
        'due_date' => 'date',
        'paid_date' => 'date',
        'is_penalty_applied' => 'boolean',
        'penalty_applied_at' => 'datetime',
        'payment_details' => 'array',
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
        'deleted_at' => 'datetime',
    ];

    /**
     * The attributes that should be appended.
     *
     * @var array<int, string>
     */
    protected $appends = ['status_badge_color', 'formatted_amount'];

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

    /**
     * Get the membership category for the contribution.
     */
    public function membershipCategory(): BelongsTo
    {
        return $this->belongsTo(MembershipCategory::class);
    }

    /**
     * Get the contribution fee for the contribution.
     */
    public function contributionFee(): BelongsTo
    {
        return $this->belongsTo(ContributionFee::class);
    }

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

    /**
     * Get the status badge color.
     */
    public function getStatusBadgeColorAttribute(): string
    {
        return match($this->status) {
            'pending' => 'yellow',
            'paid' => 'green',
            'overdue' => 'red',
            'cancelled' => 'gray',
            'refunded' => 'blue',
            default => 'gray',
        };
    }

    /**
     * Get formatted amount with currency.
     */
    public function getFormattedAmountAttribute(): string
    {
        $symbols = [
            'USD' => '$',
            'SSP' => '£',
            'KES' => 'KSh',
            'UGX' => 'USh',
        ];

        $symbol = $symbols[$this->currency] ?? $this->currency . ' ';
        return $symbol . number_format($this->amount, 2);
    }

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

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

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

    /**
     * Scope a query to filter by period.
     */
    public function scopePeriod($query, string $period)
    {
        return $query->where('period', $period);
    }

    /**
     * Scope a query to filter by membership category.
     */
    public function scopeByCategory($query, int $categoryId)
    {
        return $query->where('membership_category_id', $categoryId);
    }

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

        return $reference;
    }

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

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

    /**
     * Calculate 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(),
            'payment_method' => $paymentData['payment_method'] ?? null,
            'payment_reference' => $paymentData['payment_reference'] ?? null,
            'transaction_id' => $paymentData['transaction_id'] ?? null,
            'payment_details' => $paymentData['payment_details'] ?? null,
        ]);
    }

    /**
     * Apply penalty.
     */
    public function applyPenalty(float $penaltyAmount): void
    {
        $this->update([
            'penalty_amount' => $penaltyAmount,
            'total_amount' => $this->amount + $penaltyAmount,
            'is_penalty_applied' => true,
            'penalty_applied_at' => now(),
            'status' => 'overdue',
        ]);
    }
}
