<?php

namespace Apexl\Vault\Interfaces;

use Apexl\Vault\Enums\HavingType;
use Apexl\Vault\Enums\SortDirection;
use Apexl\Vault\Enums\WhereCondition;
use Apexl\Vault\Enums\WhereType;
use Closure;

interface QueryBuilderInterface
{
    public function executeRaw(string $sql): array;

    /**
     * The structure tools for this builder's store — describing tables, creating them.
     *
     * A factory rather than inherited methods, because DDL is a different job from
     * building a query and not every store speaks it in the same vocabulary. Reachable
     * from here because this is where callers already are; the schema it hands back
     * works against the same connection().
     */
    public function schema(): SchemaInterface;

    /**
     * The link this builder runs on, and the owner of transactions over it.
     *
     * lastInsertId() and the raw handle moved here: both are properties of the
     * connection, not of the query you happen to be building on it.
     */
    public function connection(): ConnectionInterface;

    /**
     * Run the built query and hand back the rows it produced.
     *
     * Returns a ResultInterface rather than $this: the builder is shared and mutable,
     * so a result that lived on it belonged to whichever query ran last.
     */
    public function execute(): ResultInterface;

    public function isCount(): bool;

    /**
     * @return string[]
     */
    public function getQueryFields(): array;

    public function update(string $table): QueryBuilderInterface;

    public function delete(string $table): QueryBuilderInterface;

    public function insert(string $table): QueryBuilderInterface;

    public function select(string $table): QueryBuilderInterface;

    public function selectDistinct(string $table): QueryBuilderInterface;

    public function fields(?array $fields = null): QueryBuilderInterface;

    public function having(
        string $field,
        $value,
        WhereCondition|string $condition = WhereCondition::EQUALS,
        HavingType $type = HavingType::AND,
    ): QueryBuilderInterface;

    public function orHaving(
        string $field,
        $value,
        WhereCondition|string $condition = WhereCondition::EQUALS,
    ): QueryBuilderInterface;

    public function where(
        string $field,
        $value,
        WhereCondition|string $condition = WhereCondition::EQUALS,
        WhereType|string $type = WhereType::AND,
    ): QueryBuilderInterface;

    public function orWhere(
        string $field,
        $value,
        WhereCondition|string $condition = WhereCondition::EQUALS
    ): QueryBuilderInterface;

    public function whereRaw(string $condition, array $params = [], WhereType|string $type = WhereType::AND): QueryBuilderInterface;

    public function orWhereRaw(string $condition, array $params = []): QueryBuilderInterface;

    public function orderBy(string|array $fieldOrMultipleOrderBys, SortDirection $sort = SortDirection::ASC);

    public function limit(int $limit, int $offset = 0);

    public function join(
        string $table,
        string|array $field1,
        ?string $field2 = null,
        WhereCondition|string $condition = WhereCondition::EQUALS,
    ): QueryBuilderInterface;

    public function leftJoin(
        string $table,
        string|array $field1,
        ?string $field2 = null,
        WhereCondition|string $condition = WhereCondition::EQUALS
    ): QueryBuilderInterface;

    public function newQuery(): QueryBuilderInterface;

    public function reset(): void;

    public function addSubQueryFields(array $fields): void;

    public function resetSubQueryFields(): void;

    public function resetOrderBy(): void;

    public function resetFields(): void;

    public function subQuery(Closure $callback, ?string $alias = null): string;

    public function groupBy(string|array $field): QueryBuilderInterface;

    public function orWhereMultiple(array $groupedClauses, WhereType|string $type = WhereType::AND): QueryBuilderInterface;

    public function endWhereGroup(): QueryBuilderInterface;

    public function startWhereGroup(): QueryBuilderInterface;

    public function executeSql(string $sql, array $params = []): ResultInterface;

    public function toCountQuery(): QueryBuilderInterface;
}
