<?php

namespace App\Repositories;

use App\Repositories\Contracts\RepositoryInterface;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Builder;
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(): static
    {
        $this->query = $this->model->newQuery();
        return $this;
    }

    /**
     * Get all records
     */
    public function all(): Collection
    {
        $result = $this->query->get();
        $this->resetQuery();
        return $result;
    }

    /**
     * Get all records with specific columns
     */
    public function allWith(array $columns = ['*']): Collection
    {
        $result = $this->query->get($columns);
        $this->resetQuery();
        return $result;
    }

    /**
     * Find a record by ID
     */
    public function find(int $id): ?Model
    {
        $result = $this->query->find($id);
        $this->resetQuery();
        return $result;
    }

    /**
     * Find a record by ID or fail
     */
    public function findOrFail(int $id): Model
    {
        $result = $this->query->findOrFail($id);
        $this->resetQuery();
        return $result;
    }

    /**
     * Find records by a specific field
     */
    public function findBy(string $field, mixed $value): Collection
    {
        $result = $this->query->where($field, $value)->get();
        $this->resetQuery();
        return $result;
    }

    /**
     * Find first record by a specific field
     */
    public function findFirstBy(string $field, mixed $value): ?Model
    {
        $result = $this->query->where($field, $value)->first();
        $this->resetQuery();
        return $result;
    }

    /**
     * Create a new record
     */
    public function create(array $data): Model
    {
        return $this->model->create($data);
    }

    /**
     * Update a record by ID
     */
    public function update(int $id, array $data): Model
    {
        $record = $this->findOrFail($id);
        $record->update($data);
        return $record->fresh();
    }

    /**
     * Delete a record by ID
     */
    public function delete(int $id): bool
    {
        $record = $this->findOrFail($id);
        return $record->delete();
    }

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

    /**
     * Get query builder with eager loaded relations
     */
    public function with(array $relations): static
    {
        $this->query = $this->query->with($relations);
        return $this;
    }

    /**
     * Get records ordered by a field
     */
    public function orderBy(string $column, string $direction = 'asc'): static
    {
        $this->query = $this->query->orderBy($column, $direction);
        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(int $id): bool
    {
        return $this->model->where('id', $id)->exists();
    }

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

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

    /**
     * Add a where between clause for dates
     */
    public function whereBetween(string $column, array $values): static
    {
        $this->query = $this->query->whereBetween($column, $values);
        return $this;
    }

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

    /**
     * Get the model instance
     */
    public function getModel(): Model
    {
        return $this->model;
    }

    /**
     * Get for DataTables
     */
    public function getForDataTable(): Builder
    {
        $query = $this->query;
        $this->resetQuery();
        return $query;
    }
}
