<?php
/**
 * Created by PhpStorm.
 * User: radko
 * Date: 23.7.18
 * Time: 14:54
 */

namespace Seka\Portal\Modul\Vzdelavanie\Repositories\Eloquent;

use Illuminate\Container\Container as App;
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundExceptionEloquent;
use Seka\Portal\Modul\Vzdelavanie\Repositories\Contracts\RepositoryInterface;
use Seka\Portal\Modul\Vzdelavanie\Repositories\Exceptions\ModelNotFoundException;
use Spatie\QueryBuilder\QueryBuilder;

/**
 * Class Repository
 *
 * @package Seka\Portal\Modul\Vzdelavanie\Repositories\Eloquent
 */
abstract class Repository implements RepositoryInterface
{
    /**
     * @var \Illuminate\Database\Eloquent\Builder
     */
    protected $model;
    /**
     * @var \Spatie\QueryBuilder\QueryBuilder
     */
    protected $query;
    /**
     * @var \Illuminate\Container\Container
     */
    private $app;

    /**
     * Repository constructor.
     *
     * @param \Illuminate\Container\Container $app
     */
    public function __construct(App $app)
    {
        $this->app = $app;
    }

    /**
     * @param array $attributes
     *
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|mixed
     * @throws \Seka\Portal\Modul\Vzdelavanie\Repositories\Exceptions\RepositoryException
     */
    public function make(array $attributes): mixed
    {
        return $this->query()->newModelInstance($attributes);
    }

    /**
     * @return \Spatie\QueryBuilder\QueryBuilder
     */
    public function query(): QueryBuilder
    {
        $this->query = QueryBuilder::for($this->model());

        return $this->query;
    }

    /**
     * @return string
     */
    abstract public function model(): string;

    /**
     * @param array $attributes
     *
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|mixed
     * @throws \Seka\Portal\Modul\Vzdelavanie\Repositories\Exceptions\RepositoryException
     */
    public function create(array $attributes): mixed
    {
        return $this->query()->create($attributes);
    }

    /**
     * @param array  $attributes
     * @param int    $id
     * @param string $attribute
     *
     * @return int|mixed
     * @throws \Seka\Portal\Modul\Vzdelavanie\Repositories\Exceptions\RepositoryException
     */
    public function update(array $attributes, int $id, $attribute = 'id'): mixed
    {
        return $this->query()->where($attribute, $id)->update($attributes);
    }

    /**
     * @param string $field
     * @param        $value
     * @param array  $columns
     *
     * @return $this
     */
    public function where(string $field, $value, array $columns = ['*']): static
    {
        $this->query->where($field, $value, $columns);

        return $this;
    }

    /**
     * @param int $id
     *
     * @return bool|mixed|null
     * @throws \Seka\Portal\Modul\Vzdelavanie\Repositories\Exceptions\RepositoryException
     */
    public function delete(int $id): mixed
    {
        return $this->query()->find($id)->delete();
    }

    /**
     * @param int   $id
     * @param array $columns
     *
     * @return mixed
     * @throws \Seka\Portal\Modul\Vzdelavanie\Repositories\Exceptions\ModelNotFoundException
     */
    public function find(int $id, array $columns = ['*']): mixed
    {
        try {
            return $this->query()->find($id, $columns);
        } catch (ModelNotFoundExceptionEloquent) {
            throw new ModelNotFoundException();
        }
    }

    /**
     * @param array $columns
     *
     * @return mixed
     */
    public function all(array $columns = ['*']): mixed
    {
        return $this->query()->get($columns);
    }

    /**
     * @param int   $perPage
     * @param array $columns
     *
     * @return mixed
     */
    public function paginate(int $perPage = 15, array $columns = ['*']): mixed
    {
        return $this->query()->paginate($perPage, $columns, 'strana');
    }

    /**
     * @param string $field
     * @param mixed  $value
     * @param array  $columns
     *
     * @return mixed
     */
    public function findBy(string $field, $value, array $columns = ['*']): mixed
    {
        return $this->query()->where($field, '=', $value)->first($columns);
    }

    /**
     * @param string $fieldName
     * @param string $value
     *
     * @return $this
     */
    public function orderBy(string $fieldName, string $value): static
    {
        $this->query->orderBy($fieldName, $value);

        return $this;
    }

    /**
     * @param int|null $value
     *
     * @return $this
     */
    public function limit(int $value = null): static
    {
        $this->query->limit($value);

        return $this;
    }

//    /**
//     * @return \Illuminate\Database\Eloquent\Model
//     */
//    public function getModel()
//    {
//        return $this->model;
//    }
//
//    /**
//     * @param string $model
//     * @return $this
//     */
//    public function setModel($model)
//    {
//        $this->model = $model;
//
//        return $this;
//    }

    /**
     * @param $relations
     *
     * @return $this
     */
    public function withCount($relations): static
    {
        if (is_string($relations)) {
            $relations = func_get_args();
        }

        $this->query->withCount($relations);

        return $this;
    }

    /**
     * @return $this
     */
    protected function eagerLoadRelations(): static
    {
        if (!is_null($this->query->with)) {
            foreach ($this->query->with as $relation) {
                $this->query->with($relation);
            }
        }

        return $this;
    }

    /**
     * @param array|string $relations
     *
     * @return $this
     */
    public function with($relations): static
    {
        if (is_string($relations)) {
            $relations = func_get_args();
        }

        $this->query->with = $relations;

        return $this;
    }
}
