<?php

declare(strict_types=1);

namespace App\Models;

use App\Models\Concerns\Auditable;
use Database\Factories\AssetCategoryFactory;
use Illuminate\Database\Eloquent\Builder;
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\SoftDeletes;

/**
 * Asset classification, as a two-level hierarchy.
 *
 * `code` is the middle segment of every generated asset tag (AUM-LAP-0042), so
 * it is short, mandatory, unique and upper-cased on save. Changing it does not
 * rewrite existing tags — those are already printed on physical labels.
 *
 * The depth cap of two levels lives in validation rather than in the schema, so
 * it can be relaxed later without a migration.
 *
 * @property int $id
 * @property int|null $parent_id
 * @property string $name
 * @property string $code
 * @property int|null $depreciation_years
 * @property int|null $default_warranty_months
 * @property bool $is_active
 */
class AssetCategory extends Model
{
    /** @use HasFactory<AssetCategoryFactory> */
    use Auditable, HasFactory, SoftDeletes;

    /**
     * How deep the hierarchy may go. A root plus one level of subcategory is
     * enough to classify hardware without turning the picker into a tree view.
     */
    public const MAX_DEPTH = 2;

    /** @var list<string> */
    protected $fillable = [
        'parent_id',
        'name',
        'code',
        'depreciation_years',
        'default_warranty_months',
        'is_active',
    ];

    /**
     * @return BelongsTo<AssetCategory, $this>
     */
    public function parent(): BelongsTo
    {
        return $this->belongsTo(self::class, 'parent_id');
    }

    /**
     * @return HasMany<AssetCategory, $this>
     */
    public function children(): HasMany
    {
        return $this->hasMany(self::class, 'parent_id');
    }

    /**
     * Every asset classified under this category.
     *
     * @return HasMany<Asset, $this>
     */
    public function assets(): HasMany
    {
        return $this->hasMany(Asset::class, 'category_id');
    }

    /**
     * Whether this category sits at the top of the hierarchy.
     */
    public function isRoot(): bool
    {
        return $this->parent_id === null;
    }

    /**
     * "Laptop" for a root, "Laptop / Ultrabook" for a subcategory.
     */
    public function qualifiedName(): string
    {
        return $this->parent === null
            ? $this->name
            : $this->parent->name.' / '.$this->name;
    }

    /**
     * @param  Builder<AssetCategory>  $query
     */
    public function scopeRoots(Builder $query): void
    {
        $query->whereNull('parent_id');
    }

    /**
     * @param  Builder<AssetCategory>  $query
     */
    public function scopeActive(Builder $query): void
    {
        $query->where('is_active', true);
    }

    /**
     * @param  Builder<AssetCategory>  $query
     */
    public function scopeSearch(Builder $query, ?string $term): void
    {
        $term = trim((string) $term);

        if ($term === '') {
            return;
        }

        $query->where(function (Builder $inner) use ($term): void {
            $inner->where('name', 'like', '%'.$term.'%')
                ->orWhere('code', 'like', '%'.$term.'%');
        });
    }

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'is_active' => 'boolean',
            'depreciation_years' => 'integer',
            'default_warranty_months' => 'integer',
        ];
    }

    protected function auditLabel(): string
    {
        return $this->name;
    }
}
