<?php

declare(strict_types=1);

namespace Seka\Portal\Modul\Terminy\Http\Controllers\Frontend\Registracie\Livewire\Components;

use Exception;
use Livewire\Component;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Models\Pouzivatel\Pouzivatel;
use App\Models\Pracovisko\Pracovisko;
use Elceka\Laravel\HashId\Facades\Hasher;
use Illuminate\Database\Eloquent\Collection;
use Seka\Portal\Modul\Terminy\Entities\Registracia\ValueObjects\ZoznamZvolenychMoznostiUdalostiValueObject;
use Seka\Portal\Modul\Terminy\Entities\Udalost\Udalost;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Seka\Portal\Modul\Terminy\Actions\CreateRegistration;
use Seka\Portal\Modul\Terminy\Entities\Udalost\ValueObjects\MoznostUdalostiValueObject;
use Seka\Portal\Modul\Terminy\Entities\Udalost\ValueObjects\ZoznamMoznostiUdalostiValueObject;
use Seka\Portal\Modul\Terminy\Exceptions\TerminyException;
use Seka\Portal\Modul\Terminy\Entities\Registracia\Registracia;

/**
 * Class Form
 *
 * @package Seka\Portal\Modul\Terminy\Http\Controllers\Frontend\Registracie\Livewire\Components
 */
final class Form extends Component
{
    use AuthorizesRequests;

    /**
     * @var \Seka\Portal\Modul\Terminy\Entities\Udalost\Udalost
     */
    public Udalost $udalost;

    /**
     * @var \Illuminate\Database\Eloquent\Collection
     */
    public Collection $pouzivatelia;

    /**
     * @var \Seka\Portal\Modul\Terminy\Entities\Registracia\Registracia
     */
    public Registracia $registracia;

    /**
     * @var \App\Models\Pouzivatel\Pouzivatel
     */
    public Pouzivatel $pouzivatel;

    /**
     * @var \Illuminate\Database\Eloquent\Collection
     */
    public Collection $pracoviska;

    /**
     * @var array
     */
    public array $moznosti = [];

    /**
     * @var bool
     */
    public bool $acceptChoices = false;

    /**
     * @param \Seka\Portal\Modul\Terminy\Entities\Udalost\Udalost $udalost
     */
    public function mount(Udalost $udalost): void
    {
        $this->udalost = $udalost;
        $this->pouzivatel = new Pouzivatel();
        $this->pouzivatelia = new Collection();
        $this->registracia = new Registracia();

        $this->udalost->moznosti->each(function (ZoznamMoznostiUdalostiValueObject $zoznamMoznostiUdalostiValueObject) {
            $this->moznosti[$zoznamMoznostiUdalostiValueObject->getKod()] = null;
        });
    }

    /**
     * @return mixed
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    public function render()
    {
        // zoznam ID pouzivatelov z pracoviska uz registrovanych na udalost
        $registrovanyPouzivateliaId = $this->udalost->registracie()
            ->select('pouzivatel_id')
            ->wherePracoviskoId(auth()->user()->pracovisko_id)
            ->whereNotnull('pouzivatel_id')
            ->pluck('pouzivatel_id');

        // zoznam pouzivatelov z pracoviska este neregistrovanych na udalost
        $this->pouzivatelia = Pouzivatel::query()
            ->with('pracovisko')
            ->aktivny()
            ->wherePracoviskoId(auth()->user()->pracovisko_id)
            ->whereNotIn('id', $registrovanyPouzivateliaId)
            ->orderBy('login')
            ->get();

        return view('Terminy::pages.udalosti.registracie.frontend.livewire.components.form');
    }

    /**
     * @return bool
     */
    public function getChoosedAllOptionsProperty()
    {
        return collect($this->moznosti)->filter(fn($item)=>is_null($item))->isEmpty();
    }

    /**
     * @param $value
     * @param $attribute
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    public function updatedPouzivatel($value, $attribute)
    {
        // ak bol pouzivatel odznaceny, tak premanna pouzivatela sa musi vyprazdnit
        if (empty($value)) {
            $this->pouzivatel = new Pouzivatel();

            return;
        }

        // ak bol zmeneny pouzivatel, tak sa premenna pouzivatela nastavi ziskanym objektom daneho pouzivatela
        // zaroven sa vyprazdni premenna registracie
        if ($attribute === 'hash_id') {
            $id = Hasher::decode($value);
            $this->pouzivatel = Pouzivatel::query()->aktivny()->findOrFail($id);
            $this->registracia = new Registracia();
        }
    }

    /**
     * @param $value
     * @param $attribute
     */
    public function updatedRegistracia($value, $attribute)
    {
        // ak bola zadana hodnota atributu pre registraciu, tak premenna pouzivatela sa musi vyprazdnit
        if (! empty($value)) {
            $this->pouzivatel = new Pouzivatel();
        }
    }

    /**
     * @param \Seka\Portal\Modul\Terminy\Actions\CreateRegistration $createRegistration
     *
     * @return \Illuminate\Http\RedirectResponse|\Livewire\Redirector
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     * @throws \Illuminate\Validation\ValidationException
     * @throws \Throwable
     */
    public function create(CreateRegistration $createRegistration)
    {
        $this->validate();

        try {
            $this->authorize('terminy.registracie.vytvorit');

            $spravca = auth()->user();
            $this->registracia->vytvoril()->associate($spravca);

            // ak bol zvoleny existujuci pouzivatel z vyberaca
            if ($this->pouzivatel->exists) {
                $this->registracia->pouzivatel()->associate($this->pouzivatel);
            }

            $this->registracia->moznosti = $this->makeListOfMoznostiValueObjects();

            DB::beginTransaction();
            $createRegistration::execute($this->udalost, $spravca, $spravca->pracovisko, $this->registracia);
            DB::commit();

            flash()->overlay('Registrácia používateľa na udalosť bola úspešná.', 'Hlásenie')->success();
        } catch (ModelNotFoundException $e) {
            flash()->overlay('Udalosť nebola nájdená.', 'Hlásenie')->warning();
        } catch (TerminyException $e) {
            flash()->overlay($e->getMessage(), 'Hlásenie')->warning();
        } catch (Exception $e) {
            DB::rollBack();
            Log::error($e->getMessage(), $e->getTrace());
            flash()->overlay('Chyba', 'Hlásenie')->error();
        } finally {
            return redirect()->route('modul.terminy.frontend.registracie.pracovisko.index', $this->udalost);
        }
    }

    /**
     * @return string[]
     */
    public function rules(): array
    {
        return [
            'pouzivatel.hash_id' => 'required_without_all:registracia.meno,registracia.priezvisko',
            'registracia.meno' => 'required_without:pouzivatel.id|max:30',
            'registracia.priezvisko' => 'required_without:pouzivatel.id|max:30',
            'registracia.poznamka' => 'max:500',
        ];
    }

    /**
     * @return \Illuminate\Support\Collection
     */
    private function makeListOfMoznostiValueObjects(): \Illuminate\Support\Collection
    {
        return collect($this->moznosti)->map(function ($moznostId, $zoznamKod) {
            $zoznam = $this->udalost->moznosti->filter(
                fn(ZoznamMoznostiUdalostiValueObject $zoznamMoznostiUdalostiValueObject) => $zoznamMoznostiUdalostiValueObject->getKod() === $zoznamKod
            )->first();

            return new ZoznamZvolenychMoznostiUdalostiValueObject(
                $zoznam->getNazov(),
                $zoznam->getKod(),
                collect($zoznam->getMoznosti())->filter(
                    fn(MoznostUdalostiValueObject $moznostUdalostiValueObject) => $moznostUdalostiValueObject->getId() === $moznostId
                )->values()->all()
            );
        })->values();
    }
}
