<?php

namespace Planaday\Database\MySQL;

use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLAggregates\SQLAvg;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLAggregates\SQLCount;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLAggregates\SQLMax;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLAggregates\SQLMin;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLAggregates\SQLSum;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLColumn;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLDelete;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLInsert;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLJoin;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLOperators\SQLAnd;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLOperators\SQLBetween;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLOperators\SQLComparison;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLOperators\SQLExists;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLOperators\SQLIn;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLOperators\SQLLike;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLOperators\SQLNot;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLOperators\SQLOr;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLOperators\SQLSubqueryComparisonModifier;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLQuery;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLTable;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLUpdate;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLUseInterfaces\SQLComparable;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLUseInterfaces\SQLCondition;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLUseInterfaces\SQLFromable;
use Planaday\Database\Abstraction\SQLQuery\QueryBuilding\SQLUseInterfaces\SQLSelectable;
use Planaday\Database\Abstraction\SQLQuery\SQLFactory;
use Planaday\Database\MySQL\QueryBuilding\SQLAggregates\MySQLAvg;
use Planaday\Database\MySQL\QueryBuilding\SQLAggregates\MySQLCount;
use Planaday\Database\MySQL\QueryBuilding\SQLAggregates\MySQLMax;
use Planaday\Database\MySQL\QueryBuilding\SQLAggregates\MySQLMin;
use Planaday\Database\MySQL\QueryBuilding\SQLAggregates\MySQLSum;
use Planaday\Database\MySQL\QueryBuilding\MySQLColumn;
use Planaday\Database\MySQL\QueryBuilding\MySQLDelete;
use Planaday\Database\MySQL\QueryBuilding\MySQLInsert;
use Planaday\Database\MySQL\QueryBuilding\MySQLJoin;
use Planaday\Database\MySQL\QueryBuilding\SQLOperators\MySQLAnd;
use Planaday\Database\MySQL\QueryBuilding\SQLOperators\MySQLBetween;
use Planaday\Database\MySQL\QueryBuilding\SQLOperators\MySQLComparison;
use Planaday\Database\MySQL\QueryBuilding\SQLOperators\MySQLExists;
use Planaday\Database\MySQL\QueryBuilding\SQLOperators\MySQLIn;
use Planaday\Database\MySQL\QueryBuilding\SQLOperators\MySQLLike;
use Planaday\Database\MySQL\QueryBuilding\SQLOperators\MySQLNot;
use Planaday\Database\MySQL\QueryBuilding\SQLOperators\MySQLOr;
use Planaday\Database\MySQL\QueryBuilding\SQLOperators\MySQLSubqueryComparisonModifier;
use Planaday\Database\MySQL\QueryBuilding\MySQLQuery;
use Planaday\Database\MySQL\QueryBuilding\MySQLTable;
use Planaday\Database\MySQL\QueryBuilding\MySQLUpdate;
use Planaday\Platform\PlatformAbstraction\DataHandler;

class MySQLFactory implements SQLFactory {
	public function __construct(
		protected DataHandler $dataHandler
	) {}

	public function query(SQLFromable $from, SQLSelectable $select): SQLQuery {
		return new MySQLQuery($from, $select);
	}

	/**
	 * @param SQLTable $table
	 * @param SQLCondition $where
	 * @param array<string, string|int> $values
	 *
	 * @return SQLUpdate
	 */
	public function update(SQLTable $table, SQLCondition $where, array $values): SQLUpdate {
		return new MySQLUpdate($table, $where, $values);
	}

	/**
	 * @param SQLTable $table
	 * @param mixed[] $values
	 *
	 * @return SQLInsert
	 */
	public function insert(SQLTable $table, array $values): SQLInsert{
		return new MySQLInsert($table, $values);
	}

	public function delete(SQLTable $table, SQLCondition $where): SQLDelete {
		return new MySQLDelete($table, $where);
	}

	public function join(SQLFromable $left, SQLTable $right, SQLColumn $leftColumn, SQLColumn $rightColumn, bool $includeAllLeft = false, bool $includeAllRight = false): SQLJoin {
		return new MySQLJoin($left, $right, $leftColumn, $rightColumn, $includeAllLeft, $includeAllRight);
	}

	public function table(string $table, ?string $alias = null): SQLTable {
		return new MySQLTable($table, $this, $alias);
	}

	public function column(string $columnName, ?string $tableAlias = null): SQLColumn {
		return new MySQLColumn($columnName, $tableAlias);
	}

	public function and(SQLCondition ...$conditions): SQLAnd {
		return new MySQLAnd(...$conditions);
	}

	public function or(SQLCondition ...$conditions): SQLOr {
		return new MySQLOr(...$conditions);
	}

	/**
	 * @param SQLComparable $toBeTested the value to be tested
	 * @param string|int $valueMin
	 * @param string|int $valueMax
	 * @param bool $betweenOrOutside if false will use NOT BETWEEN
	 *
	 * @return SQLBetween
	 */
	public function between(SQLComparable $toBeTested, string|int $valueMin, string|int $valueMax, bool $betweenOrOutside = true): SQLBetween {
		return new MySQLBetween($this->dataHandler, $toBeTested, $valueMin, $valueMax, $betweenOrOutside);
	}

	/**
	 * @param SQLComparable $toBeTested
	 * @param null|string|int|float|SQLSubqueryComparisonModifier $value
	 * @param bool $isEqual
	 * @param bool|null $isGreater
	 *
	 * @return SQLComparison
	 */
	public function compare(SQLComparable $toBeTested, null|string|int|float|SQLSubqueryComparisonModifier $value, bool $isEqual, ?bool $isGreater = null): SQLComparison {
		return new MySQLComparison($this->dataHandler, $toBeTested, $value, $isEqual, $isGreater);
	}

	public function exists(SQLQuery $subQuery): SQLExists {
		return new MySQLExists($subQuery);
	}

	/**
	 * @param SQLComparable $toBeTested the value to be tested
	 * @param array|SQLQuery $values can be an array of values or a subquery
	 * @param bool $inOrOutside if false will use NOT IN
	 */
	public function in(SQLComparable $toBeTested, array|SQLQuery $values, bool $inOrOutside = true): SQLIn {
		return new MySQLIn($this->dataHandler, $toBeTested, $values, $inOrOutside);
	}

	public function like(SQLColumn $toBeTested, string $value): SQLLike {
		return new MySQLLike($this->dataHandler, $toBeTested, $value);
	}

	public function not(SQLCondition $condition):SQLNot {
		return new MySQLNot($condition);
	}

	public function subquery(SQLQuery $subQuery, bool $needsAll): SQLSubqueryComparisonModifier {
		return new MySQLSubqueryComparisonModifier($subQuery, $needsAll);
	}

	public function avg(SQLColumn $column, ?string $alias = null): SQLAvg {
		return new MySQLAvg($column, $alias);
	}

	public function sum(SQLColumn $column, ?string $alias = null): SQLSum {
		return new MySQLSum($column, $alias);
	}

	public function count(SQLColumn $column, ?string $alias = null): SQLCount {
		return new MySQLCount($column, $alias);
	}

	public function max(SQLColumn $column, ?string $alias = null): SQLMax {
		return new MySQLMax($column, $alias);
	}

	public function min(SQLColumn $column, ?string $alias = null): SQLMin {
		return new MySQLMin($column, $alias);
	}
}