<?php

declare(strict_types=1);

namespace Seka\Portal\Modul\Terminy\Http\Controllers\Backend\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\Backend\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 \App\Models\Pracovisko\Pracovisko
     */
    public Pracovisko $pracovisko;

    /**
     * @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->pracovisko = new Pracovisko();
        $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()
    {
        $this->pracoviska = Pracovisko::query()->get();
        $this->pouzivatelia = Pouzivatel::query()
            ->with('pracovisko')
            ->aktivny()
            ->orderBy('login')
            ->get();

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

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

    /**
     * @return bool
     */
    public function getPouzivatelIsSetProperty(): bool
    {
        return (!empty($this->pouzivatel->hash_id))
            || (!empty($this->registracia->meno)
                && !empty($this->registracia->priezvisko)
                && !empty($this->registracia->pracovisko_id));
    }

    /**
     * @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();
        }

        if ($attribute === 'pracovisko_id') {
            $id = Hasher::decode($value);
            $this->pracovisko = Pracovisko::query()->findOrFail($id);
        }
    }

    /**
     * @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');

            $pek = null;

            DB::beginTransaction();

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

            if ($this->pouzivatel->exists) {
                $this->registracia->pouzivatel()->associate($this->pouzivatel);
                $pek = $this->pouzivatel->pracovisko;
            }

            if ($this->pracovisko->exists) {
                $pek = $this->pracovisko;
            }

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

            $createRegistration::execute($this->udalost, auth()->user(), $pek, $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.backend.udalosti.udalost.registracie.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.pracovisko_id' => 'required_without:pouzivatel.id',
            'registracia.poznamka' => 'max:500',
        ];
    }

    /**
     * @return string[]
     */
    public function messages(): array
    {
        return [
            'pouzivatel.hash_id.required_without_all' => 'Voľba používateľa sa vyžaduje ak nebol zadaný manuálne',
            'registracia.meno.required_without' => 'Meno používateľa sa vyžaduje ak používateľ nebol zvolený zo zoznamu používateľov',
            'registracia.meno.max' => 'Meno používateľa smie obsahovať maximálne :max znakov',
            'registracia.priezvisko.required_without' => 'Priezvisko používateľa sa vyžaduje ak používateľ nebol zvolený zo zoznamu používateľov',
            'registracia.priezvisko.max' => 'Priezvisko používateľa smie obsahovať maximálne :max znakov',
            'registracia.pracovisko_id.required_without' => 'Pracovisko používateľa sa vyžaduje ak používateľ nebol zvolený zo zoznamu používateľov',
            'registracia.poznamka.max' => 'Poznámka smie obsahovať maximálne :max znakov',
        ];
    }

    /**
     * @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();
    }
}
