<?php

declare(strict_types=1);

namespace Seka\Portal\Modul\Terminy\Entities\Udalost;

use Acaronlex\LaravelCalendar\Event;
use Carbon\Carbon;
use DateTime;
use Elceka\Laravel\HashId\Http\Traits\Hashidable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
use Seka\Portal\Modul\Terminy\Entities\MiestoKonania\MiestoKonania;
use Seka\Portal\Modul\Terminy\Entities\Registracia\Registracia;
use Seka\Portal\Modul\Terminy\Entities\Udalost\Casts\MoznostiUdalostiCastable;

/**
 * Class Udalost
 *
 * @property int                                                             $id
 * @property int                                                             $vytvoril_id
 * @property int                                                             $miesto_konania_id
 * @property string                                                          $nazov
 * @property \Seka\Portal\Modul\Terminy\Entities\Udalost\DruhUdalosti        $druh
 * @property \Seka\Portal\Modul\Terminy\Entities\Udalost\StavUdalosti        $stav
 * @property \Seka\Portal\Modul\Terminy\Entities\Udalost\StavRegistracie     $registracia
 * @property \Seka\Portal\Modul\Terminy\Entities\Udalost\StavUpozornenia     $upozornenie
 * @property \Seka\Portal\Modul\Terminy\Entities\MiestoKonania\MiestoKonania $miestoKonania
 * @property \Illuminate\Support\Collection                                  $moznosti
 * @property \Carbon\Carbon                                                  $datum_od
 * @property \Carbon\Carbon                                                  $datum_do
 * @property int                                                             $kapacita
 * @property string                                                          $popis
 * @property \Carbon\Carbon                                                  $created_at
 * @property \Carbon\Carbon                                                  $updated_at
 * @property \Carbon\Carbon                                                  $deleted_at
 * @property bool                                                            $ma_volnu_kapacitu
 * @property bool                                                            $nema_volnu_kapacitu
 * @property int                                                             $pocet_volnych_miest
 * @property int                                                             $pocet_obsadenych_miest
 * @property bool                                                            $skoncila
 * @property bool                                                            $prebieha
 * @property bool                                                            $nezacala
 * @property int                                                             $trvanie_udalosti
 * @property bool                                                            $je_dostupna_registracia
 *
 * @package Seka\Portal\Modul\Terminy\Entities\Udalost
 */
final class Udalost extends Model implements Event
{
    use Hashidable;
    use SoftDeletes;
    use HasFactory;

    /**
     * @var string
     */
    protected $table = 'terminy_udalost_udalosti';

    /**
     * @var string[]
     */
    protected $with = [
        'miestoKonania',
    ];

    /**
     * @var array
     */
    protected $guarded = [];

    /**
     * @var string[]
     */
    protected $appends = [
        'ma_volnu_kapacitu',
        'nema_volnu_kapacitu',
        'pocet_volnych_miest',
        'skoncila',
        'prebieha',
        'nezacala',
        'povolena_registracia',
        'je_dostupna_registracia',
        'povolene_upozornenie',
    ];

    /**
     * @var string[]
     */
    protected $casts = [
        'kapacita' => 'integer',
        'datum_od' => 'datetime:Y-m-d\TH:i',
        'datum_do' => 'datetime:Y-m-d\TH:i',
        'druh' => DruhUdalosti::class,
        'stav' => StavUdalosti::class,
        'registracia' => StavRegistracie::class,
        'upozornenie' => StavUpozornenia::class,
        'moznosti' => MoznostiUdalostiCastable::class,
    ];

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function vytvoril(): BelongsTo
    {
        return $this->belongsTo('\App\Models\Pouzivatel\Pouzivatel', 'vytvoril_id', 'id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function miestoKonania(): BelongsTo
    {
        return $this->belongsTo(MiestoKonania::class, 'miesto_konania_id', 'id')->withTrashed();
    }

    /**
     * @return bool
     */
    public function getNemaVolnuKapacituAttribute(): bool
    {
        return !$this->getMaVolnuKapacituAttribute();
    }

    /**
     * @return bool
     */
    public function getMaVolnuKapacituAttribute(): bool
    {
        return $this->kapacita > $this->registracie()->count();
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function registracie(): HasMany
    {
        return $this->hasMany(Registracia::class, 'udalost_id', 'id');
    }

    /**
     * @return int
     */
    public function getPocetVolnychMiestAttribute(): int
    {
        return $this->kapacita - $this->registracie()->count();
    }

    /**
     * @return int
     */
    public function getPocetObsadenychMiestAttribute(): int
    {
        return $this->registracie()->count();
    }

    /**
     * @return bool
     */
    public function getSkoncilaAttribute(): bool
    {
        if (is_null($this->getAttribute('datum_od'))) {
            return false;
        }

        return Carbon::now()->greaterThan($this->getAttribute('datum_do'));
    }

    /**
     * @return bool
     */
    public function getPrebiehaAttribute(): bool
    {
        if (is_null($this->getAttribute('datum_od'))) {
            return false;
        }

        return Carbon::now()->betweenIncluded($this->getAttribute('datum_od'), $this->getAttribute('datum_do'));
    }

    /**
     * @return bool
     */
    public function getNezacalaAttribute(): bool
    {
        if (is_null($this->getAttribute('datum_od'))) {
            return false;
        }

        return Carbon::now()->lessThan($this->getAttribute('datum_od'));
    }

    /**
     * @return bool
     */
    public function getPovolenaRegistraciaAttribute(): bool
    {
        return $this->jePovolenaRegistracia();
    }

    /**
     * @return bool
     */
    public function jePovolenaRegistracia(): bool
    {
        return StavRegistracie::POVOLENA->equal($this->getAttribute('registracia'));
    }

    /**
     * @return bool
     */
    public function getPovoleneUpozornenieAttribute(): bool
    {
        return $this->upozornenie->equal(StavUpozornenia::POVOLENE);
    }

    /**
     * @return bool
     * @throws \ReflectionException
     */
    public function getJeDostupnaRegistraciaAttribute(): bool
    {
        return StavRegistracie::POVOLENA->equal(
                $this->getAttribute('registracia'),
            ) && !$this->skoncila && !$this->prebieha;
    }

    /**
     * Trvanie udalosti v dnoch
     *
     * @return int
     */
    public function getTrvanieUdalostiAttribute(): int
    {
        return $this->datum_do->diffInDays($this->datum_od);
    }

    /**
     * Odstránenie prázdnych znakov zo začiatku a z konca názvu udalosti
     *
     * @param string|null $nazov
     */
    public function setNazovAttribute(?string $nazov)
    {
        $this->attributes['nazov'] = trim($nazov ?? '');
    }

    /**
     * @param bool|null $povolene
     *
     * @return void
     */
    public function setPovoleneUpozornenieAttribute(?bool $povolene): void
    {
        $this->attributes['upozornenie'] = $povolene ? StavUpozornenia::POVOLENE : StavUpozornenia::NEPOVOLENE;
    }

    /**
     * @return bool
     */
    public function nieJePovoleneUpozornenie(): bool
    {
        return StavUpozornenia::NEPOVOLENE->equal($this->getAttribute('upozornenie'));
    }

    /**
     * @return bool
     */
    public function jeZverejnena(): bool
    {
        return $this->stav->equal(StavUdalosti::ZVEREJNENA);
    }

    /**
     * @return bool
     */
    public function nieJeZverejnena(): bool
    {
        return $this->stav->equal(StavUdalosti::NEZVEREJNENA);
    }

    /**
     * @param \Illuminate\Database\Eloquent\Builder $query
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeZverejnena(Builder $query): Builder
    {
        return $query->where('stav', StavUdalosti::ZVEREJNENA);
    }

    /**
     * @return string
     */
    public function getTitle(): string
    {
        return Str::ucfirst($this->druh->label());
    }

    /**
     * @return false
     */
    public function isAllDay(): bool
    {
        return (bool)$this->datum_od->diffInHours($this->datum_do) > 24;
    }

    /**
     * @return \DateTime
     */
    public function getStart(): DateTime
    {
        return $this->datum_od;
    }

    /**
     * @return \DateTime
     */
    public function getEnd(): DateTime
    {
        return $this->datum_do;
    }

    /**
     * @return bool
     */
    public function maVytvoreneMoznosti(): bool
    {
        return $this->moznosti->isNotEmpty();
    }

    /**
     * @return array
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    public function getEventOptions(): array
    {
        return [
            'url' => route('modul.terminy.frontend.udalosti.udalost.show', $this->hashId()),
            'color' => $this->druh->color(),
            'description' => $this->nazov,
            'displayEventTime' => (bool)$this->datum_od->diffInHours($this->datum_do) <= 24,
        ];
    }
}
