<?php

declare(strict_types=1);

namespace App\Repositories\Eloquent;

use App\Exceptions\Hodnotenie\HodnotenieNeboloOdstraneneException;
use App\Exceptions\Hodnotenie\HodnotenieNeboloUlozeneException;
use App\Models\Hodnotenie\HodnotenieEnum;
use App\Models\Hodnotenie\HodnotenieModel;
use App\Repositories\HodnotenieRepositoryContract;
use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Auth\User;
use Prettus\Repository\Eloquent\BaseRepository;

/**
 * Class HodnotenieRepository
 *
 * @package App\Repositories\Eloquent;
 */
final class HodnotenieRepository extends BaseRepository implements HodnotenieRepositoryContract
{
    /**
     * @return string
     */
    public function model(): string
    {
        return HodnotenieModel::class;
    }

    /**
     * @param User $pouzivatel
     * @param Model $hodnotenyZaznam
     * @param HodnotenieEnum $hodnotenie
     * @return void
     * @throws HodnotenieNeboloUlozeneException
     * @throws HodnotenieNeboloOdstraneneException
     */
    public function ohodnot(User $pouzivatel, Model $hodnotenyZaznam, HodnotenieEnum $hodnotenie): void
    {
        try {
            $hodnotenieZaznam = HodnotenieModel::whereHasMorph(
                'hodnotitelne',
                get_class($hodnotenyZaznam),
                function ($query) use ($hodnotenyZaznam, $pouzivatel) {
                    $query
                        ->whereHodnotilId($pouzivatel->id)
                        ->whereHodnotitelneId($hodnotenyZaznam->id);
                },
            )->firstOrFail();
        } catch (ModelNotFoundException $e) {
            $hodnotenieZaznam = new HodnotenieModel();
            $hodnotenieZaznam->hodnotil()->associate($pouzivatel);
            $hodnotenieZaznam->hodnotitelne()->associate($hodnotenyZaznam);
        }

        if ($hodnotenieZaznam->exists && $hodnotenieZaznam->hodnotenie->equals($hodnotenie)) {
            try {
                $result = false;
                $result = $hodnotenieZaznam->delete();
            } catch (Exception $e) {
                throw new HodnotenieNeboloOdstraneneException();
            } finally {
                if ($result !== true) {
                    throw new HodnotenieNeboloOdstraneneException();
                }
            }
        } else {
            try {
                $result = false;
                $hodnotenieZaznam->hodnotenie = $hodnotenie;
                $result = $hodnotenieZaznam->save();
            } catch (Exception $e) {
                throw new HodnotenieNeboloUlozeneException();
            } finally {
                if ($result !== true) {
                    throw new HodnotenieNeboloUlozeneException();
                }
            }
        }
    }

    /**
     * @param Model $hodnotenyZaznam
     * @param HodnotenieEnum $hodnotenie
     * @return int
     */
    public function pocetHodnoteniPodlaDruhu(Model $hodnotenyZaznam, HodnotenieEnum $hodnotenie): int
    {
        return $hodnotenyZaznam->pocetHodnoteniPodlaDruhu($hodnotenie) ?? 0;
    }

    /**
     * @param User $pouzivatel
     * @param Model $hodnotenyZaznam
     * @return HodnotenieEnum|null
     */
    public function hodnoteniePouzivatela(User $pouzivatel, Model $hodnotenyZaznam): ?HodnotenieEnum
    {
        return $hodnotenyZaznam->hodnoteniePouzivatela($pouzivatel);
    }

    /**
     * @param Model $hodnotenyZaznam
     * @return int
     */
    public function pocetVsetkychHodnoteni(Model $hodnotenyZaznam): int
    {
        return $hodnotenyZaznam->pocetHodnoteni() ?? 0;
    }
}
