<?php

/**
 * Realizar una consulta de procesamiento del lado del servidor.
 *
 * @param array $search Los parámetros de búsqueda.
 * @param string $type El tipo de búsqueda.
 * @param array $columns Las columnas en las que buscar.
 * @param string $es_index El índice de Elasticsearch.
 * @param string $advance Una consulta adicional opcional para incluir.
 * @param bool $source_includes Incluir o no el campo _source en la respuesta.
 * @param bool $return Si se devuelve el resultado o se emite como respuesta JSON.
 * @return array|null El resultado de la consulta o null si se ha producido un error.
 * @throws Exception
 */
function ssp(array $search, string $type, array $columns, string $es_index, string $advance = '', bool $source_includes = true, bool $return = false): array
{
    $searchTerm = empty($search['value']) ? '*' : strtolower($search['value']);
    $searchTerm = urlencode(trim($searchTerm));
    //
    $query = convertFields($columns, $searchTerm);
    //
    $column = $_POST['order'][0]['column'] ?? 0;
    $dir = $_POST['order'][0]['dir'] ?? 'asc';
    //
    $sort = "&sort={$columns[$column]}.keyword:$dir";
    $from = $_POST['start'] ?? 0;
    $size = $_POST['length'] ?? 10;
    //
    $fields = implode(',', $columns);
    //
    if ($advance == '') {
        $q = "(slm.type:$type)AND($query)";
    } else {
        $q = "{$advance}AND($query)";
    }
    $q_count = $q;
    //
    if ($source_includes) {
        $q = "$q&from=$from&size=$size$sort&_source_includes=$fields&default_operator=AND";
    } else {
        $q = "$q&from=$from&size=$size$sort&default_operator=AND";
    }
    //
    $result = @json_decode(es_execute("/$es_index/_search?q=$q"), true);
    $result_total = @json_decode(es_execute("/$es_index/_count?q=$q_count"), true);
    //
    $records = [];
    $recordsTotal = 0;
    if (!isset($result['error'])) {
        $result = $result['hits']['hits'];
        $recordsTotal = $result_total['count'];
        foreach ($result as $hit) {
            $records[] = array_merge(['index' => $hit['_index'], 'id' => $hit['_id']], $hit['_source']);
        }
    }
    //
    $data = [
        'draw' => $_POST['draw'] ?? 0,
        'recordsTotal' => $recordsTotal,
        'recordsFiltered' => $recordsTotal,
        'data' => $records,
        'error' => false,
        'query' => "/$es_index/_search?q=$q"
    ];
    //
    if ($return) {
        return $data;
    } else {
        header('Content-Type: application/json;charset=utf-8');
        die(json_encode($data));
    }
}

/**
 * Realiza una consulta de procesamiento del lado del servidor para una tabla DataTable.
 *
 * @param array $search Los parámetros de búsqueda de DataTable.
 * @param array $search_in_columns Los parámetros de búsqueda de cada columna de DataTable.
 * @param string $advance Una consulta adicional opcional para incluir.
 * @param array $columns Las columnas en las que buscar.
 * @param bool $return Si se devuelve el resultado o se emite como respuesta JSON.
 * @return array|null El resultado de la consulta o null si se ha producido un error.
 * @throws Exception
 */
function sspDataTable(array $search, array $search_in_columns, string $advance, array $columns, bool $return = false): ?array
{
    $es_index = getEsIndex();

    $searchTerm = empty($search['value']) ? '*' : strtolower($search['value']);
    $searchTerm = urlencode(trim($searchTerm));

    $sort = "";
    if (isset($_POST['order'])) {
        foreach ($_POST['order'] as $item) {
            $sort .= "&sort={$columns[$item['column']]}:{$item['dir']}";
        }
    }

    $from = $_POST['start'] ?? 0;
    $size = $_POST['length'] ?? 10;

    $search_in_column = "";
    foreach ($search_in_columns as $column) {
        if (!empty($column['search']['value'])) {
            $search_in_column .= "AND({$column['search']['value']})";
        }
    }

    $q = "/$es_index/_search?q={$advance}AND($searchTerm)$search_in_column";

    $q = "$q&from=$from&size=$size$sort&default_operator=AND";

    $result = @json_decode(es_execute($q), true);

    $recordsTotal = 0;

    $records = [];
    if (!isset($result['error'])) {
        if ($result['hits']['total']['value'] != 0) {
            $recordsTotal = $result['hits']['total']['value'];
            $result = $result['hits']['hits'];
            foreach ($result as $hit) {
                $records[] = array_merge(['id' => $hit['_id']], $hit['_source']);
            }
        }
    }

    $result = null;
    $data = [
        'success' => true,
        'draw' => $_POST['draw'] ?? 0,
        'recordsTotal' => $recordsTotal,
        'recordsFiltered' => $recordsTotal,
        'data' => $records,
        'query' => $q,
    ];

    if ($return) {
        return $data;
    } else {
        jsonResponse($data);
    }
}

/**
 * Genera una lista de control de acceso (ACL) basada en los archivos de un directorio.
 *
 * @param string $dir El directorio del que obtener los archivos.
 * @param bool $groupBySubdirectories Indica si se debe agrupar la ACL por subdirectorios.
 * @return array La lista de control de acceso (ACL) generada.
 */
function generate_acl(string $dir, bool $groupBySubdirectories = true): array
{
    // Define las extensiones de archivo válidas
    $validPrefixes = ['proc', 'load', 'view', 'end', 'ajax'];

    // Obtiene todos los archivos en el directorio dado de manera recursiva
    $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));

    $acl = []; // Inicializa el array que almacena la lista de control de acceso (ACL)

    // Itera sobre todos los archivos recogidos
    foreach ($files as $file) {
        // Ignora los directorios, tratamos solo con archivos
        if ($file->isDir()) {
            continue;
        }

        // Divide el nombre del archivo por '.'
        $filenameParts = explode('.', $file->getFilename());

        // Si la extensión del archivo no está en la lista de extensiones válidas, pasa a la siguiente iteración
        if (!in_array($filenameParts[0], $validPrefixes)) {
            continue;
        }

        // Extrae el nombre del archivo como el nombre del módulo
        $moduleName = pathinfo($file->getFileName(), PATHINFO_FILENAME);

        // Componer la ruta del recurso
        $modulePath = str_replace("\\", "/", $file->getPath());
        $resource = str_replace($dir . '/', '', $modulePath) . '.' . $moduleName;

        // Añade el recurso a la lista de ACL si no existe ya
        if (!isset($acl[$resource])) {
            $acl[$resource] = [
                "folder" => $resource
            ];
        }
    }

    // Ordena la ACL alfabéticamente
    ksort($acl);

    if (!$groupBySubdirectories) {
        return $acl;
    }

    // Agrupa la ACL
    $groupedAcl = [];

    // Recorre cada recurso en el ACL
    foreach ($acl as $folderAcl) {
        $resource = explode('.', $folderAcl['folder']);
        //
        $directory = $resource[0];
        $verb = $resource[1];
        $file = $resource[2];
        //
        if (!isset($groupedAcl[$directory])) {
            $groupedAcl[$directory] = [];
        }
        //
        if (!in_array("$directory.$file", array_column($groupedAcl[$directory], 'folder'))) {
            $groupedAcl[$directory][] = array_merge($folderAcl, ["file" => $file, "folder" => "$directory.$file"]);
        }
    }

    // Retorna el ACL agrupado
    return $groupedAcl;
}