<?php

namespace App\Domain\Inventory\Models;

use App\Domain\Inventory\Enums\ProductType;
use App\Domain\Shared\Traits\Auditable;
use App\Domain\Shared\Traits\BelongsToAcademy;
use App\Domain\Shared\Traits\HasUuid;
use App\Domain\Shared\Traits\ManglesUniqueOnDelete;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Domain\Shared\Traits\BelongsToBranch;

class Product extends Model
{
    use BelongsToAcademy, HasUuid, SoftDeletes, Auditable, ManglesUniqueOnDelete, BelongsToBranch;

    protected array $uniqueFieldsToMangle = ['sku'];

    protected $fillable = [
        'academy_id',
        'category_id',
        'branch_id',
        'sku',
        'barcode',
        'name_ar',
        'name',
        'description_ar',
        'type',
        'unit',
        'selling_price',
        'cost_price',
        'track_inventory',
        'is_active',
        'is_essential',
        'allows_partial_payment',
        'minimum_deposit_percent',
        'billing_cycle',
        'member_price',
        'non_member_price',
        'min_stock_level',
        'max_stock_level',
        'weight_grams',
        'dimensions',
        'photo_path',
        'tax_rate',
        'sort_order',
        'created_by',
        'metadata',
    ];

    protected function casts(): array
    {
        return [
            'type' => ProductType::class,
            'selling_price' => 'integer',
            'cost_price' => 'integer',
            'member_price' => 'integer',
            'non_member_price' => 'integer',
            'track_inventory' => 'boolean',
            'is_active' => 'boolean',
            'is_essential' => 'boolean',
            'allows_partial_payment' => 'boolean',
            'minimum_deposit_percent' => 'integer',
            'min_stock_level' => 'integer',
            'max_stock_level' => 'integer',
            'weight_grams' => 'integer',
            'dimensions' => 'array',
            'tax_rate' => 'integer',
            'sort_order' => 'integer',
            'metadata' => 'array',
        ];
    }

    public function priceForTier(string $membershipType): int
    {
        return $this->explicitTierPrice($membershipType) ?? $this->selling_price;
    }

    /**
     * The price this product was deliberately given for one membership tier —
     * or null when nobody set one.
     *
     * priceForTier() answers "what do we charge", and falls back to the
     * catalogue price so a sale is never blocked. That fallback is exactly
     * wrong for the pricing engine, which has to be able to tell "a member pays
     * 8,000 because someone typed 8,000 in the member column" apart from "a
     * member pays 8,000 because nobody filled that column in". Only the first
     * is a membership-specific price, and only the first may outrank a base
     * price row.
     */
    public function explicitTierPrice(string $membershipType): ?int
    {
        return match ($membershipType) {
            'member' => $this->member_price,
            'non_member' => $this->non_member_price,
            default => null,
        };
    }

    public function isAnnual(): bool
    {
        return $this->billing_cycle === 'annual';
    }

    /**
     * Programmes that bundle this product — the other side of
     * TrainingProgram::bundledProducts(). Needed to ask "which products does
     * this player's programme require" from the product's side, which is how
     * the settlement wizard offers the right ones.
     */
    public function programs()
    {
        return $this->belongsToMany(
            \App\Domain\Training\Models\TrainingProgram::class,
            'program_products',
            'product_id',
            'training_program_id'
        )->withPivot(['is_required', 'quantity'])->withTimestamps();
    }

    public function category(): BelongsTo
    {
        return $this->belongsTo(ProductCategory::class, 'category_id');
    }

    public function branch(): BelongsTo
    {
        return $this->belongsTo(\App\Domain\Identity\Models\Branch::class);
    }

    public function creator(): BelongsTo
    {
        return $this->belongsTo(User::class, 'created_by');
    }

    public function inventoryLevels(): HasMany
    {
        return $this->hasMany(InventoryLevel::class);
    }

    public function installmentPlans(): HasMany
    {
        return $this->hasMany(ProductInstallmentPlan::class);
    }

    /**
     * The details the desk must capture before this product can be sold —
     * size, colour, the name printed on the back.
     */
    public function customizations(): HasMany
    {
        return $this->hasMany(ProductCustomization::class)
            ->orderBy('sort_order')
            ->orderBy('id');
    }

    public function activeCustomizations(): HasMany
    {
        return $this->customizations()->where('is_active', true);
    }

    public function scopeActive(Builder $query): Builder
    {
        return $query->where('is_active', true);
    }

    public function scopeTracked(Builder $query): Builder
    {
        return $query->where('track_inventory', true);
    }

    public function scopeByCategory(Builder $query, int $categoryId): Builder
    {
        return $query->where('category_id', $categoryId);
    }
}
