<?php

declare(strict_types=1);

namespace Tournament\Bracket;

use PDO;
use RuntimeException;

final class BracketStateManager
{
    private const STATUS_PENDING   = 'pending';
    private const STATUS_ACTIVE    = 'active';
    private const STATUS_COMPLETED = 'completed';
    private const STATUS_CANCELLED = 'cancelled';

    public function __construct(
        private readonly PDO $pdo
    ) {
    }

    /**
     * Activate a pending bracket.
     */
    public function activate(int $bracketId): void
    {
        $this->changeStatus(
            $bracketId,
            self::STATUS_PENDING,
            self::STATUS_ACTIVE
        );
    }

    /**
     * Complete an active bracket.
     */
    public function complete(int $bracketId): void
    {
        $this->changeStatus(
            $bracketId,
            self::STATUS_ACTIVE,
            self::STATUS_COMPLETED
        );
    }

    /**
     * Cancel a bracket.
     *
     * A completed bracket cannot be cancelled.
     */
    public function cancel(int $bracketId): void
    {
        $bracket = $this->getBracket(
            $bracketId
        );

        if ($bracket['status'] === self::STATUS_COMPLETED) {
            throw new RuntimeException(
                'Completed bracket cannot be cancelled.'
            );
        }

        if ($bracket['status'] === self::STATUS_CANCELLED) {
            return;
        }

        $statement = $this->pdo->prepare(
            <<<SQL
            UPDATE tournament_brackets
            SET status = ?
            WHERE id = ?
            SQL
        );

        $statement->execute([
            self::STATUS_CANCELLED,
            $bracketId,
        ]);
    }

    /**
     * Get current bracket status.
     */
    public function getStatus(
        int $bracketId
    ): string {
        $bracket = $this->getBracket(
            $bracketId
        );

        return $bracket['status'];
    }

    /**
     * Check whether bracket is active.
     */
    public function isActive(
        int $bracketId
    ): bool {
        return $this->getStatus($bracketId)
            === self::STATUS_ACTIVE;
    }

    /**
     * Check whether bracket is completed.
     */
    public function isCompleted(
        int $bracketId
    ): bool {
        return $this->getStatus($bracketId)
            === self::STATUS_COMPLETED;
    }

    /**
     * Change bracket status safely.
     */
    private function changeStatus(
        int $bracketId,
        string $from,
        string $to
    ): void {
        $statement = $this->pdo->prepare(
            <<<SQL
            UPDATE tournament_brackets
            SET status = ?
            WHERE id = ?
              AND status = ?
            SQL
        );

        $statement->execute([
            $to,
            $bracketId,
            $from,
        ]);

        if ($statement->rowCount() !== 1) {
            throw new RuntimeException(
                sprintf(
                    'Cannot change bracket %d status from "%s" to "%s".',
                    $bracketId,
                    $from,
                    $to
                )
            );
        }
    }

    /**
     * Get bracket.
     */
    private function getBracket(
        int $bracketId
    ): array {
        $statement = $this->pdo->prepare(
            <<<SQL
            SELECT
                id,
                stage_id,
                name,
                type,
                status,
                participant_count
            FROM tournament_brackets
            WHERE id = ?
            LIMIT 1
            SQL
        );

        $statement->execute([
            $bracketId,
        ]);

        $bracket = $statement->fetch(
            PDO::FETCH_ASSOC
        );

        if ($bracket === false) {
            throw new RuntimeException(
                'Bracket not found.'
            );
        }

        return $bracket;
    }
}