<?php

namespace App\Repositories\Eloquent;

use App\Repositories\Contracts\RepositoryInterface;
use App\Services\LoggingService;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;

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

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

    abstract protected function resolveModel(): Model;

    protected function resetQuery(): void
    {
        $this->query = $this->model->newQuery();
    }

    protected function executeWithLogging(string $method, callable $callback): mixed
    {
        $startTime = microtime(true);
        $result = $callback();
        $executionTime = (microtime(true) - $startTime) * 1000;

        $this->loggingService->logRepository(
            static::class . '::' . $method,
            $executionTime
        );

        $this->resetQuery();

        return $result;
    }

    public function all(array $columns = ['*']): Collection
    {
        return $this->executeWithLogging(__FUNCTION__, function () use ($columns) {
            return $this->query->get($columns);
        });
    }

    public function paginate(int $perPage = 15, array $columns = ['*']): LengthAwarePaginator
    {
        return $this->executeWithLogging(__FUNCTION__, function () use ($perPage, $columns) {
            return $this->query->paginate($perPage, $columns);
        });
    }

    public function find(int $id, array $columns = ['*']): ?Model
    {
        return $this->executeWithLogging(__FUNCTION__, function () use ($id, $columns) {
            return $this->query->find($id, $columns);
        });
    }

    public function findOrFail(int $id, array $columns = ['*']): Model
    {
        return $this->executeWithLogging(__FUNCTION__, function () use ($id, $columns) {
            return $this->query->findOrFail($id, $columns);
        });
    }

    public function findBy(string $field, mixed $value, array $columns = ['*']): ?Model
    {
        return $this->executeWithLogging(__FUNCTION__, function () use ($field, $value, $columns) {
            return $this->query->where($field, $value)->first($columns);
        });
    }

    public function findWhere(array $where, array $columns = ['*']): Collection
    {
        return $this->executeWithLogging(__FUNCTION__, function () use ($where, $columns) {
            foreach ($where as $field => $value) {
                if (is_array($value)) {
                    $this->query->whereIn($field, $value);
                } else {
                    $this->query->where($field, $value);
                }
            }
            return $this->query->get($columns);
        });
    }

    public function create(array $data): Model
    {
        return $this->executeWithLogging(__FUNCTION__, function () use ($data) {
            return $this->model->create($data);
        });
    }

    public function update(int $id, array $data): Model
    {
        return $this->executeWithLogging(__FUNCTION__, function () use ($id, $data) {
            $model = $this->model->findOrFail($id);
            $model->update($data);
            return $model->fresh();
        });
    }

    public function delete(int $id): bool
    {
        return $this->executeWithLogging(__FUNCTION__, function () use ($id) {
            $model = $this->model->findOrFail($id);
            return $model->delete();
        });
    }

    public function with(array $relations): self
    {
        $this->query->with($relations);
        return $this;
    }

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

    public function whereIn(string $column, array $values): self
    {
        $this->query->whereIn($column, $values);
        return $this;
    }

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

    public function whereActive(): self
    {
        $this->query->where('is_active', true);
        return $this;
    }

    public function forBranch(int $branchId): self
    {
        if ($this->model->getTable() !== 'branches') {
            $this->query->where('branch_id', $branchId);
        }
        return $this;
    }

    public function count(): int
    {
        return $this->executeWithLogging(__FUNCTION__, function () {
            return $this->query->count();
        });
    }

    public function exists(): bool
    {
        return $this->executeWithLogging(__FUNCTION__, function () {
            return $this->query->exists();
        });
    }
}
