<?php

namespace App\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class RecurringTransaction extends Model
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array<string>
     */
    protected $fillable = [
        'user_id',
        'account_id',
        'category_id',
        'type',
        'amount',
        'description',
        'payee',
        'frequency',
        'interval',
        'start_date',
        'end_date',
        'next_due_date',
        'last_processed_date',
        'auto_post',
        'remind_days_before',
        'is_active',
        'notes',
    ];

    /**
     * The attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'amount' => 'decimal:2',
            'start_date' => 'date',
            'end_date' => 'date',
            'next_due_date' => 'date',
            'last_processed_date' => 'date',
            'auto_post' => 'boolean',
            'is_active' => 'boolean',
        ];
    }

    /**
     * Frequency labels.
     */
    public const FREQUENCY_LABELS = [
        'daily' => 'Daily',
        'weekly' => 'Weekly',
        'biweekly' => 'Bi-weekly',
        'monthly' => 'Monthly',
        'quarterly' => 'Quarterly',
        'yearly' => 'Yearly',
    ];

    /**
     * Get the user that owns this recurring transaction.
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    /**
     * Get the account for this recurring transaction.
     */
    public function account(): BelongsTo
    {
        return $this->belongsTo(Account::class);
    }

    /**
     * Get the category for this recurring transaction.
     */
    public function category(): BelongsTo
    {
        return $this->belongsTo(Category::class);
    }

    /**
     * Get transactions created from this recurring transaction.
     */
    public function transactions(): HasMany
    {
        return $this->hasMany(Transaction::class);
    }

    /**
     * Scope to get active recurring transactions.
     */
    public function scopeActive($query)
    {
        return $query->where('is_active', true);
    }

    /**
     * Scope to get due recurring transactions.
     */
    public function scopeDue($query)
    {
        return $query->where('next_due_date', '<=', now()->toDateString())
            ->where(function ($q) {
                $q->whereNull('end_date')
                    ->orWhere('end_date', '>=', now()->toDateString());
            });
    }

    /**
     * Scope to get upcoming recurring transactions within X days.
     */
    public function scopeUpcoming($query, int $days = 7)
    {
        return $query->whereBetween('next_due_date', [
            now()->toDateString(),
            now()->addDays($days)->toDateString(),
        ]);
    }

    /**
     * Get the frequency label.
     */
    public function getFrequencyLabelAttribute(): string
    {
        $label = self::FREQUENCY_LABELS[$this->frequency] ?? $this->frequency;

        if ($this->interval > 1) {
            return "Every {$this->interval} " . strtolower($label);
        }

        return $label;
    }

    /**
     * Calculate the next due date after processing.
     */
    public function calculateNextDueDate(): Carbon
    {
        $current = $this->next_due_date->copy();

        return match ($this->frequency) {
            'daily' => $current->addDays($this->interval),
            'weekly' => $current->addWeeks($this->interval),
            'biweekly' => $current->addWeeks(2 * $this->interval),
            'monthly' => $current->addMonths($this->interval),
            'quarterly' => $current->addMonths(3 * $this->interval),
            'yearly' => $current->addYears($this->interval),
            default => $current->addMonths($this->interval),
        };
    }

    /**
     * Process this recurring transaction (create a transaction).
     */
    public function process(): ?Transaction
    {
        if (!$this->is_active || $this->next_due_date > now()) {
            return null;
        }

        if ($this->end_date && $this->next_due_date > $this->end_date) {
            $this->update(['is_active' => false]);
            return null;
        }

        $transaction = Transaction::create([
            'user_id' => $this->user_id,
            'account_id' => $this->account_id,
            'category_id' => $this->category_id,
            'currency_id' => $this->account->currency_id,
            'recurring_transaction_id' => $this->id,
            'type' => $this->type,
            'amount' => $this->amount,
            'date' => $this->next_due_date,
            'description' => $this->description,
            'payee' => $this->payee,
            'notes' => $this->notes,
            'is_recurring' => true,
        ]);

        $this->update([
            'last_processed_date' => $this->next_due_date,
            'next_due_date' => $this->calculateNextDueDate(),
        ]);

        return $transaction;
    }

    /**
     * Skip the next occurrence.
     */
    public function skip(): void
    {
        $this->update([
            'next_due_date' => $this->calculateNextDueDate(),
        ]);
    }

    /**
     * Check if reminder should be sent.
     */
    public function shouldRemind(): bool
    {
        if (!$this->is_active || $this->remind_days_before <= 0) {
            return false;
        }

        $reminderDate = $this->next_due_date->copy()->subDays($this->remind_days_before);

        return $reminderDate->isToday();
    }
}
