<?php

namespace App\Repositories;

use App\Repositories\Interfaces\RepositoryInterface;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Pagination\LengthAwarePaginator;

abstract class BaseRepository implements RepositoryInterface
{
    protected Model $model;
    protected Builder $query;

    public function __construct()
    {
        $this->model = $this->resolveModel();
        $this->resetQuery();
    }

    /**
     * Resolve the model instance.
     */
    abstract protected function resolveModel(): Model;

    /**
     * Reset the query builder.
     */
    protected function resetQuery(): self
    {
        $this->query = $this->model->newQuery();
        return $this;
    }

    /**
     * Get the query builder instance.
     */
    public function getQuery(): Builder
    {
        return $this->query;
    }

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

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

    /**
     * {@inheritdoc}
     */
    public function find(int $id, array $columns = ['*']): ?Model
    {
        $result = $this->query->find($id, $columns);
        $this->resetQuery();
        return $result;
    }

    /**
     * {@inheritdoc}
     */
    public function findOrFail(int $id, array $columns = ['*']): Model
    {
        $result = $this->query->findOrFail($id, $columns);
        $this->resetQuery();
        return $result;
    }

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

    /**
     * {@inheritdoc}
     */
    public function findFirstBy(string $field, mixed $value, array $columns = ['*']): ?Model
    {
        $result = $this->query->where($field, $value)->first($columns);
        $this->resetQuery();
        return $result;
    }

    /**
     * {@inheritdoc}
     */
    public function create(array $data): Model
    {
        return $this->model->create($data);
    }

    /**
     * {@inheritdoc}
     */
    public function update(int $id, array $data): bool
    {
        $record = $this->find($id);
        if (!$record) {
            return false;
        }
        return $record->update($data);
    }

    /**
     * {@inheritdoc}
     */
    public function delete(int $id): bool
    {
        $record = $this->find($id);
        if (!$record) {
            return false;
        }
        return $record->delete();
    }

    /**
     * {@inheritdoc}
     */
    public function with(array $relations): self
    {
        $this->query = $this->query->with($relations);
        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function orderBy(string $column, string $direction = 'asc'): self
    {
        $this->query = $this->query->orderBy($column, $direction);
        return $this;
    }

    /**
     * Add a where clause.
     */
    public function where(string $column, mixed $operator, mixed $value = null): self
    {
        $this->query = $this->query->where($column, $operator, $value);
        return $this;
    }

    /**
     * Add a where in clause.
     */
    public function whereIn(string $column, array $values): self
    {
        $this->query = $this->query->whereIn($column, $values);
        return $this;
    }

    /**
     * Add a where null clause.
     */
    public function whereNull(string $column): self
    {
        $this->query = $this->query->whereNull($column);
        return $this;
    }

    /**
     * Add a where not null clause.
     */
    public function whereNotNull(string $column): self
    {
        $this->query = $this->query->whereNotNull($column);
        return $this;
    }

    /**
     * Add a where date clause.
     */
    public function whereDate(string $column, string $operator, string $value): self
    {
        $this->query = $this->query->whereDate($column, $operator, $value);
        return $this;
    }

    /**
     * Get count of records.
     */
    public function count(): int
    {
        $result = $this->query->count();
        $this->resetQuery();
        return $result;
    }

    /**
     * Check if record exists.
     */
    public function exists(): bool
    {
        $result = $this->query->exists();
        $this->resetQuery();
        return $result;
    }

    /**
     * Get first record.
     */
    public function first(array $columns = ['*']): ?Model
    {
        $result = $this->query->first($columns);
        $this->resetQuery();
        return $result;
    }
}
