<?php

namespace App\Models\Inventory;

use App\Models\Catalog\Product;
use App\Models\Concerns\Auditable;
use App\Models\Concerns\BelongsToTenant;
use App\Models\Concerns\BranchScoped;
use App\Models\Lookups\UnitOfMeasure;
use App\Services\BarcodeLabelService;
use App\Support\Quantity;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;

/**
 * A barcode the shop prints itself, standing for one exact thing to sell:
 * this product, out of this lot, this serial, this much.
 *
 * A manufacturer's barcode identifies a product and nothing else, so scanning
 * one at the till still leaves the cashier to answer "which lot?", "which
 * serial?" and "how many?" by hand. Everything a printed label encodes is
 * decided once, here, when the label is made — so the scan itself is the whole
 * interaction: {@see BarcodeLabelService::resolve()} hands the
 * terminal a finished cart line.
 *
 * The label is a pointer, never a price: the lot it names is still the price
 * owner, and InvoiceService reprices from that lot at checkout.
 */
class BarcodeLabel extends Model
{
    use Auditable, BelongsToTenant, BranchScoped, SoftDeletes;

    /**
     * The namespace every printed label's code lives in.
     *
     * Its whole job is to be recognisable without a lookup: the terminal only
     * asks the server about a scan that starts with this, so an ordinary SKU
     * scan stays a zero-round-trip, client-side match. Changing it orphans
     * every label already on a shelf.
     */
    public const CODE_PREFIX = 'LBL-';

    protected $fillable = [
        'uuid', 'tenant_id', 'code', 'product_id', 'branch_id',
        'stock_batch_id', 'product_serial_id',
        'quantity', 'unit_id', 'base_quantity',
        'label_name', 'note', 'is_active',
        'printed_count', 'last_printed_at',
        'created_by', 'updated_by', 'deleted_by',
    ];

    protected function casts(): array
    {
        return [
            'quantity' => 'decimal:4',
            'base_quantity' => 'decimal:4',
            'is_active' => 'boolean',
            'printed_count' => 'integer',
            'last_printed_at' => 'datetime',
        ];
    }

    protected static function booted(): void
    {
        static::creating(function (self $label): void {
            if (! $label->uuid) {
                $label->uuid = (string) Str::ulid();
            }
        });
    }

    // ── Relations ─────────────────────────────────────────────────────────────

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

    public function batch(): BelongsTo
    {
        return $this->belongsTo(StockBatch::class, 'stock_batch_id');
    }

    public function serial(): BelongsTo
    {
        return $this->belongsTo(ProductSerial::class, 'product_serial_id');
    }

    public function unit(): BelongsTo
    {
        return $this->belongsTo(UnitOfMeasure::class, 'unit_id');
    }

    // ── Scopes ────────────────────────────────────────────────────────────────

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

    // ── Helpers ───────────────────────────────────────────────────────────────

    /** The caption printed above the barcode. */
    public function displayName(): string
    {
        return $this->label_name ?: (string) ($this->product?->name ?? '');
    }

    /** "250 g", "1 BOX" — as keyed in, not as converted. */
    public function quantityLabel(): string
    {
        return Quantity::format($this->quantity, $this->unit ?? $this->product?->baseUnit);
    }
}
