<?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\Database\Eloquent\Relations\HasMany;

class Transaction extends Model
{
    use HasFactory, SoftDeletes;

    protected $fillable = [
        'user_id',
        'account_id',
        'category_id',
        'cost_center_id',
        'amount',
        'planned_amount',
        'type',
        'description',
        'original_description',
        'notes',
        'effective_date',
        'planned_date',
        'status',
        'reference',
        'import_hash',
        'import_log_id',
        'is_recurring',
        'recurring_parent_id',
        'recurring_instance_id',
        'transfer_pair_id',
        'parent_transaction_id',
        'is_split_child',
        'is_split_parent',
        'duplicate_ignored_with',
        'is_transfer',
        'transfer_linked_id',
        'is_refund_pair',
        'refund_linked_id',
    ];

    protected $casts = [
        'amount' => 'decimal:2',
        'planned_amount' => 'decimal:2',
        'effective_date' => 'date',
        'planned_date' => 'date',
        'is_recurring' => 'boolean',
        'is_split_child' => 'boolean',
        'is_split_parent' => 'boolean',
        'is_transfer' => 'boolean',
        'is_refund_pair' => 'boolean',
    ];

    // =========================================================================
    // RELACIONAMENTOS
    // =========================================================================

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    public function account(): BelongsTo
    {
        return $this->belongsTo(Account::class);
    }

    public function category(): BelongsTo
    {
        return $this->belongsTo(Category::class);
    }

    public function costCenter(): BelongsTo
    {
        return $this->belongsTo(CostCenter::class);
    }

    public function recurringParent(): BelongsTo
    {
        return $this->belongsTo(Transaction::class, 'recurring_parent_id');
    }

    public function recurringChildren(): HasMany
    {
        return $this->hasMany(Transaction::class, 'recurring_parent_id');
    }

    /**
     * Instância de recorrência vinculada
     */
    public function recurringInstance(): BelongsTo
    {
        return $this->belongsTo(RecurringInstance::class, 'recurring_instance_id');
    }

    public function importLog(): BelongsTo
    {
        return $this->belongsTo(ImportLog::class);
    }

    /**
     * Transação par de transferência (débito ↔ crédito)
     */
    public function transferPair(): BelongsTo
    {
        return $this->belongsTo(Transaction::class, 'transfer_pair_id');
    }

    /**
     * Transação pai (quando esta é uma divisão)
     */
    public function parentTransaction(): BelongsTo
    {
        return $this->belongsTo(Transaction::class, 'parent_transaction_id');
    }

    /**
     * Transações filhas (divisões desta transação)
     */
    public function splitChildren(): HasMany
    {
        return $this->hasMany(Transaction::class, 'parent_transaction_id');
    }

    // =========================================================================
    // SCOPES
    // =========================================================================

    public function scopeOfUser($query, $userId)
    {
        return $query->where('user_id', $userId);
    }

    public function scopePending($query)
    {
        return $query->where('status', 'pending');
    }

    public function scopeCompleted($query)
    {
        return $query->where('status', 'completed');
    }

    public function scopeCancelled($query)
    {
        return $query->where('status', 'cancelled');
    }

    public function scopeCredits($query)
    {
        return $query->where('type', 'credit');
    }

    public function scopeDebits($query)
    {
        return $query->where('type', 'debit');
    }

    public function scopeInPeriod($query, $startDate, $endDate, $dateField = 'planned_date')
    {
        return $query->whereBetween($dateField, [$startDate, $endDate]);
    }

    public function scopeOfAccount($query, $accountId)
    {
        return $query->where('account_id', $accountId);
    }

    public function scopeOfCategory($query, $categoryId)
    {
        return $query->where('category_id', $categoryId);
    }

    public function scopeOfCostCenter($query, $costCenterId)
    {
        return $query->where('cost_center_id', $costCenterId);
    }

    // =========================================================================
    // ATRIBUTOS COMPUTADOS
    // =========================================================================

    /**
     * Retorna o valor final (efetivo se existir, senão planejado)
     */
    public function getFinalAmountAttribute(): float
    {
        return $this->amount ?? $this->planned_amount;
    }

    /**
     * Retorna a data final (efetiva se existir, senão planejada)
     */
    public function getFinalDateAttribute()
    {
        return $this->effective_date ?? $this->planned_date;
    }

    /**
     * Verifica se a transação está atrasada (pendente e data planejada passou)
     */
    public function getIsOverdueAttribute(): bool
    {
        if ($this->status !== 'pending') {
            return false;
        }
        return $this->planned_date < now()->startOfDay();
    }

    /**
     * Retorna o valor com sinal (positivo para crédito, negativo para débito)
     */
    public function getSignedAmountAttribute(): float
    {
        $amount = $this->final_amount;
        return $this->type === 'credit' ? $amount : -$amount;
    }

    // =========================================================================
    // MÉTODOS
    // =========================================================================

    /**
     * Marca a transação como concluída
     */
    public function markAsCompleted(?float $amount = null, ?string $effectiveDate = null): self
    {
        $this->status = 'completed';
        $this->amount = $amount ?? $this->planned_amount;
        $this->effective_date = $effectiveDate ?? now()->toDateString();
        $this->save();

        return $this;
    }

    /**
     * Marca a transação como cancelada
     */
    public function markAsCancelled(): self
    {
        $this->status = 'cancelled';
        $this->save();

        return $this;
    }

    /**
     * Reverte para pendente
     */
    public function markAsPending(): self
    {
        $this->status = 'pending';
        $this->amount = null;
        $this->effective_date = null;
        $this->save();

        return $this;
    }

    /**
     * Gera hash único para evitar duplicidade na importação
     * Baseado em: data + valor + descrição original + saldo (se disponível no extrato)
     * 
     * O saldo é usado APENAS para diferenciar transações idênticas no hash,
     * mas NÃO é armazenado na BD para não interferir no cálculo dinâmico de saldo.
     */
    public static function generateImportHash(
        string $date,
        float $amount,
        ?string $originalDescription,
        ?float $balance = null
    ): string {
        // Normaliza os valores para garantir consistência
        $normalizedDate = date('Y-m-d', strtotime($date));
        $normalizedAmount = number_format($amount, 2, '.', '');
        $normalizedDescription = trim(strtolower($originalDescription ?? ''));
        
        // Prepara os componentes do hash
        $components = [
            $normalizedDate,
            $normalizedAmount,
            $normalizedDescription,
        ];
        
        // Se o saldo foi fornecido no extrato, usa para diferenciar transações idênticas
        if ($balance !== null) {
            $components[] = number_format($balance, 2, '.', '');
        }
        
        // Concatena os valores e gera hash SHA-256
        return hash('sha256', implode('|', $components));
    }

    /**
     * Verifica se já existe transação com este hash para o usuário
     */
    public static function existsByHash(int $userId, string $hash): bool
    {
        return self::where('user_id', $userId)
            ->where('import_hash', $hash)
            ->exists();
    }

    /**
     * Scope para buscar por hash de importação
     */
    public function scopeByImportHash($query, string $hash)
    {
        return $query->where('import_hash', $hash);
    }

    /**
     * Verifica se a transação foi importada
     */
    public function getIsImportedAttribute(): bool
    {
        return !empty($this->import_hash);
    }
}
