<?php

include_once "slm_api.slm";

// Dispatcher - routes AJAX requests to corresponding functions
if (isset($_POST['acc'])) {
    $function = $_POST['acc'];
    if (!function_exists($function)) {
        jsonError('Acción no válida');
    }
    $function();
}

// =============================================================================
// MODULES CRUD
// =============================================================================

function getModules(): void
{
    $response = _response(true, [
        'draw' => getPostInt('draw'),
        'recordsTotal' => 0,
        'recordsFiltered' => 0,
        'data' => []
    ]);
    //
    $fileModules = generate_acl('../slm.framework/modules');
    try {
        $data = [];
        foreach ($fileModules as $directory => $section) {
            foreach ($section as $module) {
                $request = getObjectWithValue('module', 'folder.keyword', $module['folder']);
                if ($request) {
                    $data[] = [
                        'name' => $request['name'],
                        'directory' => $directory,
                        'file' => $request['file'],
                        'folder' => $request['folder'],
                        'registered_module' => true,
                        'timestamp' => $request['slm']['timestamp'],
                        'status' => $request['status'],
                        'id' => $request['id'],
                    ];
                } else {
                    $data[] = [
                        'name' => 'unknown',
                        'directory' => $directory,
                        'file' => $module['file'],
                        'folder' => $module['folder'],
                        'registered_module' => false,
                        'timestamp' => null,
                        'status' => 'unknown',
                        'id' => null,
                    ];
                }
                $response['recordsTotal']++;
                $response['recordsFiltered']++;
            }
        }

        $response['error'] = false;
        $response['data'] = $data;
    } catch (Exception $e) {
        $response = _response(true, $e->getMessage(), [
            'draw' => getPostInt('draw'),
            'recordsTotal' => 0,
            'recordsFiltered' => 0,
            'data' => []
        ]);
    }
    //
    jsonResponse($response);
}

function _buildModuleRecord(string $directory, array $module): array
{
    $registered = getObjectWithValue('module', 'folder.keyword', $module['folder']);

    if ($registered) {
        return [
            'id' => $registered['id'],
            'name' => $registered['name'],
            'directory' => $directory,
            'file' => $registered['file'],
            'folder' => $registered['folder'],
            'status' => $registered['status'],
            'timestamp' => $registered['slm']['timestamp'] ?? null,
            'registered' => true,
        ];
    }

    return [
        'id' => null,
        'name' => 'Sin registrar',
        'directory' => $directory,
        'file' => $module['file'],
        'folder' => $module['folder'],
        'status' => 'unregistered',
        'timestamp' => null,
        'registered' => false,
    ];
}

/**
 * Registers a new module in the system.
 *
 * Required POST fields: name, directory, file, folder
 * Optional POST fields: description
 *
 * @return never Outputs JSON response
 * @throws InvalidArgumentException If required fields are missing
 */
function registerModule(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        // Validate required fields
        validateRequiredFields(['name', 'directory', 'file', 'folder']);

        // Check if module is already registered
        $folder = getPostString('folder');
        $existing = getObjectWithValue('module', 'folder.keyword', $folder);
        if ($existing) {
            throw new Exception("El módulo '{$folder}' ya está registrado");
        }

        // Prepare document data
        $document = _prepareModuleDocument();

        // Create in Elasticsearch
        $result = setNewObject($document);
        $logData['esResult'] = $result;

        // Validate creation result
        _validateEsCreateResult($result);

        // Success response
        $response = _response(false, 'Módulo registrado correctamente', [
            'id' => $result['_id'] ?? null
        ]);
        $logType = 'success';

    } catch (InvalidArgumentException $e) {
        $response = _response(true, $e->getMessage());
    } catch (Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

/**
 * Prepares the module document for Elasticsearch.
 *
 * @return array The prepared document
 */
function _prepareModuleDocument(): array
{
    $document = [
        'name' => getPostString('name'),
        'directory' => getPostString('directory'),
        'file' => getPostString('file'),
        'folder' => getPostString('folder'),
        'status' => 'active',
        'slm' => [
            'type' => 'module',
            'timestamp' => getTimestamp()
        ]
    ];

    // Optional description field
    $description = getPostString('description');
    if (!empty($description)) {
        $document['description'] = $description;
    }

    return $document;
}

/**
 * Validates Elasticsearch create operation result.
 *
 * @param array $result The Elasticsearch response
 * @throws Exception If creation failed
 */
function _validateEsCreateResult(array $result): void
{
    if (isset($result['error'])) {
        throw new Exception('Error al crear el registro en la base de datos');
    }

    if (($result['result'] ?? '') !== 'created') {
        throw new Exception('No se pudo crear el registro');
    }
}

/**
 * Validates Elasticsearch update operation result.
 *
 * @param array $result The Elasticsearch response
 * @throws Exception If update failed
 */
function _validateEsUpdateResult(array $result): void
{
    if (isset($result['error'])) {
        throw new Exception('Error al actualizar el registro en la base de datos');
    }

    if (($result['result'] ?? '') !== 'updated') {
        throw new Exception('No se pudo actualizar el registro');
    }
}

/**
 * Retrieves a single module by its ID.
 *
 * Required POST fields: module_id
 *
 * @return never Outputs JSON response with module data
 */
function getModule(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['module_id']);

        $moduleId = getPostString('module_id');
        $module = getObjectById($moduleId, '_source_excludes=slm.timestamp');

        if (!$module) {
            throw new Exception("Módulo no encontrado: {$moduleId}");
        }

        $response = _response(false, '', ['data' => $module]);
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

/**
 * Updates an existing module.
 *
 * Required POST fields: module_id, name
 * Optional POST fields: description
 *
 * @return never Outputs JSON response
 */
function updateModule(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['module_id', 'name']);

        $moduleId = getPostString('module_id');
        $module = getObjectById($moduleId);

        if (!$module) {
            throw new Exception("Módulo no encontrado: {$moduleId}");
        }

        // Prepare update data
        $updateData = [
            'name' => getPostString('name'),
            'slm' => [
                'type' => 'module',
                'timestamp' => getTimestamp()
            ]
        ];

        // Optional description
        $description = getPostString('description');
        if (!empty($description)) {
            $updateData['description'] = $description;
        }

        $logData['updateData'] = $updateData;

        // Execute update
        $result = updateObjectByID($moduleId, $updateData);
        $logData['esResult'] = $result;

        _validateEsUpdateResult($result);

        $response = _response(false, 'Módulo actualizado correctamente');
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

/**
 * Removes (soft-deletes) a module by setting its status to inactive.
 *
 * Required POST fields: module_id
 *
 * @return never Outputs JSON response
 */
function removeModule(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['module_id']);

        $moduleId = getPostString('module_id');
        $module = getObjectById($moduleId);

        if (!$module) {
            throw new Exception("Módulo no encontrado: {$moduleId}");
        }

        // Soft delete: mark as inactive, keep original type
        $updateData = [
            'status' => 'inactive',
            'slm' => [
                'type' => 'module',
                'timestamp' => getTimestamp()
            ]
        ];

        $logData['updateData'] = $updateData;

        $result = updateObjectByID($moduleId, $updateData);
        $logData['esResult'] = $result;

        _validateEsUpdateResult($result);

        $response = _response(false, 'Módulo eliminado correctamente');
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

// =============================================================================
// DROPDOWN MENUS CRUD
// =============================================================================

/**
 * Retrieves paginated list of dropdown menus for DataTables.
 *
 * @return never Outputs JSON response in DataTables format
 */
function getDropdownMenus(): void
{
    $columns = [
        0 => 'name.keyword',
        1 => 'icon',
        4 => 'timestamp',
        5 => 'status.keyword',
    ];

    $filter = "(slm.type:dropdown_menu)";
    $search = $_POST['search'] ?? ['value' => ''];

    $response = sspDataTable($search, $_POST['columns'] ?? [], $filter, $columns, true);

    jsonResponse($response);
}

/**
 * Creates a new dropdown menu.
 *
 * Required POST fields: name, icon, display, module
 *
 * @return never Outputs JSON response
 */
function registerDropdownMenu(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['name', 'icon', 'display', 'module']);

        $document = _prepareDropdownMenuDocument();

        $result = setNewObject($document);
        $logData['esResult'] = $result;

        _validateEsCreateResult($result);

        $response = _response(false, 'Menú desplegable registrado correctamente', [
            'id' => $result['_id'] ?? null
        ]);
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

/**
 * Retrieves a single dropdown menu by ID.
 *
 * Required POST fields: dropdown_menu_id
 *
 * @return never Outputs JSON response
 */
function getDropdownMenu(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['dropdown_menu_id']);

        $menuId = getPostString('dropdown_menu_id');
        $menu = getObjectById($menuId, '_source_excludes=slm.timestamp');

        if (!$menu) {
            throw new Exception("Menú no encontrado: {$menuId}");
        }

        $response = _response(false, '', ['data' => $menu]);
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

/**
 * Updates an existing dropdown menu.
 *
 * Required POST fields: dropdown_menu_id, name, icon, display, module
 *
 * @return never Outputs JSON response
 */
function updateDropdownMenu(): void
{
    $response = _response(true);
    //
    $logType = 'error';
    $logData = [];
    try {
        validateRequiredFields(['dropdown_menu_id', 'name', 'icon', 'display', 'module']);

        $menuId = getPostString('dropdown_menu_id');
        $menu = getObjectById($menuId);

        if (!$menu) {
            throw new Exception("Menú no encontrado: {$menuId}");
        }

        $updateData = [
            'name' => getPostString('name'),
            'icon' => getPostString('icon'),
            'display' => getPostValue('display'),
            'module' => _processModuleItems(),
            'slm' => [
                'type' => 'dropdown_menu',
                'timestamp' => getTimestamp()
            ]
        ];

        $logData['updateData'] = $updateData;

        $result = updateObjectByID($menuId, $updateData);
        $logData['esResult'] = $result;

        _validateEsUpdateResult($result);

        $response = _response(false, 'Menú desplegable actualizado correctamente');
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

/**
 * Removes (soft-deletes) a dropdown menu.
 *
 * Required POST fields: dropdown_menu_id
 *
 * @return never Outputs JSON response
 */
function removeDropdownMenu(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['dropdown_menu_id']);

        $menuId = getPostString('dropdown_menu_id');
        $menu = getObjectById($menuId);

        if (!$menu) {
            throw new Exception("Menú no encontrado: {$menuId}");
        }

        $updateData = [
            'status' => 'inactive',
            'slm' => [
                'type' => 'dropdown_menu',
                'timestamp' => getTimestamp()
            ]
        ];

        $logData['updateData'] = $updateData;

        $result = updateObjectByID($menuId, $updateData);
        $logData['esResult'] = $result;

        _validateEsUpdateResult($result);

        $response = _response(false, 'Menú desplegable eliminado correctamente');
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

/**
 * Prepares a dropdown menu document for Elasticsearch.
 *
 * @return array The prepared document
 */
function _prepareDropdownMenuDocument(): array
{
    return [
        'name' => getPostString('name'),
        'icon' => getPostString('icon'),
        'display' => getPostValue('display'),
        'module' => json_encode(_processModuleItems()),
        'status' => 'active',
        'slm' => [
            'type' => 'dropdown_menu',
            'timestamp' => getTimestamp()
        ]
    ];
}

/**
 * Processes module items from POST data.
 *
 * @return array Processed module items
 */
function _processModuleItems(): array
{
    $modules = getPostValue('module', []);

    return array_map(function ($item) {
        return [
            'folder' => $item['folder'] ?? '',
            'display' => [(bool)($item['display'] ?? false)],
        ];
    }, $modules);
}

// =============================================================================
// SIMPLE MENUS CRUD
// =============================================================================

/**
 * Retrieves paginated list of simple menus for DataTables.
 *
 * @return never Outputs JSON response in DataTables format
 */
function getSimpleMenus(): void
{
    $columns = [
        0 => 'name.keyword',
        1 => 'icon',
        2 => 'folder.keyword',
        3 => 'display',
        4 => 'timestamp',
        5 => 'status.keyword',
    ];

    $filter = "(slm.type:simple_menu)";
    $search = $_POST['search'] ?? ['value' => ''];

    $response = sspDataTable($search, $_POST['columns'] ?? [], $filter, $columns, true);

    // Enrich data with folder details
    $response['data'] = array_map(function ($row) {
        $folderData = getObjectById($row['folder'] ?? '');
        $row['folder'] = $folderData ?: ['name' => '', 'folder' => ''];
        return $row;
    }, $response['data']);

    jsonResponse($response);
}

/**
 * Creates a new simple menu.
 *
 * Required POST fields: name, icon, folder
 * Optional POST fields: display
 *
 * @return never Outputs JSON response
 */
function registerSimpleMenu(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['name', 'icon', 'folder']);

        $document = [
            'name' => getPostString('name'),
            'icon' => getPostString('icon'),
            'folder' => getPostString('folder'),
            'display' => getPostBool('display', false),
            'status' => 'active',
            'slm' => [
                'type' => 'simple_menu',
                'timestamp' => getTimestamp()
            ]
        ];

        $result = setNewObject($document);
        $logData['esResult'] = $result;

        _validateEsCreateResult($result);

        $response = _response(false, 'Menú simple registrado correctamente', [
            'id' => $result['_id'] ?? null
        ]);
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

/**
 * Retrieves a single simple menu by ID.
 *
 * Required POST fields: simple_menu_id
 *
 * @return never Outputs JSON response
 */
function getSimpleMenu(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['simple_menu_id']);

        $menuId = getPostString('simple_menu_id');
        $menu = getObjectById($menuId, '_source_excludes=slm.timestamp');

        if (!$menu) {
            throw new Exception("Menú no encontrado: {$menuId}");
        }

        $response = _response(false, '', ['data' => $menu]);
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

/**
 * Updates an existing simple menu.
 *
 * Required POST fields: simple_menu_id, name, icon, folder
 * Optional POST fields: display
 *
 * @return never Outputs JSON response
 */
function updateSimpleMenu(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['simple_menu_id', 'name', 'icon', 'folder']);

        $menuId = getPostString('simple_menu_id');
        $menu = getObjectById($menuId);

        if (!$menu) {
            throw new Exception("Menú no encontrado: {$menuId}");
        }

        $updateData = [
            'name' => getPostString('name'),
            'icon' => getPostString('icon'),
            'folder' => getPostString('folder'),
            'display' => getPostBool('display', false),
            'slm' => [
                'type' => 'simple_menu',
                'timestamp' => getTimestamp()
            ]
        ];

        $logData['updateData'] = $updateData;

        $result = updateObjectByID($menuId, $updateData);
        $logData['esResult'] = $result;

        _validateEsUpdateResult($result);

        $response = _response(false, 'Menú simple actualizado correctamente');
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

/**
 * Removes (soft-deletes) a simple menu.
 *
 * Required POST fields: simple_menu_id
 *
 * @return never Outputs JSON response
 */
function removeSimpleMenu(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['simple_menu_id']);

        $menuId = getPostString('simple_menu_id');
        $menu = getObjectById($menuId);

        if (!$menu) {
            throw new Exception("Menú no encontrado: {$menuId}");
        }

        $updateData = [
            'status' => 'inactive',
            'slm' => [
                'type' => 'simple_menu',
                'timestamp' => getTimestamp()
            ]
        ];

        $logData['updateData'] = $updateData;

        $result = updateObjectByID($menuId, $updateData);
        $logData['esResult'] = $result;

        _validateEsUpdateResult($result);

        $response = _response(false, 'Menú simple eliminado correctamente');
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

// =============================================================================
// GROUP MENUS CRUD
// =============================================================================

/**
 * Recupera una lista de grupos y genera una respuesta en formato JSON.
 *
 * Este método utiliza parámetros enviados por POST para filtrar, buscar y ordenar un conjunto de datos sobre grupos.
 * Cada grupo incluirá el conteo de elementos de menú asociados.
 *
 * @return void Devuelve una respuesta JSON con los datos procesados.
 * @throws Exception
 */
function getGroups(): void
{
    $columns = [
        0 => 'title.keyword',
        1 => 'order',
        2 => 'timestamp',
        3 => 'status.keyword',
    ];

    $filter = "(slm.type:group_menu)";
    $search = $_POST['search'] ?? ['value' => ''];

    $response = sspDataTable($search, $_POST['columns'] ?? [], $filter, $columns, true);

    // Add menu count to each row
    $response['data'] = array_map(function ($row) {
        $menuItems = $row['menu'] ?? [];
        $row['menuCount'] = is_array($menuItems) ? count($menuItems) : 0;
        return $row;
    }, $response['data']);

    jsonResponse($response);
}

/**
 * Obtiene el siguiente valor de orden para el menú del grupo.
 *
 * Esta función consulta un índice de Elasticsearch para determinar el valor más alto
 * del campo 'order' dentro de los documentos con el tipo especificado como 'group_menu'.
 * Si no se encuentran documentos relevantes, devuelve 0.
 *
 * @return int El siguiente valor de orden calculado para el menú del grupo.
 * @throws Exception
 */
function _getNextGroupMenuOrder(): int
{
    $esIndex = getEsIndex();
    $query = '(slm.type:group_menu)';

    $result = json_decode(es_execute("/{$esIndex}/_search?q={$query}&sort=order:desc&size=1"), true);

    if (!isset($result['error']) && ($result['hits']['total']['value'] ?? 0) > 0) {
        return (int)($result['hits']['hits'][0]['_source']['order'] ?? 0) + 1;
    }

    return 0;
}

/**
 * Creates a new group menu.
 *
 * Required POST fields: title, menu
 * Optional POST fields: order, display
 *
 * @return never Outputs JSON response
 */
function registerGroupMenu(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['title', 'menu']);

        $order = getPostValue('order', '');
        $document = [
            'title' => getPostString('title'),
            'order' => $order === '' ? _getNextGroupMenuOrder() : (int)$order,
            'display' => getPostBool('display', false),
            'menu' => json_encode(getPostValue('menu', [])),
            'status' => 'active',
            'slm' => [
                'type' => 'group_menu',
                'timestamp' => getTimestamp()
            ]
        ];

        $result = setNewObject($document);
        $logData['esResult'] = $result;

        _validateEsCreateResult($result);

        $response = _response(false, 'Grupo de menú registrado correctamente', [
            'id' => $result['_id'] ?? null
        ]);
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

/**
 * Retrieves a single group menu by ID.
 *
 * Required POST fields: group_menu_id
 *
 * @return never Outputs JSON response
 */
function getGroupMenu(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['group_menu_id']);

        $groupId = getPostString('group_menu_id');
        $group = getObjectById($groupId, '_source_excludes=slm.timestamp');

        if (!$group) {
            throw new Exception("Grupo no encontrado: {$groupId}");
        }

        $response = _response(false, '', ['data' => $group]);
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

/**
 * Updates an existing group menu.
 *
 * Required POST fields: group_menu_id, title, menu
 * Optional POST fields: order, display
 *
 * @return never Outputs JSON response
 */
function updateGroupMenu(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['group_menu_id', 'title', 'menu']);

        $groupId = getPostString('group_menu_id');
        $group = getObjectById($groupId);

        if (!$group) {
            throw new Exception("Grupo no encontrado: {$groupId}");
        }

        $order = getPostValue('order', '');
        $updateData = [
            'title' => getPostString('title'),
            'order' => $order === '' ? _getNextGroupMenuOrder() : (int)$order,
            'display' => getPostBool('display', false),
            'menu' => json_encode(getPostValue('menu', [])),
            'slm' => [
                'type' => 'group_menu',
                'timestamp' => getTimestamp()
            ]
        ];

        $logData['updateData'] = $updateData;

        $result = updateObjectByID($groupId, $updateData);
        $logData['esResult'] = $result;

        _validateEsUpdateResult($result);

        $response = _response(false, 'Grupo de menú actualizado correctamente');
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

/**
 * Removes (soft-deletes) a group menu.
 *
 * Required POST fields: group_menu_id
 *
 * @return never Outputs JSON response
 */
function removeGroupMenu(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['group_menu_id']);

        $groupId = getPostString('group_menu_id');
        $group = getObjectById($groupId);

        if (!$group) {
            throw new Exception("Grupo no encontrado: {$groupId}");
        }

        $updateData = [
            'status' => 'inactive',
            'slm' => [
                'type' => 'group_menu',
                'timestamp' => getTimestamp()
            ]
        ];

        $logData['updateData'] = $updateData;

        $result = updateObjectByID($groupId, $updateData);
        $logData['esResult'] = $result;

        _validateEsUpdateResult($result);

        $response = _response(false, 'Grupo de menú eliminado correctamente');
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage());
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}