<?php

declare(strict_types=1);

namespace App\Enums;

/**
 * Lifecycle state of a physical asset.
 *
 * Stored as VARCHAR rather than a MySQL ENUM so that adding a state never
 * requires an ALTER TABLE on a large, live table.
 *
 * The transition matrix below is the single definition of which state changes
 * are legal. Without it, a UI dropdown eventually lets someone move a disposed
 * asset back into stock and the audit trail becomes fiction.
 */
enum AssetStatus: string implements HasBadge
{
    case InStock = 'in_stock';
    case Assigned = 'assigned';
    case UnderRepair = 'under_repair';
    case Damaged = 'damaged';
    case Lost = 'lost';
    case Retired = 'retired';
    case Disposed = 'disposed';

    /**
     * Values that may be picked when creating an asset.
     *
     * @return array<int, self>
     */
    public static function assignableOnCreate(): array
    {
        return [self::InStock, self::UnderRepair, self::Damaged];
    }

    /**
     * @return array<string, string> value => label, for select inputs
     */
    public static function options(): array
    {
        return array_reduce(
            self::cases(),
            static fn (array $carry, self $case): array => $carry + [$case->value => $case->label()],
            [],
        );
    }

    public function label(): string
    {
        return match ($this) {
            self::InStock => 'In Stock',
            self::Assigned => 'Assigned',
            self::UnderRepair => 'Under Repair',
            self::Damaged => 'Damaged',
            self::Lost => 'Lost',
            self::Retired => 'Retired',
            self::Disposed => 'Disposed',
        };
    }

    public function color(): string
    {
        return match ($this) {
            self::InStock => 'success',
            self::Assigned => 'primary',
            self::UnderRepair => 'warning',
            self::Damaged => 'danger',
            self::Lost => 'danger',
            self::Retired => 'secondary',
            self::Disposed => 'dark',
        };
    }

    public function icon(): string
    {
        return match ($this) {
            self::InStock => 'box-seam',
            self::Assigned => 'person-check',
            self::UnderRepair => 'tools',
            self::Damaged => 'exclamation-triangle',
            self::Lost => 'question-circle',
            self::Retired => 'archive',
            self::Disposed => 'trash3',
        };
    }

    /**
     * Only an in-stock asset can be issued to an employee.
     */
    public function canBeAssigned(): bool
    {
        return $this === self::InStock;
    }

    /**
     * A terminal state cannot transition anywhere.
     */
    public function isTerminal(): bool
    {
        return $this === self::Disposed;
    }

    /**
     * States this asset may move to next.
     *
     * `Assigned` is never offered here: an asset becomes assigned only through
     * AssignmentService, and leaves that state only through a recorded return.
     *
     * @return array<int, self>
     */
    public function allowedTransitions(): array
    {
        return match ($this) {
            self::InStock => [self::UnderRepair, self::Damaged, self::Lost, self::Retired, self::Disposed],
            self::Assigned => [self::Lost, self::Damaged],
            self::UnderRepair => [self::InStock, self::Damaged, self::Disposed],
            self::Damaged => [self::UnderRepair, self::Retired, self::Disposed],
            self::Lost => [self::InStock, self::Disposed],
            self::Retired => [self::Disposed],
            self::Disposed => [],
        };
    }

    public function canTransitionTo(self $target): bool
    {
        return in_array($target, $this->allowedTransitions(), strict: true);
    }
}
