<?php

namespace Fury\Modules\ThinBuilder;

/**
 * trait ThinBuilderProcessing
 * @author  Eugene Sukhodolskiy <eugene.sukhodolskiy@gmail.com>
 * @version 0.1
 * Update at 18.08.2022
 */

trait ThinBuilderProcessing{
	protected $pdo;
	protected $db_config;
	protected $history;
	protected $history_enabled;
	protected $driver;
	protected $gen_sql_only = false;

	public function __construct($db_config, $driver = null, $history_enabled = true){
		$this -> db_config = $db_config;
		$this -> pdo = $this -> create_connect($this -> db_config);
		$this -> driver = $driver;
		$this -> history_enabled = $history_enabled;
		if($this -> history_enabled){
			$this -> create_history_instance();
		}
	}

	protected function create_history_instance(){
		$this -> history = new History();
	}

	public function beginTransaction(): bool {
		return $this -> pdo -> beginTransaction();
	}

	public function commit(): bool {
		return $this -> pdo -> commit();
	}

	public function rollBack(): bool {
		return $this -> pdo -> rollBack();
	}

	public function inTransaction(): bool {
		return $this -> pdo -> inTransaction();
	}

	protected function create_connect($db_conf){
		if(($db_conf['dblib'] ?? '') === 'sqlite'){
			$dblib = "sqlite:" . ($db_conf['dbname'] ?? ':memory:');
			$pdo = new \PDO($dblib);
		} else {
			$dblib = "{$db_conf['dblib']}:host={$db_conf['host']};dbname={$db_conf['dbname']};charset={$db_conf['charset']}";
			$pdo = new \PDO($dblib, $db_conf['user'], $db_conf['password']);
		}
		$pdo -> setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
		return $pdo;
	}

	protected function validate_identifier(String $name): String {
		if(preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $name) !== 1){
			throw new \Exception("Invalid SQL identifier: {$name}");
		}
		return $name;
	}

	protected function select_data_preprocessing($fields, $where, $order_fields, $limit){
		// FIELDS PREPROCESSING
		if(count($fields)){
			foreach ($fields as $f) {
				$this -> validate_identifier($f);
			}
			$fields_sql = '`' . implode('`,`', $fields) . '`';
		}else{
			$fields_sql = '*';
		}

		// ORDER PREPROCESSING
		if(count($order_fields)){
			foreach ($order_fields as $f) {
				$this -> validate_identifier($f);
			}
			$order_sql = 'ORDER BY `' . implode("`,`", $order_fields) . '`';
		}else{
			$order_sql = '';
		}

		// WHERE PREPROCESSING
		list($where_sql, $where_params) = $this -> where_processing($where);

		// LIMIT PREPROCESSING
		if(count($limit)){
			$limit_sql = 'LIMIT ' . implode(',', array_map('intval', $limit));
		}else{
			$limit_sql = '';
		}

		return [$fields_sql, $where_sql, $order_sql, $limit_sql, $where_params];
	}

	protected function normalize_where($where) {
		if(!count($where)){
			return [];
		}

		// Если первый элемент — не массив и не логический оператор,
		// значит where передан как плоский массив одного условия: ['field', '=', 'value']
		if(!is_array($where[0]) && !in_array(strtoupper($where[0]), ['AND', 'OR'])) {
			if(count($where) === 2) {
				if(is_array($where[1])) {
					return [ [$where[0], 'IN', $where[1]] ];
				}
				return [ [$where[0], '=', $where[1]] ];
			}
			return [ [$where[0], $where[1], $where[2]] ];
		}

		return $where;
	}

	protected function where_processing($where){
		$where = $this -> normalize_where($where);

		if(!count($where)){
			return ['', []];
		}

		$sql_parts = [];
		$params = [];
		foreach ($where as $i => $w_item) {
			if(is_array($w_item)){
				if(count($w_item) === 2){
					if(is_array($w_item[1])) {
						$w_item = [$w_item[0], 'IN', $w_item[1]];
					} else {
						$w_item = [$w_item[0], '=', $w_item[1]];
					}
				}

				$field = "`" . $this -> validate_identifier($w_item[0]) . "`";
				$operator = strtoupper($w_item[1]);
				$allowed_operators = ['=', '!=', '<>', '<', '>', '<=', '>=', 'LIKE', 'IN', 'IS', 'NOT IN', 'BETWEEN'];
				if(!in_array($operator, $allowed_operators, true)){
					throw new \Exception("Invalid WHERE operator: {$operator}");
				}
				if($operator == 'IN'){
					if(!is_array($w_item[2])){
						throw new \Exception("IN operator requires array value");
					}
					$placeholders = array_fill(0, count($w_item[2]), '?');
					$sql_parts[] = "{$field} IN (" . implode(',', $placeholders) . ")";
					$params = array_merge($params, $w_item[2]);
				} else {
					$sql_parts[] = "{$field} {$operator} ?";
					$params[] = $w_item[2];
				}
			} else {
				$sql_parts[] = $w_item;
			}
		}

		$sql = 'WHERE ' . implode(' ', $sql_parts);
		return [$sql, $params];
	}
}
