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

// =============================================================================
// USERS CRUD
// =============================================================================

/**
 * Obtiene una lista de perfiles activos.
 *
 * Este método utiliza criterios predefinidos para buscar y filtrar perfiles activos en la base de datos.
 * Puede recibir un valor de búsqueda opcional a través del campo POST "search".
 *
 * @return void Devuelve una respuesta JSON que incluye la lista de perfiles activos encontrados.
 * @throws Exception
 */
function getProfiles(): void
{
    $response = _response(false, '', ['data' => []]);
    //
    $columns = [
        'name',
        'description'
    ];
    //
    if (empty($_POST['search']['value'])) {
        $response["data"]["profiles"] = ssp(['value' => ''], 'profile', $columns, getEsIndex(), '(slm.type:profile)AND(status:active)', false, true);
    } else {
        $response["data"]["profiles"] = ssp($_POST['search'], 'profile', $columns, getEsIndex(), '(slm.type:profile)AND(status:active)', false, true);
    }
    // Si el ID del perfil no es el de un SLM Super, no se agrega el perfil SLM Super a la lista de perfiles,
    if (_SLM::$session['user']['profile']['id'] !== 'ZYNuIogBygoTW4EQXOl4') {
        $targetId = 'ZYNuIogBygoTW4EQXOl4';
        $response["data"]["profiles"]["data"] = array_filter(
            $response["data"]["profiles"]["data"],
            function ($profile) use ($targetId) {
                return $profile["id"] !== $targetId;
            }
        );
    }
    //
    jsonResponse($response);
}

/**
 * Obtiene una lista de usuarios en formato compatible con DataTables.
 *
 * Este método aplica filtros básicos al conjunto de datos para obtener
 * únicamente usuarios, y permite realizar búsquedas, ordenamientos y paginaciones.
 * Enriquece los resultados agregando detalles del perfil asociado a cada usuario.
 *
 * Requiere que el formato de los datos de entrada sea el esperado por una tabla DataTables.
 *
 * @return void Devuelve una respuesta JSON con los datos de usuarios formateados y enriquecidos
 * @throws Exception
 */
function getUsers(): void
{
    $columns = [
        0 => 'name.keyword',
        1 => 'email.keyword',
        3 => 'profile.keyword',
        4 => 'timestamp',
        5 => 'status.keyword',
    ];
    //
    $advance = "(slm.type:user)";
    //
    if (empty($_POST['search']['value'])) {
        $ssp = sspDataTable(['value' => ''], $_POST['columns'], $advance, $columns, true);
    } else {
        $ssp = sspDataTable($_POST['search'], $_POST['columns'], $advance, $columns, true);
    }
    //
    $rows = [];
    foreach ($ssp['data'] as $index => $row) {
        $profile = getObjectById($row['profile']);
        if ($profile) {
            $row = array_merge($row, ['profile' => $profile]);
        } else {
            $row = array_merge($row, ['profile' => ['name' => '']]);
        }
        //
        $rows[$index] = $row;
    }
    //
    $ssp['data'] = $rows;

    jsonResponse($ssp);
}

/**
 * Retrieves a single user by ID with available profiles.
 *
 * Required POST fields: id
 *
 * @return never Outputs JSON response with user data and profiles
 */
function getUser(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['id']);

        $userId = getPostString('id');
        $source = "_source_includes=name,lastname,email,country_code,telephone_number,profile,acl";
        $user = getObjectById($userId, $source);

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

        // Get active profiles for dropdown
        $columns = ['name', 'description'];
        $filter = '(slm.type:profile)AND(status.keyword:active)';
        $profiles = ssp(['value' => ''], 'profile', $columns, getEsIndex(), $filter, false, true);

        // Get user's profile for comparison
        $userProfile = null;
        if (!empty($user['profile'])) {
            $userProfile = getObjectById($user['profile']);
        }

        $response = _response(false, '', [
            'data' => [
                'user' => $user,
                'profiles' => $profiles,
                'profile_acl' => $userProfile['acl'] ?? null
            ]
        ]);
        $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 user.
 *
 * Required POST fields: name, lastname, email, profile
 * Optional POST fields: country_code, telephone_number
 *
 * @return never Outputs JSON response with UI action flags
 */
function createUser(): void
{
    $response = _response(true, '', [
        'title' => 'Error',
        'icon' => 'warning',
        'closeModal' => false,
        'reloadTable' => false
    ]);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['name', 'lastname', 'email', 'profile']);

        $profileId = getPostString('profile');
        $email = strtolower(getPostString('email'));

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

        // Validate email format
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new Exception("Debe ingresar un correo electrónico válido");
        }

        // Check email uniqueness
        if (getObjectWithValue('user', 'email.keyword', $email)) {
            throw new Exception("El correo ya se encuentra registrado");
        }

        // Generate temporary password
        $password = generateRandomString();

        $document = [
            'name' => sanitizeString(getPostString('name'), 'upper'),
            'lastname' => sanitizeString(getPostString('lastname'), 'upper'),
            'email' => $email,
            'email_confirmed' => true,
            'country_code' => getPostString('country_code'),
            'telephone_number' => getPostString('telephone_number'),
            'password' => md5($password),  // TODO: Use password_hash() instead
            'profile' => $profileId,
            'status' => 'active',
            'slm' => [
                'type' => 'user',
                'timestamp' => getTimestamp()
            ]
        ];

        // Agregar ACL personalizado si se especificó
        $hasCustomACL = getPostBool('hasCustomACL');
        if ($hasCustomACL && isset($_POST['acl'])) {
            $document['acl'] = $_POST['acl'];
        }

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

        _validateEsCreateResult($result);

        // TODO: Send confirmation email
        // sendConfirmationEmail($email, ['name' => $document['name'], 'user_id' => $result['_id']]);

        $response = _response(false, 'Usuario creado 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' => 'warning',
            'closeModal' => false,
            'reloadTable' => false
        ]);
    } finally {
        $logData['response'] = $response;
        addToLog($logType, 'config', __FUNCTION__, $logData);
    }

    jsonResponse($response);
}

/**
 * Updates an existing user.
 *
 * Required POST fields: user_id, name, lastname, email, profile
 * Optional POST fields: country_code, telephone_number
 *
 * @return never Outputs JSON response with UI action flags
 */
function updateUser(): void
{
    $response = _response(true, '', [
        'title' => 'Error',
        'icon' => 'warning',
        'closeModal' => false,
        'reloadTable' => false
    ]);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['user_id', 'name', 'lastname', 'email', 'profile']);

        $userId = getPostString('user_id');
        $email = strtolower(getPostString('email'));
        $profileId = getPostString('profile');

        // Validate user exists
        $user = getObjectById($userId);
        if (!$user) {
            throw new Exception("Usuario no encontrado: {$userId}");
        }

        // Validate email uniqueness if changed
        if ($user['email'] !== $email) {
            if (getObjectWithValue('user', 'email.keyword', $email)) {
                throw new Exception('El correo proporcionado ya está en uso por otro usuario');
            }
        }

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

        $updateData = [
            'name' => sanitizeString(getPostString('name'), 'upper'),
            'lastname' => sanitizeString(getPostString('lastname'), 'upper'),
            'email' => $email,
            'country_code' => getPostString('country_code'),
            'telephone_number' => getPostString('telephone_number'),
            'profile' => $profileId,
            'slm' => [
                'type' => 'user',
                'timestamp' => getTimestamp()
            ]
        ];

        // Manejar ACL personalizado
        $hasCustomACL = getPostBool('hasCustomACL');
        if ($hasCustomACL && isset($_POST['acl'])) {
            $updateData['acl'] = $_POST['acl'];
        } else {
            // Remover ACL personalizado si se desmarcó
            $updateData['acl'] = null;
        }

        $logData['updateData'] = $updateData;

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

        _validateEsUpdateResult($result);

        $response = _response(false, 'Usuario 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);
}

/**
 * Toggles user status between active and inactive.
 *
 * Required POST fields: user_id, status ('enable' or 'disable')
 *
 * @return never Outputs JSON response
 */
function switchUserStatus(): void
{
    $response = _response(true);
    $logType = 'error';
    $logData = [];

    try {
        validateRequiredFields(['user_id', 'status']);

        $userId = getPostString('user_id');
        $action = getPostString('status');

        $updateData = [
            'status' => $action === 'enable' ? 'active' : 'inactive',
            'slm' => [
                'type' => 'user',
                'timestamp' => getTimestamp()
            ]
        ];

        $logData['updateData'] = $updateData;

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

        _validateEsUpdateResult($result);

        $actionText = $action === 'enable' ? 'habilitado' : 'deshabilitado';
        $response = _response(false, "Usuario {$actionText} correctamente");
        $logType = 'success';

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

    jsonResponse($response);
}

/**
 * Resets user password and sends notification email.
 *
 * Required POST fields: user_id
 *
 * @return never Outputs JSON response with new password
 */
function restartPassword(): 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}");
        }

        // Generate new password
        $newPassword = generateRandomString();

        $updateData = [
            'md5_password' => md5($newPassword),  // TODO: Use password_hash() instead
            'slm' => [
                'type' => 'user',
                'timestamp' => getTimestamp()
            ]
        ];

        $logData['updateData'] = ['password_reset' => true];  // Don't log actual password

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

        _validateEsUpdateResult($result);

        // Send password reset notification
        sendPasswordResetConfirmation($user['email'], $newPassword);

        // TODO: Invalidate all active sessions for this user

        $response = _response(false, "Contraseña reiniciada correctamente. Nueva contraseña: {$newPassword}");
        $logType = 'success';

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

    jsonResponse($response);
}

// =============================================================================
// HELPER FUNCTIONS (imported from ajax.modules.slm)
// =============================================================================

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