<?php

namespace Modules\Vrc\Services\RepairOrder;

/**
 * Repair-order state machine (spec §4.3). The authoritative transition graph
 * lives in config('vrc.repair_order.transitions'); this service enforces it.
 * Guard *rules* that depend on order data (e.g. "estimate_sent requires ≥1
 * line", "in_progress requires ≥1 technician") are layered on top in the
 * Phase 4 RepairOrderService — this class owns only the legal-graph check so
 * it stays pure and unit-testable without the framework or a database.
 */
class RepairOrderStateMachine
{
    /** @var array<string, string[]> from-status => allowed to-statuses */
    protected array $transitions;

    protected string $initial;

    /**
     * @param  array<string,string[]>|null  $transitions  defaults to config
     */
    public function __construct(?array $transitions = null, ?string $initial = null)
    {
        $this->transitions = $transitions
            ?? (function_exists('config') ? (array) config('vrc.repair_order.transitions', []) : []);
        $this->initial = $initial
            ?? (function_exists('config') ? (string) config('vrc.repair_order.initial_status', 'draft') : 'draft');
    }

    public function initialStatus(): string
    {
        return $this->initial;
    }

    /** All known statuses (sources + targets), de-duplicated. */
    public function statuses(): array
    {
        $all = array_keys($this->transitions);
        foreach ($this->transitions as $targets) {
            $all = array_merge($all, $targets);
        }
        return array_values(array_unique($all));
    }

    /** Statuses reachable from $from. */
    public function allowedFrom(string $from): array
    {
        return $this->transitions[$from] ?? [];
    }

    public function canTransition(string $from, string $to): bool
    {
        return in_array($to, $this->allowedFrom($from), true);
    }

    /** A status with no outgoing transitions (completed / cancelled). */
    public function isTerminal(string $status): bool
    {
        return empty($this->transitions[$status] ?? []);
    }

    /**
     * @throws InvalidTransitionException when the transition is not allowed.
     */
    public function assertCanTransition(string $from, string $to): void
    {
        if (! $this->canTransition($from, $to)) {
            throw new InvalidTransitionException($from, $to);
        }
    }
}
