<?php

namespace Planaday\Database\SQLDatabaseControllers\Location;

use ArrayAccess;
use Countable;
use Planaday\Database\Abstraction\SQLQuery\ColumnTypeMap;
use Planaday\Database\Abstraction\SQLQuery\SQLFactory;
use Planaday\Database\SQLDatabaseControllers\Table;
use Planaday\Entities\Location\DTO\Location;
use Planaday\Platform\PlatformAbstraction\DatabaseInterface;
use Planaday\Utility\DataGrouping\Collection;
use Traversable;

class LocationTable extends Table {
	public const LOCATION_TABLE = 'locations';

	public function __construct( DatabaseInterface $querySender, SQLFactory $sql, ColumnTypeMap $columnTypeMap ) {
		$columns = [];

		$columns[LocationColumnEnum::ID] = implode(" ", [
			$columnTypeMap->STRING_ID(),
			$columnTypeMap->NOT_NULL(),
			$columnTypeMap->PRIMARY_KEY()
		]);

		$columns[LocationColumnEnum::NAME] = implode(" ", [
			$columnTypeMap->NAME(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::VAT_CODE] =
		$columns[LocationColumnEnum::CODE] = implode(" ", [
			$columnTypeMap->CODE(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::STREET_1] =
		$columns[LocationColumnEnum::STREET_2] = implode(" ", [
			$columnTypeMap->STREET(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::HOUSE_NUMBER] = implode(" ", [
			$columnTypeMap->HOUSE_NUMBER(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::HOUSE_NUMBER_EXTENSION] = implode(" ", [
			$columnTypeMap->HOUSE_NUMBER_EXTENSION(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::ZIPCODE] = implode(" ", [
			$columnTypeMap->ZIPCODE(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::CITY] = implode(" ", [
			$columnTypeMap->CITY(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::COUNTRY] = implode(" ", [
			$columnTypeMap->COUNTRY(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::PHONE_NUMBER] = implode(" ", [
			$columnTypeMap->PHONE_NUMBER(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::EMAIL] = implode(" ", [
			$columnTypeMap->EMAIL(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::WEBSITE] = implode(" ", [
			$columnTypeMap->WEBSITE(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::LNG] =
		$columns[LocationColumnEnum::LAT] = implode(" ", [
			$columnTypeMap->COORDINATES(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::DESCRIPTION] = implode(" ", [
			$columnTypeMap->DESCRIPTION(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::CAPACITY] = implode(" ", [
			$columnTypeMap->AVAILABILITY_COUNT(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::IS_VIRTUAL]        =
		$columns[LocationColumnEnum::IS_EXTERNAL]       =
		$columns[LocationColumnEnum::CAN_BE_OVERBOOKED] = implode(" ", [
			$columnTypeMap->BOOLEAN(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::COST] = implode(" ", [
			$columnTypeMap->PRICE(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::VAT] = implode(" ", [
			$columnTypeMap->TAX(),
			$columnTypeMap->DEFAULT_NULL()
		]);

		$columns[LocationColumnEnum::LAST_UPDATE] = $columnTypeMap->LAST_UPDATED_TIMESTAMP();

		parent::__construct( $querySender, $sql, self::LOCATION_TABLE, $columns, [] );
	}

	public function setLocation( Location $location ): void {
		$databaseArray = LocationDatabaseObject::getDatabaseArrayFromLocation($location);
		$table = $this->getSqlTable();
		$query = $this->sql->insert($table, $databaseArray);
		$this->querySender->runPreparedQuery($query->toQuery(true), $databaseArray);
	}

	public function getLocationById( string $id ): ?Location {
		$table = $this->getSqlTable();
		$query = $this->sql->query($table, $table);
		$query->setWhere($this->sql->compare($table->getColumn(LocationColumnEnum::ID), $id, true));
		$query->setLimit(1);

		$result = $this->querySender->sendQuery($query->toQuery());

		if (!is_array($result) || count($result) == 0){
			return null;
		}

		return LocationDatabaseObject::getLocationObject($result[0]);
	}

	/**
	 * @param ArrayAccess<int|string, Location>&Traversable<int|string, Location>&Countable $locations
	 * @param bool $wipeDB
	 *
	 * @return void
	 */
	public function setAll( ArrayAccess&Traversable&Countable $locations, bool $wipeDB = false ): void {
		if ($wipeDB){
			$sql = "TRUNCATE TABLE " . $this->getSqlTable()->getFromQuery();
			$this->querySender->sendQuery($sql);
		}

		foreach ($locations as $location){
			$this->setLocation($location);
		}
	}

	/**
	 * @return ArrayAccess<int|string, Location>&Traversable<int|string, Location>&Countable
	 */
	public function getAll(): ArrayAccess&Traversable&Countable {
		$result = $this->getAllRaw();

		$returnArray = [];

		foreach ($result as $item){
			$returnArray[] = LocationDatabaseObject::getLocationObject($item);
		}

		return Collection::fromArray($returnArray);
	}

	public function getAllRaw():ArrayAccess&Traversable&Countable{
		$query = "SELECT * FROM {pad-table}";

		$results = $this->querySender->getQueryResults($this, $query);

		if (!is_array($results) || count($results) == 0){
			return new Collection();
		}

		return Collection::fromArray($results);
	}

	public function getCountAll(): int {
		$table = $this->getSqlTable();
		$count = $this->sql->count($table->getColumn(LocationColumnEnum::ID));
		$count->setAlias('Count');
		$query = $this->sql->query( $table, $count );

		$result = $this->querySender->sendQuery($query->toQuery());

		if (!is_array($result) || count($result) == 0){
			return 0;
		}

		return $result[0]['Count'] ?? 0;
	}

	/**
	 * @return Collection<string>
	 */
	public function getAllCities(): Collection {
		$table = $this->getSqlTable();
		$query = $this->sql->query($table, $table->getColumn(LocationColumnEnum::CITY));
		$query->isDistinct();

		$result = $this->querySender->sendQuery($query->toQuery());

		$cities = new Collection();
		foreach ($result as $item){
			$cities->append($item[LocationColumnEnum::CITY]);
		}

		return $cities;
	}
}