<?php

namespace App\Models\Lookups;

use App\Filament\Support\StatusLookup;
use App\Models\Concerns\BelongsToTenant;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;

class Status extends Model
{
    use BelongsToTenant;

    protected static function booted(): void
    {
        static::saved(fn (self $status) => StatusLookup::forgetCache($status->context));
        static::deleted(fn (self $status) => StatusLookup::forgetCache($status->context));
    }

    // ── Contexts ──────────────────────────────────────────────────────────────
    public const CTX_INVOICE_ORDER = 'invoice_order';

    public const CTX_INVOICE_PAYMENT = 'invoice_payment';

    public const CTX_TRANSACTION = 'transaction';

    public const CTX_CREDIT_NOTE = 'credit_note';

    public const CTX_RETURN = 'return';

    public const CTX_POS_SESSION = 'pos_session';

    public const CTX_SALES_ORDER = 'sales_order';

    // ── invoice_order ─────────────────────────────────────────────────────────
    public const CREATED = 'created';

    public const INVOICED = 'invoiced';

    public const PACKING = 'packing';

    public const READY_FOR_DELIVERY = 'ready_for_delivery';

    public const OUT_FOR_DELIVERY = 'out_for_delivery';

    public const UNDELIVERED = 'undelivered';

    public const DELIVERED = 'delivered';

    public const CANCELLED = 'cancelled';

    // ── invoice_payment ───────────────────────────────────────────────────────
    public const UNPAID = 'unpaid';

    public const PARTIAL = 'partial';

    public const PAID = 'paid';

    // ── transaction / payment_split ───────────────────────────────────────────
    public const PENDING = 'pending';

    public const APPROVED = 'approved';

    public const UTILIZED = 'utilized';

    public const REJECTED = 'rejected';

    // ── credit_note ───────────────────────────────────────────────────────────
    public const DRAFT = 'draft';

    public const ISSUED = 'issued';

    public const CLAIMED = 'claimed';

    public const VOID = 'void';

    // ── return ────────────────────────────────────────────────────────────────
    public const RETURN_PENDING = 'pending';

    public const RETURN_APPROVED = 'approved';

    public const RETURN_CREDITED = 'credited';

    public const RETURN_CANCELLED = 'cancelled';

    public const RETURN_REPLACED = 'replaced';

    // ── pos_session ───────────────────────────────────────────────────────────
    public const SESSION_OPEN = 'open';

    public const SESSION_CLOSED = 'closed';

    // ── sales_order ───────────────────────────────────────────────────────────
    // The order's own lifecycle, from taking it to handing it over. Distinct
    // from `invoice_order` above, which only starts once an invoice exists.
    // `invoiced` is reached solely by converting an order, never by hand.
    public const SO_DRAFT = 'draft';

    public const SO_CONFIRMED = 'confirmed';

    public const SO_IN_PROGRESS = 'in_progress';

    public const SO_READY = 'ready';

    public const SO_DELIVERED = 'delivered';

    public const SO_INVOICED = 'invoiced';

    public const SO_CANCELLED = 'cancelled';

    // ── stock_batch ───────────────────────────────────────────────────────────
    public const CTX_STOCK_BATCH = 'stock_batch';

    public const BATCH_ACTIVE = 'active';

    /**
     * Operator-parked: the lot is still physically on the shelf and still
     * counted in branch_stock, but it is withheld from every place lots are
     * offered (POS, sales orders, transfers, adjustments, write-offs).
     */
    public const BATCH_INACTIVE = 'inactive';

    public const BATCH_EXPIRED = 'expired';

    public const BATCH_RECALLED = 'recalled';

    public const BATCH_LOCKED = 'locked';

    // ── product_serial ────────────────────────────────────────────────────────
    public const CTX_PRODUCT_SERIAL = 'product_serial';

    /** @deprecated Use SERIAL_AVAILABLE */
    public const SERIAL_IN_STOCK = 'in_stock';

    public const SERIAL_AVAILABLE = 'available';

    public const SERIAL_RESERVED = 'reserved';

    public const SERIAL_SOLD = 'sold';

    public const SERIAL_RETURNED = 'returned';

    public const SERIAL_RETURNED_TO_SUPPLIER = 'returned_to_supplier';

    public const SERIAL_UNDER_REPAIR = 'under_repair';

    public const SERIAL_DAMAGED = 'damaged';

    public const SERIAL_LOST = 'lost';

    public const SERIAL_WRITTEN_OFF = 'written_off';

    // ── supplier ──────────────────────────────────────────────────────────────
    public const CTX_SUPPLIER = 'supplier';

    public const SUPPLIER_ACTIVE = 'active';

    public const SUPPLIER_INACTIVE = 'inactive';

    public const SUPPLIER_BLACKLISTED = 'blacklisted';

    // ── purchase_order ───────────────────────────────────────────────────────
    public const CTX_PURCHASE_ORDER = 'purchase_order';

    public const PO_DRAFT = 'draft';

    public const PO_SENT = 'sent';

    public const PO_PARTIAL = 'partial';

    public const PO_RECEIVED = 'received';

    public const PO_CANCELLED = 'cancelled';

    // ── voucher ───────────────────────────────────────────────────────────────
    public const CTX_VOUCHER = 'voucher';

    public const VOUCHER_ACTIVE = 'active';

    public const VOUCHER_EXPIRED = 'expired';

    public const VOUCHER_VOID = 'void';

    public const VOUCHER_EXHAUSTED = 'exhausted';

    protected $fillable = ['context', 'code', 'name', 'color', 'sort_order', 'is_active'];

    protected function casts(): array
    {
        return [
            'is_active' => 'boolean',
            'sort_order' => 'integer',
        ];
    }

    /**
     * @param  Builder<Status>  $query
     * @return Builder<Status>
     */
    public function scopeContext(Builder $query, string $context): Builder
    {
        return $query->where('context', $context);
    }

    public static function find(string $context, string $code): ?self
    {
        return static::query()
            ->where('context', $context)
            ->where('code', $code)
            ->first();
    }

    public static function idOf(string $context, string $code): ?int
    {
        return static::find($context, $code)?->id;
    }
}
