<?php

namespace Ignite\Core\Contracts\Entities\Filters;

use Ignite\Core\Entities\User;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Query\Builder as QueryBuilder;

/**
 * This provides the ability to scope a query (Query builder or Eloquent builder query), typically for a user.
 * The reason for not using Eloquent's global scopes are:
 *   - We wanted the ability to apply these scopes not only to Eloquent queries but also to Laravel's DBAL queries.
 *   - We wanted to be able to alter the scope applied based on a permission (e.g. browse participants).
 */
interface QueryFilter
{
    /**
     * Gives you the table this filter is for.
     *
     * @return string
     */
    public function getTable(): string;

    /**
     * Gives you the primary key of the table this filter is for.
     * (we could add support for composite keys in the future, but for now we do not support those).
     *
     * @return string
     */
    public function getKeyName(): string;

    /**
     * Does the given or authed user have the ability to access the given entity?
     *
     * @param Model|int $model The entity that should exist in the scope.
     * @param User|null $user If null, we will use the authed user if any.
     * @return bool
     */
    public function canAccess($model, ?User $user = null): bool;

    /**
     * Apply this scope to a query.
     *
     * @param QueryBuilder|EloquentBuilder $query
     * @param string|null $tableAlias How the model's table is aliased in the given query, if different from the model.
     * @param User|null $user If null, we will use the authed user if any.
     * @return QueryBuilder|EloquentBuilder
     */
    public function apply($query, ?string $tableAlias = null, ?User $user = null);

    /**
     * Assert that this filter is for the given table.
     *
     * @param string|Model $table
     * @throws \UnexpectedValueException When it is not for the same class.
     */
    public function assertForTable($table): void;
}
