<?php

declare(strict_types=1);

namespace Tournament\Stage;

use RuntimeException;

final class BattleRoyaleAdvancementCalculator
{
    /**
     * محاسبه سهمیه صعود هر Group در Battle Royale
     *
     * قوانین:
     *
     * - advancement_count تعداد کل صعودی‌های هدف Stage است.
     * - سهم هر Group مستقل محاسبه می‌شود.
     * - اگر Group ظرفیت کافی نداشته باشد،
     *   سهم استفاده‌نشده حذف می‌شود.
     * - هیچ سهمی به Group دیگر منتقل نمی‌شود.
     *
     * مثال:
     *
     * advancement_count = 240
     * groups = 11
     *
     * نتیجه:
     *
     * Group 1-9  => 22 نفر
     * Group 10-11 => 21 نفر
     *
     * اگر Group 3 فقط 18 نفر داشته باشد:
     *
     * Group 3 => 18 نفر
     *
     * چهار سهم باقی‌مانده حذف می‌شود.
     *
     * @param array<int,int> $groupEligibleCounts
     *
     * @return array<int,int>
     */
    public function calculateGroupQuotas(
        array $groupEligibleCounts,
        string $advancementType,
        int $advancementCount
    ): array {

        /*
         * ==========================================
         * Validation
         * ==========================================
         */

        if (!$groupEligibleCounts) {
            throw new RuntimeException(
                'هیچ Groupای برای محاسبه سهمیه وجود ندارد.'
            );
        }


        if ($advancementCount < 0) {
            throw new RuntimeException(
                'تعداد صعودکنندگان نامعتبر است.'
            );
        }


        $type =
            strtolower(
                trim(
                    $advancementType
                )
            );


        if ($type !== 'top_n') {

            throw new RuntimeException(
                'نوع advancement پشتیبانی نمی‌شود: '
                . $advancementType
            );
        }


        /*
         * ==========================================
         * Normalize Groups
         * ==========================================
         */

        $groups = [];

        foreach (
            $groupEligibleCounts as $groupNumber => $count
        ) {

            $groupNumber =
                (int) $groupNumber;

            $count =
                (int) $count;


            if ($groupNumber <= 0) {

                throw new RuntimeException(
                    "شماره Group نامعتبر است: {$groupNumber}"
                );
            }


            if ($count < 0) {

                throw new RuntimeException(
                    "ظرفیت Group #{$groupNumber} نامعتبر است."
                );
            }


            $groups[$groupNumber] =
                $count;
        }


        ksort(
            $groups,
            SORT_NUMERIC
        );


        $groupCount =
            count($groups);


        if ($groupCount <= 0) {

            throw new RuntimeException(
                'تعداد Groupها نامعتبر است.'
            );
        }


        /*
         * ==========================================
         * محاسبه سهم ثابت
         * ==========================================
         *
         * مثال:
         *
         * 240 / 11
         *
         * base = 21
         * remainder = 9
         *
         */

        $baseQuota =
            intdiv(
                $advancementCount,
                $groupCount
            );


        $remainder =
            $advancementCount
            %
            $groupCount;


        $quotas = [];


        /*
         * ==========================================
         * Assign Fixed Quota
         * ==========================================
         */

        foreach (
            $groups as $groupNumber => $eligibleCount
        ) {

            /*
             * سهم پایه
             */
            $quota =
                $baseQuota;


            /*
             * Groupهای اول remainder
             * یک نفر بیشتر می‌گیرند.
             */
            if ($remainder > 0) {

                $quota++;
                $remainder--;
            }


            /*
             * محدودیت ظرفیت واقعی Group
             *
             * بدون Redistribution
             */
            $quotas[$groupNumber] =
                min(
                    $quota,
                    $eligibleCount
                );
        }


        /*
         * ==========================================
         * Final Validation
         * ==========================================
         */

        foreach (
            $quotas as $groupNumber => $quota
        ) {

            if ($quota < 0) {

                throw new RuntimeException(
                    "سهمیه Group #{$groupNumber} نامعتبر است."
                );
            }
        }


        return $quotas;
    }


    /**
     * دریافت سهم یک Group خاص
     *
     * @param array<int,int> $groupEligibleCounts
     */
    public function getGroupQuota(
        array $groupEligibleCounts,
        int $groupNumber,
        string $advancementType,
        int $advancementCount
    ): int {

        if (
            !array_key_exists(
                $groupNumber,
                $groupEligibleCounts
            )
        ) {

            throw new RuntimeException(
                "Group #{$groupNumber} پیدا نشد."
            );
        }


        $quotas =
            $this->calculateGroupQuotas(
                $groupEligibleCounts,
                $advancementType,
                $advancementCount
            );


        return (int) (
            $quotas[$groupNumber]
            ??
            0
        );
    }


    /**
     * بررسی اینکه سهم همه Groupها برابر است یا نه.
     *
     * برای نمایش در UI.
     *
     * @param array<int,int> $quotas
     */
    public function isUniform(
        array $quotas
    ): bool {

        if (!$quotas) {
            return true;
        }


        return count(
            array_unique(
                array_values(
                    $quotas
                )
            )
        ) === 1;
    }
}