<?php

namespace App\Domains\Catalog\Models;

use App\Domains\Attachable\Models\Attachment;
use App\Domains\Catalog\Enums\CatalogItemType;
use App\Domains\Catalog\Enums\InventoryPolicy;
use App\Domains\Catalog\Services\CatalogInventoryService;
use App\Domains\Tenant\Models\Tenant;
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\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;

#[Fillable([
    'tenant_code',
    'category_id',
    'brand_id',
    'inventory_id',
    'type',
    'slug',
    'nombre',
    'descripcion',
    'precio',
    'inventory_policy',
    'has_tickets',
    'maximum_use_date',
    'minimum_use_date',
])]
class CatalogItem extends Model
{
    use HasFactory;

    public $timestamps = false;

    protected $table = 'catalog_items';

    protected $attributes = [
        'type' => CatalogItemType::Standard->value,
        'inventory_policy' => InventoryPolicy::Tracked->value,
        'has_tickets' => false,
    ];

    protected function casts(): array
    {
        return [
            'category_id' => 'integer',
            'brand_id' => 'integer',
            'inventory_id' => 'integer',
            'type' => CatalogItemType::class,
            'precio' => 'decimal:2',
            'inventory_policy' => InventoryPolicy::class,
            'has_tickets' => 'boolean',
            'maximum_use_date' => 'datetime',
            'minimum_use_date' => 'datetime',
        ];
    }

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

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

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

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

    /** @return HasMany<BundleComponent, $this> */
    public function bundleComponents(): HasMany
    {
        return $this->hasMany(BundleComponent::class, 'bundle_catalog_item_id');
    }

    /** @return HasMany<BundleComponent, $this> */
    public function bundleComponentUsages(): HasMany
    {
        return $this->hasMany(BundleComponent::class, 'component_catalog_item_id');
    }

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

    /** @return BelongsToMany<Attribute, $this> */
    public function attributes(): BelongsToMany
    {
        return $this->belongsToMany(Attribute::class, 'item_attributes')
            ->withTimestamps();
    }

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

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

    /** @return BelongsToMany<Attachment, $this> */
    public function attachments(): BelongsToMany
    {
        return $this->belongsToMany(
            Attachment::class,
            'catalog_items_attachments',
            'catalog_item_id',
            'attachment_id'
        )
            ->withPivot('orden')
            ->wherePivotNull('variant_id')
            ->orderByPivot('orden');
    }

    public function availableStock(): ?int
    {
        return app(CatalogInventoryService::class)->availableQuantity($this);
    }

    public function isAvailable(): bool
    {
        if ($this->type === CatalogItemType::Bundle) {
            $availableStock = $this->availableStock();

            return $availableStock === null || $availableStock > 0;
        }

        if ($this->inventory_policy === InventoryPolicy::Unlimited) {
            return true;
        }

        return ($this->availableStock() ?? 0) > 0;
    }

    public function getPrice(): float
    {
        return (float) $this->precio;
    }

    public function getName(): string
    {
        return $this->nombre;
    }

    public function isBundle(): bool
    {
        return $this->type === CatalogItemType::Bundle;
    }
}
