<?php

declare(strict_types=1);

namespace Seka\Portal\Modul\Terminy\Livewire\Backend\Udalosti\Components;

use Carbon\Carbon;
use Exception;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Validate;
use Livewire\Component;
use Seka\Portal\Modul\Terminy\Entities\MiestoKonania\MiestoKonania;
use Seka\Portal\Modul\Terminy\Entities\Udalost\DruhUdalosti;
use Seka\Portal\Modul\Terminy\Entities\Udalost\StavRegistracie;
use Seka\Portal\Modul\Terminy\Entities\Udalost\StavUdalosti;
use Seka\Portal\Modul\Terminy\Entities\Udalost\StavUpozornenia;
use Seka\Portal\Modul\Terminy\Entities\Udalost\Udalost;

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

    #[Locked]
    public ?int $id = null;
    public ?int $miesto_konania_id;
    #[Validate]
    public ?string $nazov;
    #[Validate]
    public ?DruhUdalosti $druh;
    #[Validate]
    public ?StavUdalosti $stav;
    #[Validate]
    public ?StavRegistracie $registracia;
    #[Validate]
    public ?StavUpozornenia $upozornenie;
    #[Validate]
    public ?Carbon $datum_od;
    #[Validate]
    public ?Carbon $datum_do;
    #[Validate]
    public ?int $kapacita;
    #[Validate]
    public ?string $popis;
    #[Validate]
    public bool $povolene_upozornenie;
    public bool $trashed = false;
    public bool $exists = false;

    public function mount(?Udalost $udalost = null): void
    {
        $this->fillForm($udalost);
    }

    /**
     * @param \Seka\Portal\Modul\Terminy\Entities\Udalost\Udalost|null $udalost
     *
     * @return void
     */
    public function fillForm(?Udalost $udalost): void
    {
        if ($udalost) {
            $this->fill(
                $udalost->only(
                    'id',
                    'miesto_konania_id',
                    'nazov',
                    'druh',
                    'stav',
                    'registracia',
                    'upozornenie',
                    'datum_od',
                    'datum_do',
                    'kapacita',
                    'popis',
                ),
            );
            $this->povolene_upozornenie = $this->upozornenie?->equal(StavUpozornenia::POVOLENE) ?? false;
            $this->exists = $udalost->exists;
            $this->trashed = $udalost->trashed();
        } else {
            $this->stav = StavUdalosti::NEZVEREJNENA;
            $this->druh = DruhUdalosti::ZAKLADNE_SKOLENIE;
            $this->registracia = StavRegistracie::NEPOVOLENA;
            $this->miesto_konania_id = MiestoKonania::query()->select('id')->first()->getKey();
            $this->upozornenie = StavUpozornenia::NEPOVOLENE;
            $this->povolene_upozornenie = !$this->upozornenie?->equal(StavUpozornenia::NEPOVOLENE) ?? false;
        }
    }

    public function render(): View
    {
        $miestaKonania = MiestoKonania::all();

        return view('Terminy::livewire.backend.udalosti.components.form', compact('miestaKonania'));
    }

    /**
     * Obnovit zneplatnenu udalost
     *
     * @throws \Throwable
     */
    public function restore(): void
    {
        try {
            $this->authorize('terminy.udalosti.upravit');

            if (!$this->trashed || !$this->exists) {
                return;
            }

            DB::beginTransaction();

            Udalost::withTrashed()->findOrFail($this->id)->restore();

            DB::commit();

            flash()->success('Udalosť bola úspešne obnovená');
        } catch (Exception $e) {
            DB::rollBack();

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

            flash()->error('Udalosť sa nepodarilo obnoviť.');
        } finally {
            $this->fillForm(Udalost::withoutTrashed()->findOrFail($this->id));
        }
    }

    /**
     * Zneplatnit udalost
     *
     * @throws \Throwable
     */
    public function trash(): void
    {
        try {
            $this->authorize('terminy.udalosti.odstranit');

            DB::beginTransaction();

            if (!$this->trashed && $this->exists) {
                // soft delete
                Udalost::query()->findOrFail($this->id)->delete();
            }

            DB::commit();

            flash()->success('Udalosť bola úspešne zneplatnená');
        } catch (Exception $e) {
            DB::rollBack();

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

            flash()->error('Udalosť nebola zneplatnená.');
        } finally {
            $this->fillForm(Udalost::withTrashed()->findOrFail($this->id));
        }
    }

    /**
     * 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 {
            $this->authorize('terminy.udalost.vytvorit');

            // over opravnenia pouzivatela podla existencie udalosti
            if ($this->exists) {
                $this->authorize('terminy.udalosti.upravit');
            } else {
                $this->authorize('terminy.udalosti.vytvorit');
            }

            DB::beginTransaction();

            $udalost = new Udalost();

            if ($this->exists) {
                $udalost = Udalost::withTrashed()->findOrFail($this->id);
            }

            $udalost->fill($this->except('id', 'trashed', 'exists'));
            $udalost->upozornenie = $this->povolene_upozornenie ? StavUpozornenia::POVOLENE : StavUpozornenia::NEPOVOLENE;
            $udalost->vytvoril()->associate(auth()->user());
            $udalost->miestoKonania()->associate(
                MiestoKonania::query()->findOrFail($this->miesto_konania_id),
            );
            $udalost->save();

            DB::commit();

            $message = $this->exists ? 'Udalosť bola aktualizovaná' : 'Nová udalosť bola vytvorená';

            flash()->overlay($message, 'Hlásenie')->success();
        } catch (Exception $e) {
            DB::rollBack();

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

            flash()->overlay('Udalosť nebola úspešne uložená.', 'Hlásenie')->error();
        } finally {
            return redirect()->route('modul.terminy.backend.udalosti.index');
        }
    }

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

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

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