<?php

declare(strict_types=1);

namespace PhpWA\Runtime;

use InvalidArgumentException;

/** Secret-free progress snapshot supplied to an application run guard. */
final class RunCheckpoint
{
    public const BEFORE_OPEN = 'before_open';
    public const AFTER_OPEN = 'after_open';
    public const BEFORE_RECEIVE = 'before_receive';
    public const AFTER_RECEIVE = 'after_receive';
    public const IDLE = 'idle';
    public const AFTER_NODE = 'after_node';
    public const BEFORE_CALLBACK = 'before_callback';
    public const BEFORE_MAINTENANCE = 'before_maintenance';

    private const PHASES = [
        self::BEFORE_OPEN,
        self::AFTER_OPEN,
        self::BEFORE_RECEIVE,
        self::AFTER_RECEIVE,
        self::IDLE,
        self::AFTER_NODE,
        self::BEFORE_CALLBACK,
        self::BEFORE_MAINTENANCE,
    ];

    /** @var string */ private $accountId;
    /** @var string */ private $phase;
    /** @var string */ private $state;
    /** @var int */ private $nodesProcessed;
    /** @var float */ private $remainingSeconds;

    public function __construct(
        string $accountId,
        string $phase,
        string $state,
        int $nodesProcessed,
        float $remainingSeconds
    ) {
        if ($accountId === '' || strlen($accountId) > 512 || preg_match('//u', $accountId) !== 1
            || preg_match('/[\x00-\x1f\x7f]/', $accountId) === 1) {
            throw new InvalidArgumentException('Run checkpoint account ID is invalid.');
        }
        if (!in_array($phase, self::PHASES, true)
            || preg_match('/^[a-z][a-z0-9_]{0,63}$/D', $state) !== 1
            || $nodesProcessed < 0
            || !is_finite($remainingSeconds)
            || $remainingSeconds < 0.0
            || $remainingSeconds > 300.0) {
            throw new InvalidArgumentException('Run checkpoint is invalid.');
        }
        $this->accountId = $accountId;
        $this->phase = $phase;
        $this->state = $state;
        $this->nodesProcessed = $nodesProcessed;
        $this->remainingSeconds = $remainingSeconds;
    }

    public function accountId(): string { return $this->accountId; }
    public function phase(): string { return $this->phase; }
    public function state(): string { return $this->state; }
    public function nodesProcessed(): int { return $this->nodesProcessed; }
    public function remainingSeconds(): float { return $this->remainingSeconds; }
}
