<?php
/**
 * BookiFlex Core
 *
 * @package    BookiFlex
 * @subpackage Core
 * @author     BookiFlex
 * @copyright  Copyright (c) 2025 BookiFlex
 * @license    GPL-2.0-or-later
 *
 * @link       https://bookiflex.com
 */

declare(strict_types=1);

namespace BookiFlex\Core\Model;

use BookiFlex\Core\Exception\RuntimeException as DomainRuntimeException;

defined( 'ABSPATH' ) || exit;

/**
 * Abstract class for status objects.
 * Supports transitioning between statuses.
 */
abstract class AbstractStatus
{
    private string $status;

    protected static array $validTransitions = [];

    /**
     * @throws DomainRuntimeException
     */
    final public function __construct(string $initialStatus)
    {
        if (!array_key_exists($initialStatus, static::$validTransitions)) {
            throw new DomainRuntimeException(
                sprintf(
                    'Invalid initial status: %s.',
                    $initialStatus
                ),
                400,
                null,
                ['status' => $initialStatus]
            );
        }
        $this->status = $initialStatus;
    }

    /**
     * @throws DomainRuntimeException
     */
    public static function from(string $status): static
    {
        return new static($status);
    }

    public function getValue(): string
    {
        return $this->status;
    }

    public function canTransitionTo(string $newStatus): bool
    {
        return in_array($newStatus, static::$validTransitions[$this->status], true);
    }

    /**
     * @throws DomainRuntimeException
     */
    public function transitionTo(string $newStatus): void
    {
        if (!$this->canTransitionTo($newStatus)) {
            throw new DomainRuntimeException(
                sprintf(
                    'Cannot transition from %1$s to %2$s',
                    $this->status,
                    $newStatus
                ),
                400,
                null,
                ['status' => $this->status, 'newStatus' => $newStatus]
            );
        }
        $this->status = $newStatus;
    }

    public function isEquals(string $status): bool
    {
        return $this->status === $status;
    }

    public function __toString(): string
    {
        return $this->status;
    }
}
