<?php

declare(strict_types=1);

namespace Seka\Terminy\Http\Controllers\Backend\Udalosti\Livewire\Components;

use Exception;
use Livewire\Component;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\DB;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\Log;
use Seka\Terminy\Models\Udalost\Udalost;
use Illuminate\Database\Eloquent\Collection;
use Seka\Terminy\Models\Udalost\StavUdalosti;
use Seka\Terminy\Models\Udalost\DruhUdalosti;
use Seka\Terminy\Models\Udalost\StavRegistracie;
use Seka\Terminy\Events\Udalost\UdalostBolaObnovena;
use Seka\Terminy\Models\MiestoKonania\MiestoKonania;
use Seka\Terminy\Events\Udalost\UdalostBolaZrusena;
use Seka\Terminy\Events\Udalost\UdalostBolaAktualizovana;
use Seka\Terminy\Events\Udalost\BolaVytvorenaNovaUdalost;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

/**
 * Class Form
 *
 * @package Seka\Terminy\Http\Controllers\Backend\Udalosti\Livewire\Components
 */
final class Form extends Component
{
    use AuthorizesRequests;

    /**
     * @var \Seka\Terminy\Models\Udalost\Udalost
     */
    public Udalost $udalost;

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

    /**
     * @param \Seka\Terminy\Models\Udalost\Udalost $udalost
     */
    public function mount(Udalost $udalost): void
    {
        $this->udalost = $udalost;
        $this->miestaKonania = MiestoKonania::all();
    }

    /**
     * @return \Illuminate\Contracts\View\View
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    public function render(): View
    {
        return view('Terminy::pages.udalosti.backend.livewire.components.form');
    }

    /**
     * Obnovit zneplatnenu udalost
     *
     * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse|void
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    public function restore()
    {
        try {
            $this->authorize('terminy.udalosti.upravit');

            if (!$this->udalost->trashed()) return;

            DB::beginTransaction();

            $this->udalost->restore();

            DB::commit();

            $this->udalost->refresh();

            if ($this->udalost->stav->equals(StavUdalosti::ZVEREJNENA())) {
                event(new UdalostBolaObnovena($this->udalost));
            }

            flash()->overlay('Udalosť bola úspešne obnovená')->success();

            return redirect()->route('modul.terminy.backend.udalosti.index');
        } catch (Exception $e) {
            DB::rollBack();

            Log::error($e->getMessage(), $e->getTrace());

            flash()->overlay('Udalosť sa nepodarilo obnoviť.')->error();
        }
    }

    /**
     * Zneplatnit udalost
     *
     * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    public function trash()
    {
        try {
            $this->authorize('terminy.udalosti.odstranit');

            DB::beginTransaction();

            $udalostTemp = clone $this->udalost;

            // soft delete
            $this->udalost->delete();

            DB::commit();

            if ($udalostTemp->stav->equals(StavUdalosti::ZVEREJNENA())) {
                event(new UdalostBolaZrusena($udalostTemp));
            }

            flash()->overlay('Udalosť bola úspešne zneplatnená')->success();

            return redirect()->route('modul.terminy.backend.udalosti.index');
        } catch (Exception $e) {
            DB::rollBack();

            Log::error($e->getMessage(), $e->getTrace());

            flash()->overlay('Udalosť nebola zneplatnená.')->error();
        }
    }

    /**
     * Vytvorit novu alebo aktualizovat existujucu udalost
     *
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     * @throws \Throwable
     */
    public function save()
    {
        $this->validate();

        try {
            $existBeforeSave = $this->udalost->exists ?? false;

            // over opravnenia pouzivatela podla existencie udalosti
            if ($existBeforeSave) {
                $udalostPovodna = clone $this->udalost->fresh();
                $this->authorize('terminy.udalosti.upravit');
            } else {
                $this->authorize('terminy.udalosti.vytvorit');
            }

            DB::beginTransaction();

            $this->udalost->vytvoril()->associate(auth()->user());
            $this->udalost->miestoKonania()->associate(MiestoKonania::query()->findOrFail($this->udalost->miesto_konania_id));
            $this->udalost->save();
            $this->udalost->refresh();

            DB::commit();

            if (false == $existBeforeSave) {
                $message = 'Nová udalosť bola vytvorená';

                if ($this->udalost->stav->equals(StavUdalosti::ZVEREJNENA())) {
                    event(new BolaVytvorenaNovaUdalost($this->udalost));
                }
            } else {
                $message = 'Udalosť bola aktualizovaná';

                if ($udalostPovodna->stav->equals(StavUdalosti::NEZVEREJNENA()) && $this->udalost->stav->equals(StavUdalosti::ZVEREJNENA())) {
                    event(new BolaVytvorenaNovaUdalost($this->udalost));
                } else if ($udalostPovodna->stav->equals(StavUdalosti::ZVEREJNENA()) && $this->udalost->stav->equals(StavUdalosti::NEZVEREJNENA())) {
                    event(new UdalostBolaZrusena($this->udalost));
                } else {
                    event(new UdalostBolaAktualizovana($this->udalost, $udalostPovodna));
                }
            }

            flash()->overlay($message, 'Hlásenie')->success();

            return redirect()->route('modul.terminy.backend.udalosti.index');
        } catch (Exception $e) {
            DB::rollBack();

            Log::error($e->getMessage(), $e->getTrace());

            flash()->overlay('Udalosť nebola úspešne uložená.')->error();
        }
    }

    /**
     * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    public function cancel()
    {
        return redirect()->route('modul.terminy.backend.udalosti.index');
    }

    /**
     * Ak je udalost zneplatnena, tak formular je v rezime zneplatnenia
     *
     * @return bool
     */
    public function trashMode(): bool
    {
        return $this->udalost->trashed();
    }

    /**
     * Ak udalost existuje, tak formular je v rezime uprava
     *
     * @return bool
     */
    public function editMode(): bool
    {
        return $this->udalost->exists;
    }

    /**
     * @return array
     */
    public function rules(): array
    {
        return [
            'udalost.miesto_konania_id' => 'required|exists:terminy_udalost_miesta_konania,id',
            'udalost.nazov' => 'required|max:255',
            'udalost.datum_od' => 'required|date',
            'udalost.datum_do' => 'required|date|after_or_equal:udalost.datum_od',
            'udalost.kapacita' => 'required|numeric|min:1',
            'udalost.stav' => [
                'required',
                Rule::in(StavUdalosti::toValues()),
            ],
            'udalost.druh' => [
                'required',
                Rule::in(DruhUdalosti::toValues()),
            ],
            'udalost.registracia' => [
                'required',
                Rule::in(StavRegistracie::toValues()),
            ],
            'udalost.popis' => 'max:16384',
        ];
    }

    /**
     * @return string[]
     */
    public function messages(): array
    {
        return [
            'udalost.nazov.required' => 'Názov udalosti je povinný',
            'udalost.nazov.max' => 'Maximálny počet znakov názvu udalosti je :max',
            'udalost.datum_od.required' => 'Začiatok udalosti je povinný',
            'udalost.datum_od.date' => 'Začiatok udalosti musí byť dátum',
            'udalost.datum_do.required' => 'Ukončenie udalosti je povinné',
            'udalost.datum_do.date' => 'Ukončenie udalosti musí byť dátum',
            'udalost.datum_do.after_or_equal' => 'Ukončenie udalosti musí byť po jej začiatku',
            'udalost.miesto_konania_id.required' => 'Miesto konania sa vyžaduje',
            'udalost.miesto_konania_id.exists' => 'Miesto konania musí byť vytvorené',
            'udalost.kapacita.required' => 'Kapacita je vyžadovaná',
            'udalost.kapacita.numeric' => 'Kapacita musí byť číselnou hodnotou',
            'udalost.kapacita.min' => 'Minimálna hodnota kapacity je :min',
            'udalost.stav.required' => 'Stav udalosti je vyžadovaný',
            'udalost.stav.in' => 'Neznámi stav udalosti',
            'udalost.druh.required' => 'Druh udalosti je vyžadovaný',
            'udalost.druh.in' => 'Neznámi druh udalosti',
            'udalost.registracia.required' => 'Nastavenie stavu registrácie je vyžadované',
            'udalost.registracia.in' => 'Neznámi druh stavu registrácie',
            'udalost.popis.max' => 'Maximálny počet znakov popisu udalosti je :max',
        ];
    }
}
