<?php

namespace App\Helpers;

use ClickHouseDB\Client;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Validator;

class ClickhouseHandler
{
    protected $current_statement;
    protected $http_statement;
    protected $http_bindings;
    protected $db;
    protected $database;
    protected $endpoint;


    function __construct($_database = null, $_host = null, $_port = null, $_user = null, $_password = null, $_endpoint = null, $_readonly = true) {
        if($_endpoint === null) {
            $this->db = new Client([
                'host' => $_host,
                'port' => $_port,
                'username' => $_user,
                'password' => $_password,
                'readonly' => $_readonly
            ]);
    
            $this->db->database($_database ?? config('creatia.clickhouse.database'));
            $this->db->setTimeout(300);
            $this->db->setConnectTimeOut(5);
        
            $this->db->ping(true);
            $this->endpoint = null;
        }
        else {
            $this->database = $_database;
            $this->endpoint = $_endpoint;
        }
    }

    public function select($_string, $_from = null, $_to = null, $_bindings = []) {
        if($this->endpoint === null) {
            $this->current_statement = $this->db->select($_string . ($_from !== null && $_to !== null ? " LIMIT $_to OFFSET $_from" : ''), $_bindings);            
        }
        else {
            $this->http_statement = $_string . ($_from !== null && $_to !== null ? " LIMIT $_to OFFSET $_from FORMAT JSON" : 'FORMAT JSON');
            $this->http_bindings = $_bindings;
        }

        return $this;
    }

    public function selectUnpaged($_string, $_bindings = []) {
        if($this->endpoint === null) {
            $this->current_statement = $this->db->select("$_string", $_bindings);
        }
        else {
            $this->http_statement = "$_string FORMAT JSON" ;
            $this->http_bindings = $_bindings;
        }

        return $this;
    }

    public function get($_debug = false, $_allow_ip = false) {

        if($this->endpoint === null) {
            $to_return = $this->current_statement->rawData()['data'];
            
            unset($this->current_statement);

            return $to_return;
        }
        else {
            $final_bindings = [];

            if($this->http_bindings !== []) {
                foreach($this->http_bindings as $i => $bind) {
                    // Realistically this will only allow alphanumerics, BUT IN CASE we also strip slashes and any quotes to absolutely annihilate injection possibility.
                    $final_bindings[urlencode("param_p$i")] = str_replace(['"', "'", '`'], '', stripslashes(preg_replace($_allow_ip ? "/[^a-zA-Z0-9\:\.]+/" : "/[^a-zA-Z0-9]+/", "", $bind)));
                }
            }

            $http = Http::get($this->endpoint, array_merge([
                'database' => $this->database,
                'query' => preg_replace('!\s+!', ' ', str_replace(["\r\n", "\n"], ' ', $this->http_statement))
            ], $final_bindings));

            return $http->json();
        }
    }

    public function insert($_table, $_row_validator, $_rows) {

        // Run validator
        if(!$_row_validator($_rows)) {
            \Log::debug("E");
            return false;
        }

        $this->db->insert($_table, $_rows, array_keys($_rows[0]));
    }

    public function delete($_table, $_key, $_identities) {
        $this->db->write('DELETE FROM {table} WHERE {key} IN (:identities);', [ 'table' => $_table, 'key' => $_key, 'identities' => $_identities ]);
    }
}
?>