<?php

declare(strict_types=1);

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

use App\Models\Pouzivatel\Pouzivatel;
use App\Models\Pracovisko\Pracovisko;
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Collection as SupportCollection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
use Livewire\Attributes\Locked;
use Livewire\Component;
use Seka\Portal\Modul\Terminy\Actions\CreateRegistration;
use Seka\Portal\Modul\Terminy\Entities\Registracia\Registracia;
use Seka\Portal\Modul\Terminy\Entities\Registracia\ValueObjects\ZoznamZvolenychMoznostiUdalostiValueObject;
use Seka\Portal\Modul\Terminy\Entities\Udalost\Udalost;
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;

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

    #[Locked]
    public int $udalostId;

    public ?int $pouzivatelId = null;

    public ?string $registraciaMeno = null;

    public ?string $registraciaPriezvisko = null;

    public ?string $registraciaPoznamka = null;

    /**
     * @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->udalostId = $udalost->id;
    }

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

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

        $pracoviska = Pracovisko::query()->select(['id', 'nazov', 'kod'])->get();

        return view(
            'Terminy::livewire.frontend.registracie.components.form',
            compact('udalost', 'pouzivatelia', 'pracoviska'),
        );
    }

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

    public function updated($property): void
    {
        if ($property === 'registraciaMeno' || $property === 'registraciaPriezvisko') {
            if (!empty($this->registraciaMeno) || !empty($this->registraciaPriezvisko)) {
                $this->pouzivatelId = null;
            }
        }

        if ($property === 'pouzivatelId') {
            if (!empty($this->pouzivatelId)) {
                $this->registraciaMeno = null;
                $this->registraciaPriezvisko = null;
            }
        }
    }

    /**
     * @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();
            $udalost = Udalost::query()->findOrFail($this->udalostId);
            $pouzivatel = null;
            $registracia = new Registracia();

            if (!empty($this->pouzivatelId)) {
                $pouzivatel = Pouzivatel::query()->aktivny()->findOrFail($this->pouzivatelId);
            }

            if (!empty($this->registraciaMeno) && !empty($this->registraciaPriezvisko)) {
                $registracia->fill([
                    'meno' => $this->registraciaMeno,
                    'priezvisko' => $this->registraciaPriezvisko,
                ]);
            }

            $registracia->vytvoril()->associate($spravca);

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

            $registracia->poznamka = $this->registraciaPoznamka ?? null;
            $registracia->moznosti = $this->makeListOfMoznostiValueObjects($udalost);

            DB::beginTransaction();
            $createRegistration::execute($udalost, $spravca, $spravca->pracovisko, $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', $udalost);
        }
    }

    /**
     * @param \Seka\Portal\Modul\Terminy\Entities\Udalost\Udalost $udalost
     *
     * @return \Illuminate\Support\Collection
     */
    private function makeListOfMoznostiValueObjects(Udalost $udalost): SupportCollection
    {
        return collect($this->moznosti)->map(function ($moznostId, $zoznamKod) use ($udalost) {
            $zoznam = $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();
    }

    /**
     * @return string[]
     */
    public function rules(): array
    {
        return [
            'pouzivatelId' => 'required_without_all:registraciaMeno,registraciaPriezvisko',
            'registraciaMeno' => 'required_without:pouzivatelId|max:30',
            'registraciaPriezvisko' => 'required_without:pouzivatelId|max:30',
            'registraciaPoznamka' => 'max:500',
        ];
    }
}
