<?php

declare(strict_types=1);

namespace Tournament\Stage;

use PDO;
use RuntimeException;
use Throwable;

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

    /**
     * Complete current stage and prepare next stage.
     *
     * @return array{
     *     current_stage_id:int,
     *     next_stage_id:int|null,
     *     qualified_count:int,
     *     next_stage_generated:bool
     * }
     */
    public function progress(
        int $stageId
    ): array {
        $stage =
            $this->getStage($stageId);

        if (!$stage) {
            throw new RuntimeException(
                'Stage not found.'
            );
        }

        /*
         * فقط Stageهای تکمیل‌شده
         */
        if ($stage['status'] !== 'active') {
            throw new RuntimeException(
                'Only active stages can progress.'
            );
        }

        /*
         * همه Roomها باید completed باشند.
         */
        $this->assertAllRoomsCompleted(
            $stageId
        );

        /*
         * Leaderboard
         */
        $leaderboardService =
            new BattleRoyaleLeaderboard(
                $this->pdo
            );

        $leaderboardResult =
            $leaderboardService->generate(
                $stageId
            );

        /*
         * Advancement
         */
        $advancementService =
            new BattleRoyaleAdvancement(
                $this->pdo
            );

        $advancementResult =
            $advancementService->process(
                $stageId
            );

        $qualifiedCount =
            (int) (
                $advancementResult['qualified']
                ?? 0
            );

        /*
         * Stage فعلی را completed کن.
         */
        $statement = $this->pdo->prepare("
            UPDATE tournament_stages
            SET
                status = 'completed',
                updated_at = NOW()
            WHERE id = ?
              AND status = 'active'
        ");

        $statement->execute([
            $stageId
        ]);

        /*
         * Stage بعدی
         */
        $nextStage =
            $this->getNextStage(
                (int) $stage['ev_id'],
                (int) $stage['stage_order']
            );

        /*
         * آخرین Stage بوده.
         */
        if (!$nextStage) {
            return [
                'current_stage_id' =>
                    $stageId,

                'next_stage_id' =>
                    null,

                'qualified_count' =>
                    $qualifiedCount,

                'next_stage_generated' =>
                    false,
            ];
        }

        /*
         * انتقال صعودکننده‌ها به Stage بعدی
         */
        $participantService =
            new StageParticipantService(
                $this->pdo
            );

        $qualifiedIds =
            $participantService->prepareNextStage(
                $stageId,
                (int) $nextStage['id']
            );

        if (
            count($qualifiedIds)
            !== $qualifiedCount
        ) {
            throw new RuntimeException(
                'تعداد Participantهای Stage بعدی با تعداد صعودکنندگان مطابقت ندارد.'
            );
        }

        /*
         * تولید ساختار Stage بعدی
         */
        $generated = false;

        if (
            $nextStage['format']
            === 'battle_royale'
        ) {
            $generator =
                new BattleRoyaleStageGenerator(
                    $this->pdo
                );

            $generator->generate(
                (int) $nextStage['id']
            );

            $generated = true;
        }

        /*
         * Elimination فعلاً فقط Participantها
         * را آماده می‌کند؛ Bracket را بعداً وصل می‌کنیم.
         */
        elseif (
            $nextStage['format']
            === 'elimination'
        ) {
            // TODO:
            // TournamentBracketGenerator
            // will be connected here.
        }

        return [
            'current_stage_id' =>
                $stageId,

            'next_stage_id' =>
                (int) $nextStage['id'],

            'qualified_count' =>
                $qualifiedCount,

            'next_stage_generated' =>
                $generated,
        ];
    }

    private function getStage(
        int $stageId
    ): ?array {
        $statement = $this->pdo->prepare("
            SELECT
                id,
                ev_id,
                name,
                stage_order,
                format,
                type,
                status
            FROM tournament_stages
            WHERE id = ?
            LIMIT 1
        ");

        $statement->execute([
            $stageId
        ]);

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

        return $stage ?: null;
    }

    private function getNextStage(
        int $eventId,
        int $currentOrder
    ): ?array {
        $statement = $this->pdo->prepare("
            SELECT
                id,
                ev_id,
                name,
                stage_order,
                format,
                type,
                status
            FROM tournament_stages
            WHERE ev_id = ?
              AND stage_order > ?
              AND status IN (
                  'pending',
                  'active'
              )
            ORDER BY stage_order ASC
            LIMIT 1
        ");

        $statement->execute([
            $eventId,
            $currentOrder
        ]);

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

        return $stage ?: null;
    }

    private function assertAllRoomsCompleted(
        int $stageId
    ): void {
        $statement = $this->pdo->prepare("
            SELECT
                COUNT(*) AS total_rooms,
                COALESCE(
                    SUM(
                        CASE
                            WHEN status = 'completed'
                            THEN 1
                            ELSE 0
                        END
                    ),
                    0
                ) AS completed_rooms
            FROM tournament_br_rooms
            WHERE stage_id = ?
        ");

        $statement->execute([
            $stageId
        ]);

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

        $total =
            (int) $stats['total_rooms'];

        $completed =
            (int) $stats['completed_rooms'];

        if ($total === 0) {
            throw new RuntimeException(
                'هیچ Roomای برای این Stage وجود ندارد.'
            );
        }

        if ($total !== $completed) {
            throw new RuntimeException(
                "همه Roomهای Stage هنوز تکمیل نشده‌اند "
                . "({$completed}/{$total})."
            );
        }
    }
}