<?php

declare(strict_types=1);

namespace Tournament\Stage;

use PDO;
use RuntimeException;
use Throwable;

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

    /**
     * آماده‌سازی Stage اول
     *
     * accepted event_players
     * ->
     * tournament_participants
     * ->
     * tournament_stage_participants
     */
    public function prepareInitialStage(
        int $stageId,
        int $limit = 0
    ): array {
        $players =
            $this->getEligibleEventPlayers(
                $stageId
            );

        if ($limit > 0) {
            $players = array_slice(
                $players,
                0,
                $limit
            );
        }

        if (count($players) < 2) {
            throw new RuntimeException(
                'At least 2 eligible event players are required.'
            );
        }

        $participantIds = [];

        foreach ($players as $index => $player) {

            $eventPlayerId =
                (int) $player['id'];

            /*
             * Participant موجود؟
             */
            $statement = $this->pdo->prepare("
                SELECT id
                FROM tournament_participants
                WHERE event_player_id = ?
                LIMIT 1
            ");

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

            $participantId =
                $statement->fetchColumn();

            /*
             * ساخت Participant در صورت نیاز
             */
            if ($participantId === false) {

                $statement = $this->pdo->prepare("
                    INSERT INTO tournament_participants (
                        event_player_id,
                        seed,
                        status
                    )
                    VALUES (?, NULL, 'active')
                ");

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

                $participantId =
                    (int) $this->pdo->lastInsertId();

            } else {

                $participantId =
                    (int) $participantId;
            }

            /*
             * Participant فعال
             */
            $statement = $this->pdo->prepare("
                UPDATE tournament_participants
                SET status = 'active'
                WHERE id = ?
            ");

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

            /*
             * Seed اولیه
             */
            $seed =
                $index + 1;

            /*
             * اتصال به Stage
             */
            $statement = $this->pdo->prepare("
                INSERT INTO tournament_stage_participants (
                    stage_id,
                    tournament_participant_id,
                    seed,
                    status
                )
                VALUES (?, ?, ?, 'active')
                ON DUPLICATE KEY UPDATE
                    seed = VALUES(seed),
                    status = 'active',
                    updated_at = NOW()
            ");

            $statement->execute([
                $stageId,
                $participantId,
                $seed
            ]);

            $participantIds[] =
                $participantId;
        }

        return $participantIds;
    }

    /**
     * آماده‌سازی Stage بعدی
     *
     * Advancement Stage قبلی
     * ->
     * tournament_stage_participants Stage جدید
     */
    public function prepareNextStage(
        int $fromStageId,
        int $toStageId
    ): array {
        $this->assertStageExists(
            $toStageId
        );

        $statement = $this->pdo->prepare("
            SELECT
                participant_id,
                rank_position,
                total_score
            FROM tournament_stage_advancements
            WHERE from_stage_id = ?
              AND status = 'qualified'
            ORDER BY rank_position ASC
        ");

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

        $qualifiers =
            $statement->fetchAll(
                PDO::FETCH_ASSOC
            );

        if (!$qualifiers) {
            throw new RuntimeException(
                'No qualified participants found for next stage.'
            );
        }

        $participantIds = [];

        foreach ($qualifiers as $qualifier) {

            $participantId =
                (int) $qualifier['participant_id'];

            $seed =
                (int) $qualifier['rank_position'];

            /*
             * اتصال به Stage مقصد
             */
            $statement = $this->pdo->prepare("
                INSERT INTO tournament_stage_participants (
                    stage_id,
                    tournament_participant_id,
                    seed,
                    status
                )
                VALUES (?, ?, ?, 'active')
                ON DUPLICATE KEY UPDATE
                    seed = VALUES(seed),
                    status = 'active',
                    updated_at = NOW()
            ");

            $statement->execute([
                $toStageId,
                $participantId,
                $seed
            ]);

            /*
             * Participant اصلی فعال بماند
             */
            $statement = $this->pdo->prepare("
                UPDATE tournament_participants
                SET status = 'active'
                WHERE id = ?
            ");

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

            $participantIds[] =
                $participantId;
        }

        return $participantIds;
    }

    public function prepareForStage(
        int $stageId,
        int $limit = 0
    ): array {
        $stage = $this->getStage(
            $stageId
        );

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

        /*
        * اگر اولین Stage ایونت است
        */
        if ((int) $stage['stage_order'] === 1) {

            return $this->prepareInitialStage(
                $stageId,
                $limit
            );
        }

        /*
        * پیدا کردن Stage قبلی
        */
        $statement = $this->pdo->prepare("
            SELECT
                id,
                stage_order,
                status
            FROM tournament_stages
            WHERE ev_id = ?
            AND stage_order < ?
            ORDER BY stage_order DESC
            LIMIT 1
        ");

        $statement->execute([
            (int) $stage['ev_id'],
            (int) $stage['stage_order']
        ]);

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

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

        /*
        * Stage قبلی باید تمام شده باشد
        */
        if ($previousStage['status'] !== 'completed') {
            throw new RuntimeException(
                'Stage قبلی هنوز تکمیل نشده است.'
            );
        }

        /*
        * فقط Qualifiedهای Stage قبلی
        */
        return $this->prepareNextStage(
            (int) $previousStage['id'],
            $stageId,
        );
    }

    /**
     * دریافت accepted players برای Stage اول
     */
    private function getEligibleEventPlayers(
        int $stageId
    ): array {
        $statement = $this->pdo->prepare("
            SELECT
                ep.id,
                ep.user_id,
                ep.ign,
                ep.squad_id,
                ep.ev_id
            FROM tournament_stages ts
            INNER JOIN event_players ep
                ON ep.ev_id = ts.ev_id
            WHERE ts.id = ?
              AND ep.status = 'accepted'
            ORDER BY ep.id ASC
        ");

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

        return $statement->fetchAll(
            PDO::FETCH_ASSOC
        );
    }

    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 assertStageExists(
        int $stageId
    ): void {
        $statement = $this->pdo->prepare("
            SELECT id
            FROM tournament_stages
            WHERE id = ?
            LIMIT 1
        ");

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

        if (
            $statement->fetchColumn() === false
        ) {
            throw new RuntimeException(
                'Stage not found.'
            );
        }
    }
}