<?php

namespace App\Models;

use RuntimeException;
use App\Traits\HasMeta;
use App\Traits\Likeable;
use App\Traits\Taggable;
use Illuminate\Support\Str;
use PhpParser\Node\Expr\FuncCall;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;

class Product extends Model
{
    use HasFactory, SoftDeletes, HasMeta, Likeable, Taggable;

    protected $fillable = [
        'user_id',
        'category_id',
        'brand_id',
        'title',
        'code',
        'slug',
        'short_description',
        'content',
        'inventory',
        'weight',
        'length',
        'width',
        'height',
        'price',
        'discount_price',
        'off_rate',
        'second_hand',
        'consumable',
        'status',
        'brand_img',
        'attributes',
    ];


    protected $with = ['inventories', 'inventoryVariants'];
    public function getDisplayPriceAttribute()
    {
        return $this->price == 0 ? 'برای خرید تماس بگیرید' : number_format($this->inventoryVariants->min('price_override') ?? $this->price);
    }

    /**
     * The part below is for this model relationships
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

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

    public function brand(): BelongsTo
    {
        return $this->belongsTo(Brand::class);
    }
    public function inventories(): HasMany
    {
        return $this->hasMany(ProductInventory::class, 'product_id');
    }
    public function inventoryVariants(): HasManyThrough
    {
        return $this->hasManyThrough(related: ProductInventoryVariant::class, through: ProductInventory::class, firstKey: 'product_id', secondKey: 'product_inventory_id');
    }

    public function attribute(): HasMany
    {
        return $this->hasMany(ProductAttributeValue::class, 'product_id');
    }
    public function metas(): HasMany
    {
        return $this->hasMany(ProductMeta::class, 'product_id');
    }

    public function comments()
    {
        return $this->morphMany(Comment::class, 'commentable');
    }

    public function sellers(): HasManyThrough
    {
        return $this->hasManyThrough(related: Shop::class, through: ProductInventory::class, firstKey: 'product_id', secondKey: 'id', localKey: 'id', secondLocalKey: 'shop_id');
    }
    public function viewers(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'product_views')
            ->withTimestamps();
    }
    public function productBundles()
    {
        return $this->belongsToMany(ProductBundle::class, 'product_bundle_items')
            ->withPivot('quantity')
            ->withTimestamps();
    }
    /**
     * The above is for this model relationships
     */

    public static function generateSlug(string $title): string
    {
        $baseSlug = Str::slug($title);
        $slugCount = static::where('slug', 'like', "$baseSlug%")->count();

        return $slugCount === 0 ? $baseSlug : $baseSlug . '-' . ($slugCount + 1);
    }

    public static function generateCode(int $length = 7, int $maxAttempts = 5): string
    {
        $attempts = 0;

        do {
            $code = strtoupper(Str::random($length));
            $attempts++;

            if ($attempts >= $maxAttempts) {
                throw new RuntimeException(__('api.request_failed'), 500);
            }
        } while (static::where('code', $code)->exists());

        return $code;
    }


    /**
     * helper functions
     */
    public function hasSpecialOffer(): bool
    {
        return $this->off_rate > 0;
    }


    public function getInventoryCount(): int
    {
        return $this->inventoryVariants()
            ->with('warehouseInventory')
            ->get()
            ->sum(fn($variant): float|int => max(
                0,
                ($variant->warehouseInventory->qty_on_hand ?? 0) - ($variant->warehouseInventory->qty_reserved ?? 0)
            ));
    }


    public function isInStock(): bool
    {
        return $this->getInventoryCount() > 0;
    }
}
