<?php

namespace App\Models;

use App\Traits\MultiTenantScope;
use App\ValueObjects\Money;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class FinancialOperation extends Model
{
    use MultiTenantScope;

    /**
     * Campos que pueden ser asignados en masa.
     *
     * Excluye deliberadamente: id, created_at, updated_at (automáticos).
     * company_id se incluye porque los servicios internos lo asignan explícitamente;
     * MultiTenantScope lo sobreescribe en el evento 'creating' cuando no viene en el payload.
     */
    protected $fillable = [
        'company_id',
        'user_id',
        'operation_type',
        'source_credit_id',
        'target_credit_id',
        'credit_amount',
        'compensated_amount',
        'cash_out',
        'cash_in',
        'expense_id',
        'income_id',
        'notes',
        'metadata',
    ];

    protected $casts = [
        'credit_amount' => 'decimal:2',
        'compensated_amount' => 'decimal:2',
        'cash_out' => 'decimal:2',
        'cash_in' => 'decimal:2',
        'metadata' => 'array',
    ];

    /**
     * Reglas duras de consistencia financiera
     */
    protected static function booted(): void
    {
        static::creating(function (FinancialOperation $op) {

            // Regla base
            if ($op->cash_out < 0 || $op->cash_in < 0) {
                throw new \RuntimeException('Los valores de caja no pueden ser negativos.');
            }

            /**
             * Operaciones que generan un crédito nuevo
             */
            if (in_array($op->operation_type, [
                'credit_disbursement',
                'credit_extension',
                'credit_refinance',
                'credit_restructure',
                'credit_renewal',
            ])) {

                if (! $op->target_credit_id) {
                    throw new \RuntimeException(
                        'Las operaciones de crédito deben tener target_credit_id.'
                    );
                }
            }

            /**
             * Extensión / refinanciación / reestructuración / renovación
             */
            if (in_array($op->operation_type, [
                'credit_extension',
                'credit_refinance',
                'credit_restructure',
                'credit_renewal',
            ])) {

                if (! $op->source_credit_id) {
                    throw new \RuntimeException(
                        'Las operaciones estructurales requieren source_credit_id.'
                    );
                }

                if ($op->compensated_amount <= 0) {
                    throw new \RuntimeException(
                        'Debe indicarse el monto compensado del crédito anterior.'
                    );
                }
            }

            /**
             * Coherencia financiera:
             * cash_out = credit_amount - compensated_amount
             */
            if ($op->credit_amount > 0) {
                $expectedCashOut = Money::of($op->credit_amount)
                    ->subtract($op->compensated_amount);

                if (! Money::of($op->cash_out)->equals($expectedCashOut)) {
                    throw new \RuntimeException(
                        'cash_out no coincide con credit_amount - compensated_amount.'
                    );
                }
            }
        });
    }

    /* =====================
     * Relaciones
     * ===================== */

    public function company()
    {
        return $this->belongsTo(Company::class);
    }

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

    public function sourceCredit()
    {
        return $this->belongsTo(Credit::class, 'source_credit_id');
    }

    public function targetCredit(): BelongsTo
    {
        return $this->belongsTo(Credit::class, 'target_credit_id');
    }

    public function expense()
    {
        return $this->belongsTo(Expense::class);
    }

    public function income()
    {
        return $this->belongsTo(Income::class);
    }
}
