<?php

declare(strict_types=1);

namespace Seka\Vzdelavanie\Services\Pouzivatel\Testovanie;

use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Seka\Vzdelavanie\Models\Testovanie\Test;
use Seka\Vzdelavanie\Models\Testovanie\Otazka;
use Seka\Vzdelavanie\Events\Testovanie\Frontend\TestGenerated;
use Seka\Vzdelavanie\Models\Testovanie\DruhSpravnejOdpovedeEnum;
use Seka\Vzdelavanie\Models\Pouzivatel\Testovanie\Test as TestPouzivatela;
use Seka\Vzdelavanie\Models\Pouzivatel\Testovanie\Otazka as OtazkaTestuPouzivatela;
use Seka\Vzdelavanie\Models\Pouzivatel\Testovanie\Odpoved as OdpovedOtazkaTestuPouzivatela;

/**
 * Class VygenerovanieTestu
 *
 * @package Seka\Vzdelavanie\Services\Pouzivatel\Testovanie
 */
final class VygenerovanieTestu
{
    /**
     * @param \Seka\Vzdelavanie\Models\Testovanie\Test $test
     *
     * @return \Seka\Vzdelavanie\Models\Pouzivatel\Testovanie\Test
     * @throws \Exception
     */
    public static function generovat(Test $test): TestPouzivatela
    {
        DB::beginTransaction();

        try {
            // vygenerovat test
            $vygenerovanyTest = (new TestPouzivatela())->fill($test->only([
                'nazov',
                'popis',
            ]));
            $vygenerovanyTest->pouzivatel()->associate(Auth::user());
            $vygenerovaneOtazky = collect();
            $maxPocetSpravnychOdpovedi = 0;

            $test->otazky()
                ->publikovana()
                ->inRandomOrder()
                ->take($test->pocet_otazok)
                ->get()
                ->each(
                    function (Otazka $otazka) use ($vygenerovaneOtazky, &$maxPocetSpravnychOdpovedi) {
                        $pocetOdpovedi = 3;
                        $pocetSpravnychOdpovedi = 1;

                        switch ($otazka->druh_spravnej_odpovede) {
                            case DruhSpravnejOdpovedeEnum::JEDNA():
                                $pocetSpravnychOdpovedi = 1;
                                break;
                            case DruhSpravnejOdpovedeEnum::MINIMALNE_JEDNA():
                                $pocetSpravnychOdpovedi = rand(1, 2);
                                break;
                            case DruhSpravnejOdpovedeEnum::DVE():
                                $pocetSpravnychOdpovedi = 2;
                                break;
                            case DruhSpravnejOdpovedeEnum::TRI():
                                $pocetSpravnychOdpovedi = 3;
                                break;
                            default:
                                return;
                        }

                        $otazka->odpovede = self::ziskajOdpovede($otazka, $pocetOdpovedi, $pocetSpravnychOdpovedi);
                        $vygenerovaneOtazky->add($otazka);

                        $maxPocetSpravnychOdpovedi += $pocetSpravnychOdpovedi;
                    }
                );

            $vygenerovanyTest->setAttribute('max_pocet_spravnych_odpovedi', $maxPocetSpravnychOdpovedi);
            $vygenerovanyTest->save();

            $vygenerovaneOtazky->each(function (Otazka $otazka) use ($vygenerovanyTest) {
                $genOtazka = new OtazkaTestuPouzivatela();
                $genOtazka->fill($otazka->only([
                    'znenie',
                    'druh_spravnej_odpovede',
                ]));
                $genOtazka->test()->associate($vygenerovanyTest)->save();
                $genOtazka = $genOtazka->refresh();

                $otazka->odpovede->each(function ($odpoved) use ($genOtazka) {
                    $genOdpoved = new OdpovedOtazkaTestuPouzivatela();
                    $genOdpoved->fill($odpoved->only([
                        'znenie',
                        'hodnotenie',
                    ]));
                    $genOdpoved->zvolena = false;
                    $genOdpoved->otazka()->associate($genOtazka)->save();
                });

                unset($otazka);
            });

            unset($vygenerovaneOtazky);

            // zaznamenat udalost noveho vygenerovaneho testu
            event(new TestGenerated($test, Auth::user()));

            DB::commit();

            return $vygenerovanyTest;
        } catch (Exception $exc) {
            DB::rollBack();

            throw $exc;
        }
    }

    /**
     * @param \Seka\Vzdelavanie\Models\Testovanie\Otazka $otazka
     * @param int                                        $pocetOdpovedi
     * @param int                                        $pocetSpravnychOdpovedi
     *
     * @return \Illuminate\Support\Collection
     */
    private static function ziskajOdpovede(Otazka $otazka, int $pocetOdpovedi, int $pocetSpravnychOdpovedi)
    {
        $odpovede = collect();
        $pocetNespravnychOdpovedi = $pocetOdpovedi - $pocetSpravnychOdpovedi;
        $odpovede = $odpovede
            ->concat($otazka->odpovede()->publikovana()->spravna()->inRandomOrder()->limit($pocetSpravnychOdpovedi)->get())
            ->concat($otazka->odpovede()->publikovana()->nespravna()->inRandomOrder()->limit($pocetNespravnychOdpovedi)->get())
            ->shuffle();

        return $odpovede;
    }
}
