<?php

namespace App\Domains\Purchase\Models;

use App\Domains\Auth\Models\User;
use App\Domains\Cart\Models\Cart;
use App\Domains\Catalog\Models\StockReservation;
use App\Domains\Logging\Models\Concerns\LogsValueChanges;
use App\Domains\Purchase\Events\PurchasePaid;
use App\Domains\Tenant\Models\Tenant;
use App\Domains\Ticket\Models\Ticket;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Support\Facades\DB;

#[Fillable([
    'cart_id',
    'tenant_codigo',
    'user_id',
    'status',
    'payment_method',
    'expires_at',
    'total',
    'dni',
    'transfer_payer_dni',
    'telefono',
    'nombre_apellido',
    'email',
])]
class Purchase extends Model
{
    use HasFactory, LogsValueChanges;

    public const STATUS_CREATED = 'created';

    public const STATUS_PENDING_PAYMENT = 'pending_payment';

    public const STATUS_IN_REVIEW = 'in_review';

    public const STATUS_PAID = 'paid';

    public const STATUS_CANCELLED = 'cancelled';

    public const STATUS_REJECTED = 'rejected';

    public const STATUS_EXPIRED = 'expired';

    public const STATUS_SUPERSEDED = 'superseded';

    /** @return list<string> */
    public static function statuses(): array
    {
        return [
            self::STATUS_CREATED,
            self::STATUS_PENDING_PAYMENT,
            self::STATUS_IN_REVIEW,
            self::STATUS_PAID,
            self::STATUS_CANCELLED,
            self::STATUS_REJECTED,
            self::STATUS_EXPIRED,
            self::STATUS_SUPERSEDED,
        ];
    }

    protected $table = 'compras';

    /** @var array<int, string> */
    protected array $loggedAttributes = [
        'status',
    ];

    protected function casts(): array
    {
        return [
            'cart_id' => 'integer',
            'user_id' => 'integer',
            'expires_at' => 'datetime',
            'total' => 'decimal:2',
        ];
    }

    /**
     * @return BelongsTo<Tenant, $this>
     */
    public function tenant(): BelongsTo
    {
        return $this->belongsTo(Tenant::class, 'tenant_codigo', 'codigo');
    }

    /**
     * @return BelongsTo<User, $this>
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    /**
     * @return BelongsTo<Cart, $this>
     */
    public function cart(): BelongsTo
    {
        return $this->belongsTo(Cart::class, 'cart_id');
    }

    /**
     * @return HasMany<PurchaseItem, $this>
     */
    public function items(): HasMany
    {
        return $this->hasMany(PurchaseItem::class, 'compra_id');
    }

    /**
     * @return HasMany<Ticket, $this>
     */
    public function tickets(): HasMany
    {
        return $this->hasMany(Ticket::class, 'source_purchase_id');
    }

    /** @return HasMany<StockReservation, $this> */
    public function stockReservations(): HasMany
    {
        return $this->hasMany(StockReservation::class);
    }

    /**
     * @return HasOne<TelepagosQr, $this>
     */
    public function telepagosQr()
    {
        return $this->hasOne(TelepagosQr::class, 'compra_id');
    }

    /**
     * @return HasMany<TelepagosPayment, $this>
     */
    public function telepagosPayments(): HasMany
    {
        return $this->hasMany(TelepagosPayment::class, 'compra_id');
    }

    /** @return HasMany<TelepagosPaymentCandidate, $this> */
    public function telepagosPaymentCandidates(): HasMany
    {
        return $this->hasMany(TelepagosPaymentCandidate::class, 'compra_id');
    }

    public function getTotalAmount(): float
    {
        if ($this->total !== null) {
            return (float) $this->total;
        }

        return $this->calculateCurrentTotalAmount();
    }

    public function calculateCurrentTotalAmount(): float
    {
        if ($this->relationLoaded('items') && $this->getRelation('items')->isNotEmpty()) {
            return (float) $this->getRelation('items')->sum('total');
        }

        return (float) $this->items()->sum('total');
    }

    protected function valueChangeTenantCode(): string
    {
        return $this->tenant_codigo;
    }

    public function markAsPendingPayment(): void
    {
        $this->update([
            'status' => self::STATUS_PENDING_PAYMENT,
        ]);
    }

    public function markAsPaid(): void
    {
        DB::transaction(function (): void {
            $currentStatus = self::query()
                ->whereKey($this->getKey())
                ->lockForUpdate()
                ->value('status');

            if ($currentStatus === self::STATUS_PAID) {
                $this->status = self::STATUS_PAID;

                return;
            }

            $this->update([
                'status' => self::STATUS_PAID,
            ]);

            PurchasePaid::dispatch($this->getKey());
        });
    }
}
