<?php

/**
 * Server Side Processing for Bootstrap Table.
 *
 * This class handles search operations and pagination for Bootstrap Table.
 */
class SSPBootstrapTable
{
    private string $searchTerm;
    private array $search;
    private array $fields;
    private array $params;
    private bool $return;
    private string $default_operator;
    private bool $debug;
    private string $es_index;

    /**
     * Constructor.
     *
     * @param string $searchTerm The search term
     * @param array $search Search configuration
     * @param array $fields Fields to search
     * @param array $params Additional parameters
     */
    public function __construct(string $searchTerm, array $search, array $fields, array $params)
    {
        $this->searchTerm = $searchTerm != "" ? $searchTerm : "*";
        $this->search = $search;
        $this->fields = $fields;
        $this->params = $params;
        $this->return = false;
        $this->default_operator = 'AND';
        $this->debug = false;
        $this->es_index = '';
    }

    /**
     * Sets whether to return results or output JSON.
     *
     * @param bool $return Return flag
     * @return static
     */
    public function setReturn(bool $return)
    {
        $this->return = $return;
        return $this;
    }

    /**
     * Sets the default operator for the search.
     *
     * @param string $default_operator Default operator (AND, OR)
     * @return static
     */
    public function setDefaultOperator(string $default_operator)
    {
        $this->default_operator = $default_operator;
        return $this;
    }

    /**
     * Sets debug mode.
     *
     * @param bool $debug Debug flag
     * @return static
     */
    public function setDebug(bool $debug)
    {
        $this->debug = $debug;
        return $this;
    }

    /**
     * Sets the Elasticsearch index.
     *
     * @param string $es_index Index name
     * @return static
     */
    public function setEsIndex(string $es_index)
    {
        $this->es_index = $es_index;
        return $this;
    }

    /**
     * Converts fields to an Elasticsearch query.
     *
     * @param array $fields Fields to convert
     * @param string $searchTerm Search term
     * @return string The query string
     */
    private function convertFields(array $fields, string $searchTerm): string
    {
        $query = "";
        foreach ($fields as $field) {
            if (str_contains($field, 'keyword')) {
                $explode = explode('.', $field);
                $field = $explode[0];
                $keyword = $explode[1];
                $query .= "OR($field.keyword:$searchTerm)OR($field:*$searchTerm)OR($field:$searchTerm*)";
            } else {
                $query .= "OR($field:$searchTerm)OR($field:*$searchTerm)OR($field:$searchTerm*)";
            }
        }

        return substr($query, 2);
    }

    /**
     * Executes the search query.
     *
     * @return null|array The search results
     * @throws \Exception If index is not set
     */
    public function run()
    {
        if (empty($this->es_index)) {
            throw new Exception('Index not set');
        }

        if (!empty($this->fields) && !empty($this->searchTerm)) {
            $this->search[] = "(" . $this->convertFields($this->fields, $this->searchTerm) . ")";
        }

        $query = implode("AND", $this->search);

        $addSort = '';
        if (!empty($this->params["sort"])) {
            $sortField = $this->params["sort"];
            $sortOrder = $this->params["order"] ?? "desc";
            $addSort = "&sort=$sortField:$sortOrder";
        }

        $from = $this->params["from"] ?? 0;
        $size = $this->params["size"] ?? 10;
        $scroll = isset($this->params["scroll"]) ? "scroll=1m" : "";

        $total = 0;
        $rows = [];
        $result = @json_decode(es_execute("/$this->es_index/_search?q=$query&from=$from&size=$size$addSort&default_operator=$this->default_operator$scroll"), true);
        $result_total = @json_decode(es_execute("/$this->es_index/_count?q=$query"), true);
        if (!isset($result['error'])) {
            $total = $result_total['count'];
            $result = $result['hits']['hits'];
            foreach ($result as $hit) {
                $rows[] = array_merge(['index' => $hit['_index'], 'id' => $hit['_id']], $hit['_source']);
            }
        }

        $response = ['total' => $total, 'rows' => $rows];

        if ($this->debug) {
            $response['query'] = "/$this->es_index/_search?q=$query&from=$from&size=$size$addSort&default_operator=$this->default_operator$scroll";
        }

        if ($this->return) {
            return $response;
        }

        die(json_encode($response));
    }
}
