<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Builder;

class Transaction extends Model
{
    use SoftDeletes;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<string>
     */
    protected $fillable = [
        'user_id',
        'account_id',
        'category_id',
        'currency_id',
        'recurring_transaction_id',
        'type',
        'amount',
        'amount_in_base_currency',
        'exchange_rate',
        'date',
        'time',
        'description',
        'notes',
        'payee',
        'reference',
        'location',
        'attachment',
        'attachment_name',
        'is_cleared',
        'is_recurring',
    ];

    /**
     * The attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'amount' => 'decimal:2',
            'amount_in_base_currency' => 'decimal:2',
            'exchange_rate' => 'decimal:8',
            'date' => 'date',
            'is_cleared' => 'boolean',
            'is_recurring' => 'boolean',
        ];
    }

    /**
     * Boot the model.
     */
    protected static function boot()
    {
        parent::boot();

        // Update account balance when transaction is created
        static::created(function ($transaction) {
            $transaction->updateAccountBalance();
        });

        // Update account balance when transaction is updated
        static::updated(function ($transaction) {
            if ($transaction->wasChanged(['amount', 'type', 'account_id'])) {
                // If account changed, recalculate both old and new accounts
                if ($transaction->wasChanged('account_id')) {
                    $oldAccountId = $transaction->getOriginal('account_id');
                    Account::find($oldAccountId)?->recalculateBalance();
                }
                $transaction->account->recalculateBalance();
            }
        });

        // Update account balance when transaction is deleted
        static::deleted(function ($transaction) {
            $transaction->account->recalculateBalance();
        });

        // Update account balance when transaction is restored
        static::restored(function ($transaction) {
            $transaction->account->recalculateBalance();
        });
    }

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

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

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

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

    /**
     * Get the recurring transaction this was created from.
     */
    public function recurringTransaction(): BelongsTo
    {
        return $this->belongsTo(RecurringTransaction::class);
    }

    /**
     * Get tags for this transaction.
     */
    public function tags(): BelongsToMany
    {
        return $this->belongsToMany(Tag::class, 'transaction_tag');
    }

    /**
     * Scope to filter by type.
     */
    public function scopeOfType(Builder $query, string $type): Builder
    {
        return $query->where('type', $type);
    }

    /**
     * Scope to filter income transactions.
     */
    public function scopeIncome(Builder $query): Builder
    {
        return $query->where('type', 'income');
    }

    /**
     * Scope to filter expense transactions.
     */
    public function scopeExpense(Builder $query): Builder
    {
        return $query->where('type', 'expense');
    }

    /**
     * Scope to filter by date range.
     */
    public function scopeDateRange(Builder $query, string $startDate, string $endDate): Builder
    {
        return $query->whereBetween('date', [$startDate, $endDate]);
    }

    /**
     * Scope to filter by month.
     */
    public function scopeInMonth(Builder $query, int $year, int $month): Builder
    {
        return $query->whereYear('date', $year)->whereMonth('date', $month);
    }

    /**
     * Scope to filter by year.
     */
    public function scopeInYear(Builder $query, int $year): Builder
    {
        return $query->whereYear('date', $year);
    }

    /**
     * Scope to filter by account.
     */
    public function scopeForAccount(Builder $query, int $accountId): Builder
    {
        return $query->where('account_id', $accountId);
    }

    /**
     * Scope to filter by category.
     */
    public function scopeForCategory(Builder $query, int $categoryId): Builder
    {
        return $query->where('category_id', $categoryId);
    }

    /**
     * Scope to search by description, payee, or notes.
     */
    public function scopeSearch(Builder $query, string $search): Builder
    {
        return $query->where(function ($q) use ($search) {
            $q->where('description', 'like', "%{$search}%")
              ->orWhere('payee', 'like', "%{$search}%")
              ->orWhere('notes', 'like', "%{$search}%")
              ->orWhere('reference', 'like', "%{$search}%");
        });
    }

    /**
     * Get the formatted amount with currency.
     */
    public function getFormattedAmountAttribute(): string
    {
        $amount = $this->type === 'expense' ? -$this->amount : $this->amount;
        return $this->currency->format($amount);
    }

    /**
     * Get the signed amount (negative for expenses).
     */
    public function getSignedAmountAttribute(): float
    {
        return $this->type === 'expense' ? -$this->amount : $this->amount;
    }

    /**
     * Update the account balance after transaction.
     */
    protected function updateAccountBalance(): void
    {
        $this->account->recalculateBalance();
    }

    /**
     * Check if this is an income transaction.
     */
    public function isIncome(): bool
    {
        return $this->type === 'income';
    }

    /**
     * Check if this is an expense transaction.
     */
    public function isExpense(): bool
    {
        return $this->type === 'expense';
    }

    /**
     * Duplicate this transaction.
     */
    public function duplicate(): self
    {
        $new = $this->replicate(['recurring_transaction_id', 'is_recurring']);
        $new->date = now()->toDateString();
        $new->save();

        // Copy tags
        $new->tags()->sync($this->tags->pluck('id'));

        return $new;
    }
}
