<?php

// 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();
}

// =============================================================================
// PROFILES CRUD
// =============================================================================

/**
 * Recupera una lista de perfiles activos desde el sistema.
 *
 * Este método utiliza los parámetros de búsqueda enviados a través del POST
 * para filtrar los resultados y devolver los perfiles activos según criterios
 * específicos definidos en el filtro y las columnas deseadas.
 *
 * @return void Devuelve una respuesta en formato JSON con una lista de perfiles.
 * @throws Exception
 */
function getProfiles(): void
{
    $columns = ['name'];
    //
    if (empty($_POST['search']['value'])) {
        ssp(['value' => ''], 'profile', $columns, getEsIndex(), '', false);
    } else {
        ssp($_POST['search'], 'profile', $columns, getEsIndex(), '', false);
    }
}

/**
 * Recupera la lista de usuarios asociados a un perfil específico.
 *
 * Este método requiere el campo POST obligatorio 'profile_id'.
 * Si no se proporciona 'profile_id', devolverá una respuesta JSON con datos vacíos.
 *
 * La función utiliza una búsqueda en ElasticSearch basada en el ID del perfil y otros filtros opcionales.
 * Los datos devueltos son optimizados para tablas que soportan paginación, búsqueda y ordenación en el lado del servidor.
 *
 * @return void Devuelve una respuesta JSON con la lista de usuarios y metadatos para soporte en tablas.
 * @throws Exception
 */
function getUsers(): void
{
    if (empty($_POST['profile_id'])) {
        $data = [
            'draw' => $_POST['draw'] ?? 0,
            'recordsTotal' => 0,
            'recordsFiltered' => 0,
            'data' => [],
            'error' => false
        ];
        exit(json_encode($data));
    }
    //
    $columns = [
        'name',
        'lastname',
        'email',
    ];
    //
    $advance = "(slm.type:user)AND(profile:{$_POST['profile_id']})";
    //
    if (empty($_POST['search']['value'])) {
        ssp(['value' => ''], '', $columns, getEsIndex(), $advance, false);
    } else {
        ssp($_POST['search'], '', $columns, getEsIndex(), $advance, false);
    }
}

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

    try {
        validateRequiredFields(['profile_id']);

        $profileId = getPostString('profile_id');
        $profile = getObjectById($profileId);

        if (!$profile) {
            throw new Exception("Perfil no encontrado: {$profileId}");
        }

        // Decode ACL JSON if present
        if (isset($profile['acl']) && is_string($profile['acl'])) {
            $decodedAcl = json_decode($profile['acl'], true);
            if (json_last_error() === JSON_ERROR_NONE) {
                $profile['acl'] = $decodedAcl;
            }
        }

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

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

    jsonResponse($response);
}

/**
 * Creates a new profile.
 *
 * Required POST fields: name, description, acl
 * Optional POST fields: default_dashboard, default_view, hasPermissions
 *
 * @return never Outputs JSON response with UI action flags
 */
function createProfile(): void
{
    $response = _response(true, '', [
        'title' => 'Error',
        'icon' => 'warning',
        'closeModal' => false,
        'reloadTable' => false
    ]);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['name', 'description', 'acl']);

        $name = sanitizeString(getPostString('name'), 'upper');

        // Check for duplicate profile name
        $existingProfile = getObjectWithValue('profile', 'name.keyword', $name);
        if ($existingProfile) {
            $response['icon'] = 'info';
            throw new Exception('El nombre de perfil ya existe, intente con uno nuevo.');
        }

        $document = [
            'name' => $name,
            'description' => getPostString('description'),
            'status' => 'active',
            'acl' => $_POST['acl'],
            'default_view' => getPostString('default_view'),
            'slm' => [
                'type' => 'profile',
                'timestamp' => getTimestamp()
            ]
        ];

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

        _validateEsCreateResult($result);

        $response = _response(false, 'Perfil agregado correctamente', [
            'title' => 'Éxito',
            'icon' => 'success',
            'closeModal' => true,
            'reloadTable' => true,
            'id' => $result['_id'] ?? null
        ]);
        $logType = 'success';

    } catch (InvalidArgumentException|Exception $e) {
        $response = _response(true, $e->getMessage(), [
            'title' => 'Error',
            'icon' => $response['icon'] ?? 'warning',
            'closeModal' => false,
            'reloadTable' => false
        ]);
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

/**
 * Updates an existing profile.
 *
 * Required POST fields: profile_id, name, description, acl
 * Optional POST fields: default_dashboard, default_view, hasPermissions
 *
 * @return never Outputs JSON response with UI action flags
 */
function updateProfile(): void
{
    $logType = 'error';
    $logData = [];
    try {
        validateRequiredFields(['profile_id', 'name', 'description', 'acl']);

        $profileId = getPostString('profile_id');
        $profile = getObjectById($profileId);

        if (!$profile) {
            throw new Exception("Perfil no encontrado: {$profileId}");
        }

        $updateData = [
            'name' => sanitizeString(getPostString('name'), 'upper'),
            'description' => getPostString('description'),
            'acl' => $_POST['acl'],
            'default_view' => getPostString('default_view'),
            'slm' => [
                'type' => 'profile',
                'timestamp' => getTimestamp()
            ]
        ];
        $logData['updateData'] = $updateData;

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

        _validateEsUpdateResult($result);

        // Eliminar el campo ACL del objeto de perfil
        $updateAcl['script'] = 'ctx._source.remove("acl");';
        $logData['updateAcl'][] = $updateAcl;

        $result = updateObjectByID($profileId, $updateAcl);
        $logData['esResult'][] = $result;

        _validateEsUpdateResult($result);

        // Añadir campo ACL al objeto de perfil
        $updateData['acl'] = $_POST['acl'];
        $logData['updateAcl'][] = $updateData;

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

        _validateEsUpdateResult($result);

        $response = _response(false, 'Perfil actualizado correctamente', [
            'title' => 'Éxito',
            'icon' => 'success',
            'closeModal' => true,
            'reloadTable' => true
        ]);
        $logType = 'success';

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

    jsonResponse($response);
}

/**
 * Soft-deletes a profile by setting status to inactive.
 *
 * Required POST fields: profile_id
 *
 * @return never Outputs JSON response
 */
function deleteProfile(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['profile_id']);

        $profileId = getPostString('profile_id');
        $profile = getObjectById($profileId);

        if (!$profile) {
            throw new Exception("Perfil no encontrado: {$profileId}");
        }

        $updateData = [
            'status' => 'inactive',
            'slm' => [
                'type' => 'profile',  // Maintain original type
                'timestamp' => getTimestamp()
            ]
        ];

        $logData['updateData'] = $updateData;

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

        _validateEsUpdateResult($result);

        $response = _response(false, 'Perfil eliminado correctamente');
        $logType = 'success';

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

    jsonResponse($response);
}

/**
 * Removes profile assignment from a user.
 *
 * Required POST fields: user_id
 *
 * @return never Outputs JSON response
 */
function removeUserProfile(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['user_id']);

        $userId = getPostString('user_id');
        $user = getObjectById($userId);

        if (!$user) {
            throw new Exception("Usuario no encontrado: {$userId}");
        }

        $updateData = [
            'profile' => '',
            'slm' => [
                'type' => 'user',
                'timestamp' => getTimestamp()
            ]
        ];

        $logData['updateData'] = $updateData;

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

        _validateEsUpdateResult($result);

        $response = _response(false, 'Perfil de usuario removido correctamente');
        $logType = 'success';

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

    jsonResponse($response);
}

// =============================================================================
// HELPER FUNCTIONS
// =============================================================================

/**
 * 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');
    }
}
