<?php

use LimeSurvey\Models\Services\UserManager;

//LSYii_Controller
/**
 * Class UserManagementController
 */
class UserManagementController extends LSBaseController
{
    /**
     * @return array
     **/
    public function accessRules()
    {
        return array(
            array(
                'allow',
                'actions' => array(),
                'users' => array('*'), //everybody
            ),
            array(
                'allow',
                'actions' => array(
                    'index', 'addEditUser', 'applyEdit',
                    'addRole', 'batchAddGroup', 'batchApplyRoles', 'batchPermissions', 'batchExpires',
                    'batchSendAndResetLoginData', 'deleteConfirm',  'deleteMultiple', 'exportUser', 'importUser',
                    'renderSelectedItems', 'renderUserImport', 'saveRole', 'saveThemePermissions',
                    'takeOwnership', 'userPermissions', 'userTemplatePermissions', 'viewUser'
                ),
                'users' => array('@'), //only login users
            ),
            array('deny'),
        );
    }

    /**
     * @inheritdoc
     */
    public function filters()
    {
        return [
            'postOnly + applyEdit, deleteUser, userActivateDeactivate,'
            . ' batchStatus, saveUserPermissions, saveThemePermissions, saveRole, importUsers, deleteMultiple,'
            . ' batchSendAndResetLoginData, batchPermissions, batchAddGroup, batchApplyRoles, batchExpires, '
            . ' TakeOwnership'
        ];
    }

    /**
     * @return string|string[]|null
     * @throws CException
     */
    public function actionIndex()
    {
        if (!Permission::model()->hasGlobalPermission('users', 'read')) {
            return $this->renderPartial(
                'partial/error',
                ['errors' => [gT("You do not have permission to access this page.")], 'noButton' => true]
            );
        }
        if (isset($_GET['pageSize'])) {
            Yii::app()->user->setState('pageSize', Yii::app()->request->getParam('pageSize'));
        }
        App()->getClientScript()->registerPackage('usermanagement');
        App()->getClientScript()->registerPackage('select2-bootstrap');

        $aData = [];
        $model = new User('search');
        $model->setAttributes(Yii::app()->getRequest()->getParam('User'), false);
        $aData['model'] = $model;
       // $aData['columnDefinition'] = $model->getManagementColums();
        $aData['pageSize'] = Yii::app()->user->getState('pageSize', Yii::app()->params['defaultPageSize']);
        $aData['formUrl'] = $this->createUrl('userManagement/index');

        $aData['massiveAction'] = $this->renderPartial(
            'massiveAction/_selector',
            ['userid' => $model->uid],
            true,
            false
        );


        $aData['topbar']['title'] = gT('User management');
        $aData['topbar']['backLink'] = App()->createUrl('dashboard/view');
        $aData['topbar']['middleButtons'] = $this->renderPartial('partial/topbarBtns/leftSideButtons', [], true);

        //this is really important, so we have the aData also before rendering the content
        $this->aData = $aData;

        return $this->render('index', [
            'model' => $aData['model'],
            //'columnDefinition' => $aData['columnDefinition'],
            'pageSize' => $aData['pageSize'],
            'formUrl' => $aData['formUrl'],
            'massiveAction' => $aData['massiveAction'],
        ]);
    }

    /**
     * Open modal to edit, or create a new user
     *
     * @param int|null $userid
     * @throws CException
     */
    public function actionAddEditUser($userid = null)
    {
        if (
            ($userid === null && !Permission::model()->hasGlobalPermission('users', 'create'))
            || ($userid !== null && !Permission::model()->hasGlobalPermission('users', 'update'))
        ) {
            return $this->renderPartial(
                'partial/error',
                ['errors' => [gT("You do not have permission to access this page.")]]
            );
        }
        if ($userid === null) {
            $oUser = new User();
        } else {
            $oUser = User::model()->findByPk((int)$userid);
            if ($oUser === null) {
                App()->user->setFlash('error', gT("User does not exist"));
                $this->redirect(App()->request->urlReferrer);
            }
        }

        $randomPassword = \LimeSurvey\Models\Services\PasswordManagement::getRandomPassword();
        $dateformatdetails = getDateFormatData(Yii::app()->session['dateformat']);
        return $this->renderPartial('partial/addedituser', ['oUser' => $oUser, 'randomPassword' => $randomPassword, 'dateformatdetails' => $dateformatdetails]);
    }

    /**
     * Stores changes to user, or triggers userCreateEvent
     *
     * @return string | JSON
     * @throws CException
     * @throws \PHPMailer\PHPMailer\Exception
     */
    public function actionApplyEdit()
    {
        if (!Permission::model()->hasGlobalPermission('users', 'update')) {
            return Yii::app()->getController()->renderPartial('/admin/super/_renderJson', ["data" => [
                'success' => false,
                'errors' => gT("You do not have permission to access this page."),
            ]]);
        }

        $aUser = Yii::app()->request->getParam('User');
        // Sanitize full name to prevent XSS attack
        if (isset($aUser['full_name'])) {
            $aUser['full_name'] = flattenText($aUser['full_name'], false, true);
        }

        $passwordTest = Yii::app()->request->getParam('password_repeat', '');

        if (!empty($passwordTest)) {
            if ($passwordTest !== $aUser['password']) {
                return Yii::app()->getController()->renderPartial('/admin/super/_renderJson', ["data" => [
                    'success' => false,
                    'errors' => gT('Passwords do not match'),
                ]]);
            }
            $user = new User();
            if ($passwordError = $user->checkPasswordStrength($passwordTest)) {
                return Yii::app()->getController()->renderPartial('/admin/super/_renderJson', ["data" => [
                    'success' => false,
                    'errors' => gT('Passwords does not fulfill minimum requirement:') . '<br/>' . $passwordError,
                ]]);
            }
        }

        $expires = Yii::app()->request->getPost('expires', null);
        if (!empty($expires)) {
            $dateformatdetails = getDateFormatData(Yii::app()->session['dateformat']);
            $datetimeobj = new Date_Time_Converter($expires, $dateformatdetails['phpdate'] . ' H:i');
            $aUser['expires'] = $datetimeobj->convert("Y-m-d H:i:s");
        } else {
            $aUser['expires'] = null;
        }

        // A user may not edit himself using this action
        if (isset($aUser['uid']) && $aUser['uid'] && $aUser['uid'] == Yii::app()->user->id) {
            return App()->getController()->renderPartial('/admin/super/_renderJson', [
                "data" => [
                    'success' => false,
                    'errors'  =>  gT('No permission')
                ]
                ]);
        }

        if (isset($aUser['uid']) && $aUser['uid']) {
            $oUser = $this->updateAdminUser($aUser);
            if ($oUser->hasErrors()) {
                return App()->getController()->renderPartial('/admin/super/_renderJson', [
                    "data" => [
                        'success' => false,
                        'errors'  => CHtml::errorSummary($oUser)
                    ]
                ]);
            }
            return App()->getController()->renderPartial('/admin/super/_renderJson', [
                'data' => [
                    'success' => true,
                    'message' => gT('User successfully updated')
                ]
            ]);
        } else {
            //generate random password when password is empty
            if (empty($aUser['password'])) {
                $newPassword = \LimeSurvey\Models\Services\PasswordManagement::getRandomPassword();
                $aUser['password'] =  $newPassword;
            }

            //retrive the raw password
            $aUser['rawPassword'] = $aUser['password'];

            $passwordSetByUser = Yii::app()->request->getParam('preset_password');
            if ($passwordSetByUser == 0) { //in this case admin has not set a password, email with link will be sent
                $data = $this->createAdminUser($aUser);
            } else {
                //in this case admin has set a password, no email will be send ..just create user with given credentials
                $data = $this->createAdminUser($aUser, false);
            }

            return App()->getController()->renderPartial('/admin/super/_renderJson', [
                "data" => $data
            ]);
        }
    }

    /**
     * Handle user deletion request: validates permissions, optionally transfers surveys, deletes the user, and returns a JSON response.
     *
     * Performs permission and safety checks, may render a survey-transfer selection when the target owns surveys, transfers surveys when a destination is provided, invokes UserManager->deleteUser for the resolved user model, and returns a JSON partial describing the outcome.
     *
     * @return string JSON partial containing either:
     *                - `success` (false) and `errors` (array|string|object) when permission or validation checks fail, or
     *                - `success` (bool) and `message` (string) after an attempted deletion, or
     *                - `success` (true) and `html` (string) with a survey-transfer selection when the target owns surveys and no transfer destination was provided.
     */
    public function actionDeleteUser()
    {
        $permission_users_delete = Permission::model()->hasGlobalPermission('users', 'delete');
        $permission_superadmin_read = Permission::model()->hasGlobalPermission('superadmin', 'read');
        if (!$permission_users_delete) {
            return App()->getController()->renderPartial('/admin/super/_renderJson', [
                'data' => [
                    'success' => false,
                    'errors' => gT("We are sorry but you don't have permissions to do this.")
                ]
            ]);
        }
        $userId = (int) App()->request->getPost('userid');
        $oUser = User::model()->findByPk($userId);
        $currentUser = (int) App()->user->getId();
        if (!$oUser) {
            return App()->getController()->renderPartial('/admin/super/_renderJson', [
                'data' => [
                    'success' => false,
                    'errors' => gT("User does not exist")
                ]
            ]);
        }
        if ($permission_superadmin_read) {
            // Can't delete forced superadmins
            if (Permission::isForcedSuperAdmin($userId)) {
                return App()->getController()->renderPartial('/admin/super/_renderJson', [
                    'data' => [
                        'success' => false,
                        'errors' => gT("We are sorry but you don't have permissions to do this.")
                    ]
                ]);
            }
            // Can't delete yourself
            if ($userId === $currentUser) {
                return App()->getController()->renderPartial('/admin/super/_renderJson', [
                    'data' => [
                        'success' => false,
                        'errors' => gT("You cannot delete yourself.")
                    ]
                ]);
            }
        }
        if (!$permission_superadmin_read) {
            // Can't delete yourself
            if ($userId === $currentUser) {
                return App()->getController()->renderPartial('/admin/super/_renderJson', [
                    'data' => [
                        'success' => false,
                        'errors' => gT("You cannot delete yourself.")
                    ]
                ]);
            }
            // Dont have permission to delete users
            if (!$permission_users_delete) {
                return App()->getController()->renderPartial('/admin/super/_renderJson', [
                    'data' => [
                        'success' => false,
                        'errors' => gT("We are sorry but you don't have permissions to do this.")
                    ]
                ]);
            }
            // Can't delete users that are not owned by the current user
            if ((int) $oUser->parent_id !== $currentUser) {
                return App()->getController()->renderPartial('/admin/super/_renderJson', [
                    'data' => [
                        'success' => false,
                        'errors' => gT("We are sorry but you don't have permissions to do this.")
                    ]
                ]);
            }
            // Can't delete forced superadmins
            if (Permission::isForcedSuperAdmin($userId)) {
                return App()->getController()->renderPartial('/admin/super/_renderJson', [
                    'data' => [
                        'success' => false,
                        'errors' => gT("We are sorry but you don't have permissions to do this.")
                    ]
                ]);
            }
        }

        $messages = [];
        $transferTo = Yii::app()->request->getPost('transfer_surveys_to');

        if (empty($transferTo)) {
            // If $transferTo is empty, check if user owns a survey.
            // If so, render the "transfer to" selection screen
            $aOwnedSurveys = Survey::model()->findAllByAttributes(array('owner_id' => $userId));
            if (count($aOwnedSurveys)) {
                $postuser = flattenText(Yii::app()->request->getPost("user"));
                $aUsers = User::model()->findAll();
                return Yii::app()->getController()->renderPartial(
                    '/admin/super/_renderJson',
                    [
                        "data" => [
                            'success' => true,
                            'html' => $this->renderPartial(
                                'partial/transfersurveys',
                                [
                                    'postuserid' => $userId,
                                    'postuser' => $postuser,
                                    'current_user' => Yii::app()->user->id,
                                    'users' => $aUsers,
                                ],
                                true
                            ),
                        ]
                    ]
                );
            }
        } else {
            // If $transferTo is not null, transfer the surveys
            $iSurveysTransferred = Survey::model()->updateAll(array('owner_id' => $transferTo), 'owner_id=' . $userId);
            if ($iSurveysTransferred) {
                $sTransferredTo = User::model()->findByPk($transferTo)->users_name;
                $messages[] = sprintf(gT("All of the user's surveys were transferred to %s."), $sTransferredTo);
            }
        }

        $userManager = new UserManager();
        $result = $userManager->deleteUser($oUser);
        $messages = array_merge($messages, $result->getRawMessages());

        return App()->getController()->renderPartial('/admin/super/_renderJson', [
            'data' => [
                'success' => $result->isSuccess(),
                'message' => implode(" ", $messages),
            ]
        ]);
    }

    /**
     * Show user activation confirmation
     *
     * @return void|string
     * @throws CException
     */
    public function actionActivationConfirm()
    {
        if (!Permission::model()->hasGlobalPermission('users', 'update')) {
            throw new CHttpException(403, gT("You do not have permission to access this page."));
        }
        $userId = Yii::app()->request->getParam('userid');
        $action = Yii::app()->request->getParam('action');

        $userId = sanitize_int($userId);

        $aData['userId'] = $userId;
        $aData['action'] = $action;

        return $this->renderPartial('partial/confirmuseractivation', $aData);
    }


    /**
     * Stores the status settings
     *
     * @return void|string
     * @throws CException
     */
    public function actionUserActivateDeactivate()
    {
        // See User_>getManagementButtons for permission
        if (!Permission::model()->hasGlobalPermission('users', 'update')) {
            throw new CHttpException(403, gT("You do not have permission to access this page."));
        }
        $userId = sanitize_int(Yii::app()->request->getParam('userid'));
        $action = Yii::app()->request->getParam('action');
        $oUser = User::model()->findByPk($userId);

        if ($oUser == null) {
            throw new CHttpException(404, gT("Invalid user ID"));
        }
        if (Permission::model()->getUserId() == $userId) { // canEdit allow user to update himself
            throw new CHttpException(403, gT("You can not update this user."));
        }
        if (!$oUser->canEdit()) {
            throw new CHttpException(403, gT("You can not update this user."));
        }

        if ($oUser->setActivationStatus($action)) {
            return $this->renderPartial('/admin/super/_renderJson', [
                'data' => [
                    'success' => true,
                    'message' => gT('Status successfully updated')
                ]
            ]);
        };
        /* activationstatus is not OK */
        return $this->renderPartial('/admin/super/_renderJson', [
            'data' => [
                'success' => false
            ]
        ]);
    }

    /**
     * Stores the status settings run via MassEdit
     *
     * @throws CException
     */
    public function actionBatchStatus()
    {
        if (!Permission::model()->hasGlobalPermission('users', 'update')) {
            throw new CHttpException(403, gT("You do not have permission to access this page."));
        }

        $userIds = json_decode(Yii::app()->request->getPost('sItems', "[]"));
        $operation = Yii::app()->request->getPost('status_selector', '');
        $results = $this->userActivation($userIds, $operation);

        $error = null;
        foreach ($results as $result) {
            if (isset($result['error']) && $result['error'] == 'Reached the limit') {
                $error = true;
            }
        }

        $tableLabels = array(gT('User ID'), gT('Username'), gT('Status'));

        $data = array(
            'aResults'     => $results,
            'successLabel' => gT('Saved successfully'),
            'tableLabels' =>  $tableLabels,
        );

        if ($error) {
            $data['additionalMessage'] = $this->renderPartial('/userManagement/partial/planupgradebutton');
        }

        Yii::app()->getController()->renderPartial('ext.admin.survey.ListSurveysWidget.views.massive_actions._action_results', $data);
    }


    /**
     * Activate / deactivate user
     * @todo : move this to a private function !!!
     * @param array $userIds
     * @param string $operation activate or deactivate
     * @return array
     */
    public function userActivation($userIds, $operation)
    {
        if (!App()->getRequest()->getIsPostRequest()) {
            throw new CHttpException(400, gT('Your request is invalid.'));
        }
        $results = [];
        foreach ($userIds as $iUserId) {
            $oUser = User::model()->findByPk($iUserId);
            if ($oUser == null) {
                throw new CHttpException(404, gT("Invalid user ID"));
            } else {
                $results[$iUserId]['title'] = $oUser->users_name;
                if (!$this->isAllowedToEdit($oUser)) {
                    $results[$iUserId]['error'] = gT('Unauthorized');
                    $results[$iUserId]['result'] = false;
                    continue;
                }
                $results[$iUserId]['result'] = $oUser->setActivationStatus($operation);
                if (!$results[$iUserId]['result']) {
                    $results[$iUserId]['error'] = gT('Error');
                }
            }
        }
        return $results;
    }

    /**
     * Check if the current user allowed to update $user
     *
     * @return boolean
     */
    private function isAllowedToEdit($user)
    {
        $permission_superadmin_read = Permission::model()->hasGlobalPermission('superadmin', 'read');
        $permission_users_update = Permission::model()->hasGlobalPermission('users', 'update');
        $ownedOrCreated = $user->parent_id == App()->session['loginID']; // User is owned or created by you

        return ( $permission_superadmin_read && !(Permission::isForcedSuperAdmin($user->uid) || $user->uid == App()->user->getId()))
            || (!$permission_superadmin_read && ($user->uid != App()->session['loginID'] //Can't change your own permissions
                    && ( $permission_users_update && $ownedOrCreated)
                    && !Permission::isForcedSuperAdmin($user->uid)
                )
            );
    }

    /**
     * Show user delete confirmation
     *
     * @return void|string
     * @throws CException
     */
    public function actionDeleteConfirm()
    {
        if (!Permission::model()->hasGlobalPermission('users', 'delete')) {
            return $this->renderPartial(
                'partial/error',
                ['errors' => [gT("You do not have permission to access this page.")], 'noButton' => true]
            );
        }
        $userId = Yii::app()->request->getParam('userid');
        $userId = sanitize_int($userId);
        $aData['userId'] = $userId;
        return $this->renderPartial('partial/confirmuserdelete', $aData);
    }

    /**
     * Show some user detail and statistics
     *
     * @param int $userid
     * @return string|null
     * @throws CException
     */
    public function actionViewUser(int $userid): ?string
    {
        if (!Permission::model()->hasGlobalPermission('users', 'read')) {
            return $this->renderPartial(
                'partial/error',
                ['errors' => [gT("You do not have permission to access this page.")], 'noButton' => true]
            );
        }
        $oUser = User::model()->findByPk($userid);

        $userGroups = array_map(function ($oUGMap) {
            return $oUGMap->group->name;
        }, UserInGroup::model()->findAllByAttributes(['uid' => $oUser->uid]));

        if (App()->getRequest()->getIsAjaxRequest()) {
            return $this->renderPartial('partial/showuser', [
                'usergroups' => $userGroups,
                'oUser' => $oUser,
                'ajax' => true
            ]);
        }
        return $this->render('partial/showuser', [
            'usergroups' => $userGroups,
            'oUser' => $oUser,
            'ajax' => false
        ]);
    }

    /**
     * Opens a modal to edit user permissions
     *
     * @return string
     * @throws CException
     */
    public function actionUserPermissions()
    {
        $userId = Yii::app()->request->getParam('userid');
        $userId = sanitize_int($userId);
        $oUser = User::model()->findByPk($userId);

        $userManager = new UserManager(Yii::app()->user, $oUser);
        if (!$userManager->canAssignPermissions()) {
            return $this->renderPartial(
                'partial/error',
                ['errors' => [gT("You do not have permission to access this page.")], 'noButton' => true]
            );
        }

        // Check permissions
        $aBasePermissions = Permission::model()->getGlobalBasePermissions();
        // superadmin permission always need create
        if (!Permission::model()->hasGlobalPermission('superadmin', 'create')) {
            unset($aBasePermissions['superadmin']);
        }
        if (!Permission::model()->hasGlobalPermission('superadmin', 'read')) {
            // if not superadmin filter the available permissions as no admin may give more permissions than he owns
            Yii::app()->session['flashmessage'] = gT("Note: You can only give limited permissions to other users because your own permissions are limited, too.");
            $aFilteredPermissions = array();
            foreach ($aBasePermissions as $PermissionName => $aPermission) {
                foreach ($aPermission as $sPermissionKey => &$sPermissionValue) {
                    if (
                        $sPermissionKey != 'title' && $sPermissionKey != 'img' &&
                        !Permission::model()->hasGlobalPermission($PermissionName, $sPermissionKey)
                    ) {
                        $sPermissionValue = false;
                    }
                }
                // Only show a row for that permission if there is at least one permission he may give to other users
                if (
                    $aPermission['create'] || $aPermission['read'] || $aPermission['update']
                    || $aPermission['delete'] || $aPermission['import'] || $aPermission['export']
                ) {
                    $aFilteredPermissions[$PermissionName] = $aPermission;
                }
            }
            $aBasePermissions = $aFilteredPermissions;
        }

        return $this->renderPartial(
            'partial/editpermissions',
            [
                "oUser" => $oUser,
                "aBasePermissions" => $aBasePermissions,
            ]
        );
    }

    /**
     * Stores the changed permissions
     *
     * @return string | JSON
     * @throws CException
     */
    public function actionSaveUserPermissions(): string
    {
        $userId = Yii::app()->request->getPost('userid');
        $oUser = User::model()->findByPk($userId);

        $userManager = new UserManager(Yii::app()->user, $oUser);
        if (!$userManager->canAssignPermissions()) {
            return Yii::app()->getController()->renderPartial('/admin/super/_renderJson', [
                "data" => [
                    'success' => false,
                    'errors' => [gT("You do not have permission to access this page.")],
                ]
            ]);
        }

        $aPermissions = Yii::app()->request->getPost('Permission', []);

        Permissiontemplates::model()->clearUser($userId);

        $results = $this->applyPermissionFromArray($userId, $aPermissions);

        $oUser->modified = date('Y-m-d H:i:s');
        $oUser->save();

        return Yii::app()->getController()->renderPartial('/admin/super/_renderJson', [
            "data" => [
                'success' => true,
                'message' => gT("Saved permissions successfully.")
            ]
        ]);
    }

    /**
     * Opens a modal to edit user template permissions
     *
     * @return string|null
     * @throws CException
     */
    public function actionUserTemplatePermissions(): ?string
    {
        if (!Permission::model()->hasGlobalPermission('users', 'update')) {
            return $this->renderPartial(
                'partial/error',
                ['errors' => [gT("You do not have permission to access this page.")], 'noButton' => true]
            );
        }
        $aTemplateModels = Template::model()->findAll();
        $userId = Yii::app()->request->getParam('userid');
        $oUser = User::model()->findByPk((int)$userId);

        $aTemplates = array_map(function ($oTemplate) use ($userId) {
            $oPermission = Permission::model()->findByAttributes(array('permission' => $oTemplate->folder, 'uid' => $userId, 'entity' => 'template'));
            $aTemplate = $oTemplate->attributes;
            $aTemplate['value'] = $oPermission == null ? 0 : $oPermission->read_p;
            return $aTemplate;
        }, $aTemplateModels);

        return $this->renderPartial(
            'partial/edittemplatepermissions',
            [
                "oUser" => $oUser,
                "aTemplates" => $aTemplates,
            ]
        );
    }

    /**
     * Stores the changed permissions
     *
     * @return string | JSON
     * @throws CException
     */
    public function actionSaveThemePermissions(): string
    {
        if (
            !(Permission::model()->hasGlobalPermission('users', 'update') &&
            Permission::model()->hasGlobalPermission('templates', 'update'))
        ) {
            return $this->renderPartial(
                'partial/error',
                ['errors' => [gT("You do not have permission to access this page.")], 'noButton' => true]
            );
        }

        $userId = Yii::app()->request->getPost('userid');
        $aTemplatePermissions = Yii::app()->request->getPost('TemplatePermissions', []);

        Permission::editThemePermissionsUser($userId, $aTemplatePermissions);

        return Yii::app()->getController()->renderPartial('/admin/super/_renderJson', [
            "data" => [
                'success' => true,
                'message' => gT("Saved theme permissions successfully.")
            ]
        ]);
    }

    /**
     * Opens the modal to add dummy users
     *
     * @return string|null
     * @throws CException
     */
    public function actionAddRole(): ?string
    {
        $userId = Yii::app()->request->getParam('userid');
        $oUser = User::model()->findByPk($userId);

        $userManager = new UserManager(Yii::app()->user, $oUser);
        if (!$userManager->canAssignRole() || $oUser->uid == App()->user->getId()) {
            return $this->renderPartial(
                'partial/error',
                ['errors' => [gT("You do not have permission to access this page.")], 'noButton' => true]
            );
        }

        $aPermissionTemplates = Permissiontemplates::model()->findAll();
        $aPossibleRoles = [];
        array_walk(
            $aPermissionTemplates,
            function ($oPermissionRole) use (&$aPossibleRoles) {
                $aPossibleRoles[$oPermissionRole->ptid] = $oPermissionRole->name;
            }
        );
        $aCurrentRoles = array_map(function ($oRole) {
            return $oRole->ptid;
        }, $oUser->roles);

        return $this->renderPartial(
            'partial/addrole',
            [
                'oUser' => $oUser,
                'aPossibleRoles' => $aPossibleRoles,
                'aCurrentRoles' => $aCurrentRoles,
            ]
        );
    }

    /**
     * Save role of user
     *
     * @return string|null
     * @throws CException
     */
    public function actionSaveRole(): ?string
    {
        $iUserId = Yii::app()->request->getPost('userid');
        $oUser = User::model()->findByPk($iUserId);

        $userManager = new UserManager(Yii::app()->user, $oUser);
        if (!$userManager->canAssignRole() || $oUser->uid == App()->user->getId()) {
            return Yii::app()->getController()->renderPartial('/admin/super/_renderJson', [
                "data" => [
                    'success' => false,
                    'errors' => gT("You do not have permission to access this page."),
                ]
            ]);
        }
        $aUserRoleIds = Yii::app()->request->getPost('roleselector', []);
        $results = [];

        $clearUser = Permissiontemplates::model()->clearUser($iUserId);
        foreach ($aUserRoleIds as $iUserRoleId) {
            if ($iUserRoleId == '') {
                continue;
            }
            $results[$iUserRoleId] = Permissiontemplates::model()->applyToUser($iUserId, $iUserRoleId);
        }
        if (empty($aUserRoleIds)) {
            $results['clear'] = $clearUser;
        }
        return $this->renderPartial('partial/json', [
            "data" => [
                'success' => true,
                'message' => 'Saved user roles successfuly',
            ]
        ]);
    }

    /**
     * Render the import-users modal part for a given import format.
     *
     * @param string $importFormat The import format to render; allowed values are 'csv' or 'json'.
     * @return string The rendered HTML partial for the import modal.
     * @throws LSUserException If an unsupported import format is provided.
     */
    public function actionRenderUserImport(string $importFormat = 'csv')
    {
        if (!Permission::model()->hasGlobalPermission('users', 'create')) {
            return $this->renderPartial(
                'partial/error',
                ['errors' => [gT("You do not have permission to access this page.")], 'noButton' => true]
            );
        }
        if (!in_array($importFormat, ['csv', 'json'])) {
            throw new LSUserException(400, gT("Invalid format"));
        }

        switch ($importFormat) {
            case "json":
                $importNote = sprintf(gT("Please make sure that your JSON arrays contain the fields '%s', '%s'; '%s' and '%s' are not imported."), '<b>users_name</b>', '<b>email</b>', '<b>uid</b>', '<b>parent_id</b>');
                $allowFileType = ".json,application/json";
                break;
            case "csv":
            default:
                $importNote = sprintf(gT("Please make sure that your CSV contains the fields '%s', '%s'; '%s' and '%s' are not imported."), '<b>users_name</b>', '<b>email</b>', '<b>uid</b>', '<b>parent_id</b>');
                $allowFileType = ".csv";
        }
        return $this->renderPartial('partial/importuser', [
            "note"         => $importNote,
            "importFormat" => $importFormat,
            "allowFile"    => $allowFileType
        ]);
    }

    /**
     * Imports users from an uploaded CSV or JSON file, creating new users or updating existing ones, then redirects to the user index after adding alert message.
     *
     * @param string $importFormat Format of the uploaded file; must be "csv" or "json".
     * @return string The rendered error partial when the current user lacks create permission; or redirect.
     * @throws LSUserException If an unsupported $importFormat is provided.
     */
    public function actionImportUsers(string $importFormat = 'csv'): string
    {
        if (!Permission::model()->hasGlobalPermission('users', 'create')) {
            return $this->renderPartial(
                'partial/error',
                ['errors' => [gT("You do not have permission to access this page.")], 'noButton' => true]
            );
        }
        if (!in_array($importFormat, ['csv', 'json'])) {
            throw new LSUserException(400, gT("Invalid format"));
        }
        $overwriteUsers = boolval(App()->getRequest()->getPost('overwrite'));

        switch ($importFormat) {
            case "json":
                $aNewUsers = UserParser::getDataFromJSON($_FILES);
                break;
            case "csv":
            default:
                $aNewUsers = UserParser::getDataFromCSV($_FILES);
        }
        if (empty($aNewUsers)) {
            Yii::app()->setFlashMessage(gT("No user definition found in file."), 'error');
            $this->redirect(['userManagement/index']);
            App()->end();
        }
        $created = [];
        $updated = [];
        $existingAttributes = User::model()->attributeNames();
        $dateAttributes = ['last_login', 'validation_key_expiration','last_forgot_email_password','expires'];
        foreach ($aNewUsers as $aNewUser) {
            // Unset not imported or invalid attribute
            $aNewUser = array_intersect_key($aNewUser, array_flip($existingAttributes));
            $aNewUser = array_diff_key($aNewUser, array_flip(['uid','parent_id', 'created', 'modified']));
            /* Find if exist */
            $oUser = User::model()->findByAttributes(['users_name' => $aNewUser['users_name']]);
            if ($oUser  !== null) {
                if ($overwriteUsers) {
                    /* Check permission to edit this user */
                    if (!$oUser->canEdit()) {
                        Yii::app()->setFlashMessage(sprintf(gT("You don't have permission to edit user %s."), $aNewUser['users_name']), 'warning');
                        continue;
                    }
                    /* Check permission to edit self */
                    if ($oUser->uid == App()->user->id) {
                        Yii::app()->setFlashMessage(gT("You can't use the import function to update your own account."), 'warning');
                        continue;
                    }
                    foreach ($aNewUser as $attribute => $value) {
                        if ($attribute === 'password') {
                            continue; // Handle password separately via setPassword()
                        }
                        if (!empty($value) || !in_array($attribute, $dateAttributes)) {
                            $oUser->setAttribute($attribute, $value);
                        }
                    }
                    if (!empty($aNewUser['password']) && $aNewUser['password'] != ' ') {
                        $oUser->setPassword($aNewUser['password'], false);
                    }
                    $save = $oUser->save(true, array_keys($aNewUser));
                    if ($save) {
                        $updated[] = $aNewUser;
                    }
                }
            } else {
                if (empty($aNewUser['password']) || $aNewUser['password'] == ' ') {
                    $aNewUser['password'] = \LimeSurvey\Models\Services\PasswordManagement::getRandomPassword();
                }
                /* Some default */
                $aNewUser['full_name'] = $aNewUser['full_name'] ?? "";
                $aNewUser['lang'] = $aNewUser['lang'] ?? "auto";
                $newUserAttributes = $this->createNewUser([
                    'users_name' => $aNewUser['users_name'],
                    'full_name' => $aNewUser['full_name'],
                    'password' => $aNewUser['password'], // AuthDB use User::insertUser this use User::setPassword
                    'email' => $aNewUser['email'],
                    'lang' => $aNewUser['lang'],
                ]);
                if ($newUserAttributes) {
                    /* Update it with other attributes */
                    $oUser = User::model()->findByPk($newUserAttributes['uid']);
                    $aNewUserExtra = array_diff_key($aNewUser, array_flip(['users_name','full_name', 'password', 'email', 'lang']));
                    if (!empty($aNewUserExtra)) {
                        foreach ($aNewUserExtra as $attribute => $value) {
                            if (!empty($value) || !in_array($attribute, $dateAttributes)) {
                                $oUser->setAttribute($attribute, $value);
                            }
                        }
                        $oUser->save(true, array_keys($aNewUserExtra));
                    }
                    $created[] = $aNewUser;
                }
            }
        }
        if (count($created) || count($updated)) {
            Yii::app()->setFlashMessage(gT("Users imported successfully."), 'success');
        }
        $this->redirect(['userManagement/index']);
    }


    /**
     * Export user records to a downloadable CSV or JSON file.
     *
     * Exports either a single user (when $uid > 0) or all users to a temporary file and sends it as a file download.
     * The exported records are the user attributes with the `password` field cleared.
     * CSV output uses a UTF-8 BOM and semicolon (;) as the field delimiter with double-quote enclosure.
     *
     * @param string $outputFormat 'json' or 'csv' (defaults to 'csv' for unrecognized values)
     * @param int $uid User ID to export; 0 exports all users
     * @return string in case of error
     */
    public function actionExportUser(string $outputFormat, int $uid = 0)
    {
        //Check if user has permissions to export users
        if (!Permission::model()->hasGlobalPermission('users', 'export')) {
            return $this->renderPartial(
                'partial/error',
                ['errors' => [gT("You do not have permission to access this page.")], 'noButton' => true]
            );
        }

        if ($uid > 0) {
            $oUsers = User::model()->findByPk($uid);
        } else {
            $oUsers = User::model()->findAll();
        }

        //test GET PARAM $ouputFormat
        switch ($outputFormat) {
            case 'csv':
            case 'json':  //all good, both cases are ok
                break;
            default:
                $outputFormat = 'csv';
        }

        $aUsers = array();
        $sTempDir = Yii::app()->getConfig("tempdir");
        $exportFile = $sTempDir . DIRECTORY_SEPARATOR . 'users_export.' . $outputFormat;
        foreach ($oUsers as $user) {
            $exportUser = $user->getAttributes();
            $exportUser['password'] = '';
            // Leave one_time_pw and validation_key ?
            array_push($aUsers, $exportUser);
        }

        switch ($outputFormat) {
            case "json":
                $json = json_encode($aUsers, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
                $fp = fopen($exportFile, 'w');
                fwrite($fp, $json);
                fclose($fp);
                header('Content-Encoding: UTF-8');
                header("Content-Type:application/json; charset=UTF-8");
                break;

            case "csv":
                $fp = fopen($exportFile, 'w');

                //Add utf-8 encoding
                fprintf($fp, chr(0xEF) . chr(0xBB) . chr(0xBF));
                $header = array_keys($exportUser);
                //Add csv header
                fputcsv($fp, $header, ';', '"');

                //add csv row datas
                foreach ($aUsers as $fields) {
                    fputcsv($fp, $fields, ';', '"');
                }
                fclose($fp);
                header('Content-Encoding: UTF-8');
                header("Content-type: text/csv; charset=UTF-8");
                break;
        }
        //end file to download
        header("Content-Disposition: attachment; filename=userExport." . $outputFormat);
        header("Pragma: no-cache");
        header("Expires: 0");
        @readfile($exportFile);
        unlink($exportFile);
    }

    /**
     * Deletes multiple users selected by massive action.
     *
     * @return void | string
     * @throws CException
     */
    public function actionDeleteMultiple()
    {
        // Check if the user has permission to delete users
        if (!Permission::model()->hasGlobalPermission('users', 'delete')) {
            return $this->renderPartial(
                'partial/error',
                ['errors' => [gT("You do not have permission to access this page.")], 'noButton' => true]
            );
        }

        // Get the selected users from the POST request
        $aUsers = json_decode(App()->request->getPost('sItems', ''));
        $aResults = [];

        // Iterate over the selected users
        foreach ($aUsers as $user) {
            $aResults[$user]['title'] = '';
            $model = $this->loadModel($user);
            $aResults[$user]['title'] = $model->users_name;

            // Delete the user and store the result
            $aResults[$user]['result'] = $this->deleteUser($user);

            // If the user could not be deleted or it's the current user, add an error message
            if (!$aResults[$user]['result'] || $user == Yii::app()->user->id) {
                $aResults[$user]['error'] = gT("You cannot delete yourself or a protected user.");
            }
        }

        // Define the labels for the table
        $tableLabels = array(gT('User ID'), gT('Username'), gT('Status'));

        // Render the results in a partial view
        Yii::app()->getController()->renderPartial(
            'ext.admin.survey.ListSurveysWidget.views.massive_actions._action_results',
            array(
                'aResults'     => $aResults,
                'successLabel' => gT('Deleted'),
                'tableLabels' =>  $tableLabels
            )
        );
    }

    /**
     * render selected items for massive action modal
     *
     * @return void
     * @throws CHttpException
     * @throws CException
     */
    public function actionRenderSelectedItems()
    {
        $aUsers = json_decode(App()->request->getPost('$oCheckedItems', ''));
        $aResults = [];
        $gridid = App()->request->getParam('$grididvalue');

        foreach ($aUsers as $user) {
            $aResults[$user]['title'] = '';
            $model = $this->loadModel($user);

            if ($gridid == 'usermanagement--identity-gridPanel') {
                $aResults[$user]['title'] = $model->users_name;
            }

            $aResults[$user]['result'] = gT('Selected');
        }
        //set Modal table labels
        $tableLabels = array(gT('User ID'), gT('Username'), gT('Status'));

        App()->getController()->renderPartial(
            'ext.admin.grid.MassiveActionsWidget.views._selected_items',
            array(
                'aResults'     => $aResults,
                'successLabel' => gT('Selected'),
                'tableLabels'  => $tableLabels,
            )
        );
    }

    /**
     * Method to resend a password to selected surveyadministrators (MassAction)
     *
     * @return String
     * @throws CException
     * @throws \PHPMailer\PHPMailer\Exception
     */
    public function actionBatchSendAndResetLoginData()
    {
        if (!Permission::model()->hasGlobalPermission('users', 'update')) {
            return $this->renderPartial(
                'partial/error',
                ['errors' => [gT("You do not have permission to access this page.")], 'noButton' => true]
            );
        }

        $aUsers = json_decode(App()->request->getPost('sItems', "[]"));

        $aResults = [];
        foreach ($aUsers as $user) {
            $oUser = $this->loadModel($user);
            $aResults[$user]['result'] = false;
            $aResults[$user]['title'] = $oUser->users_name;
            //User should not reset and resend  email to himself throw massive action
            if ($oUser->uid == Yii::app()->user->id) {
                $aResults[$user]['result'] = false;
                $aResults[$user]['error'] = gT("Error! Please change your password from your profile settings.");
                continue;
            }
            $userManager = new UserManager(Yii::app()->user, $oUser);
            if (!$userManager->canEdit()) {
                $aResults[$user]['result'] = false;
                $aResults[$user]['error'] = gT("Error! You do not have the permission to edit this user.");
                continue;
            }
            $passwordManagement = new \LimeSurvey\Models\Services\PasswordManagement($oUser);
            $successData = $passwordManagement->sendPasswordLinkViaEmail(\LimeSurvey\Models\Services\PasswordManagement::EMAIL_TYPE_RESET_PW);
            $success = $successData['success'];
            if (!$success) {
                $aResults[$user]['error'] = sprintf(gT("Error: New password could not be sent to %s"), $oUser->email);
            }
            $aResults[$user]['result'] = $success;
        }

        $tableLabels = array(gT('User ID'), gT('Username'), gT('Status'));

        Yii::app()->getController()->renderPartial(
            'ext.admin.survey.ListSurveysWidget.views.massive_actions._action_results',
            array(
                'aResults'     => $aResults,
                'successLabel' => gT('Email successfully sent.'),
                'tableLabels' =>  $tableLabels
            )
        );
    }

    /**
     * Stores the permission settings run via MassEdit
     *
     * @return string
     * @throws CException
     */
    public function actionBatchPermissions()
    {
        if (!Permission::model()->hasGlobalPermission('users', 'update')) {
            return $this->renderPartial(
                'partial/error',
                ['errors' => [gT("You do not have permission to access this page.")], 'noButton' => true]
            );
        }

        $userIds = json_decode(Yii::app()->request->getPost('sItems', "[]"));
        $aPermissions = Yii::app()->request->getPost('Permission', []);
        $results = [];
        foreach ($userIds as $iUserId) {
            $oUser = User::model()->findByPk($iUserId);
            $results[$iUserId] = [
                'title' => $oUser->users_name
            ];
            if ($oUser->uid == Yii::app()->user->id) {
                $aResults[$iUserId]['result'] = false;
                $aResults[$iUserId]['error'] = gT("You can not update your own permission.");
                continue;
            }
            $userManager = new UserManager(Yii::app()->user, $oUser);
            if (!$userManager->canAssignPermissions()) {
                $results[$iUserId]['result'] = false;
                $results[$iUserId]['error'] = gT("You are not allowed to assign permissions to this user.");
                continue;
            }
            $aPermissionsResults = $this->applyPermissionFromArray($iUserId, $aPermissions);
            $oUser->modified = date('Y-m-d H:i:s');
            $results[$iUserId]['result'] = $oUser->save();
            $results[$iUserId]['title'] = $oUser->users_name;
            foreach ($aPermissionsResults as $aPermissionsResult) {
                if (!$aPermissionsResult['success']) {
                    $results[$iUserId]['result'] = false;
                    break;
                }
            }
            if (!$results[$iUserId]['result']) {
                $results[$iUserId]['error'] = gT('Error');
            }
        }

        $tableLabels = array(gT('User ID'), gT('Username'), gT('Status'));

        Yii::app()->getController()->renderPartial(
            'ext.admin.survey.ListSurveysWidget.views.massive_actions._action_results',
            array(
                'aResults'     => $results,
                'successLabel' => gT('Saved successfully'),
                'tableLabels' =>  $tableLabels
            )
        );
    }

    /**
     * Mass edition apply roles
     *
     * @return string|null|void
     * @throws CException
     * @throws CHttpException
     */
    public function actionBatchAddGroup()
    {
        if (!Permission::model()->hasGlobalPermission('users', 'update')) {
            throw new CHttpException(403, gT("You do not have permission to access this page."));
        }
        $aItems = json_decode(Yii::app()->request->getPost('sItems', '')) ?? [];
        $iUserGroupId = App()->request->getPost('addtousergroup');
        $oUserGroup = UserGroup::model()->findByPk($iUserGroupId);
        if (!$oUserGroup) {
            throw new CHttpException(404, gT("Group not found"));
        }
        /* check if have permission */
        if (!Permission::model()->hasGlobalPermission('superadmin', 'read') && $oUserGroup->requestEditGroup($oUserGroup->ugid, App()->getCurrentUserId())) {
            throw new CHttpException(403, gT("You do not have permission to access this page."));
        }

        $aResults = [];
        foreach ($aItems as $sItem) {
            $aResults[$sItem]['title'] = '';
            $model = $this->loadModel($sItem);
            $aResults[$sItem]['title'] = $model->users_name;
            if (!$oUserGroup->hasUser($sItem)) {
                $aResults[$sItem]['result'] = $oUserGroup->addUser($sItem);
            } else {
                $aResults[$sItem]['result'] = false;
                $aResults[$sItem]['error'] = gT('User is already a member of the group.');
            }
        }

        $tableLabels = array(gT('User ID'), gT('Username'), gT('Status'));

        Yii::app()->getController()->renderPartial(
            'ext.admin.survey.ListSurveysWidget.views.massive_actions._action_results',
            array(
                'aResults'     => $aResults,
                'successLabel' => gT('Usergroup updated'),
                'tableLabels' =>  $tableLabels
            )
        );
    }

    /**
     * Mass action to set expires
     *
     * @return string
     * @throws CException
     */
    public function actionBatchExpires()
    {
        if (!Permission::model()->hasGlobalPermission('users', 'update')) {
            throw new CHttpException(403, gT("You do not have permission to access this page."));
        }
        $aItems = json_decode(Yii::app()->request->getPost('sItems', '')) ?? [];
        $expires = App()->request->getPost('batchExpires');
        $formatdata = getDateFormatData(Yii::app()->session['dateformat']);
        Yii::import('application.libraries.Date_Time_Converter', true);
        if (trim((string) $expires) === "") {
            $expires = null;
        } else {
            $datetimeobj = DateTime::createFromFormat('!' . $formatdata['phpdate'] . ' H:i', $expires);
            if (!is_object($datetimeobj)) {
                throw new CHttpException(400, sprintf(gT('Invalid date, please use "%s" format.', 'unescaped'), $formatdata['phpdate'] . " H:i"));
            }
            $expires = $datetimeobj->format('Y-m-d H:i:s');
        }
        $aResults = [];
        foreach ($aItems as $sItem) {
            $aResults[$sItem]['title'] = '';
            $model = $this->loadModel($sItem);
            $aResults[$sItem]['title'] = $model->users_name;
            if (!$model->canEdit()) {
                $aResults[$sItem]['result'] = false;
                $aResults[$sItem]['error'] = gT("You are not allowed to update this user expiration date.");
                continue;
            }
            if ($model->uid == Yii::app()->user->id) {
                 $aResults[$sItem]['result'] = false;
                 $aResults[$sItem]['error'] = gT("You are not allowed to update your own expiration date.");
                 continue;
            }
            $model->expires = $expires;
            if ($model->save(true, ['expires'])) {
                $aResults[$sItem]['result'] = true;
            } else {
                $aResults[$sItem]['result'] = false;
                $aResults[$sItem]['error'] = gT('An error happened when setting the expiration date.');
            }
        }

        $tableLabels = array(gT('User ID'), gT('Username'), gT('Expires'));
        Yii::app()->getController()->renderPartial(
            'ext.admin.survey.ListSurveysWidget.views.massive_actions._action_results',
            array(
                'aResults'     => $aResults,
                'successLabel' => $expires ? gT('Expiration date updated') : gT('Expiration date deleted'),
                'tableLabels' =>  $tableLabels
            )
        );
    }

    /**
     * Mass edition apply roles
     *
     * @return string
     * @throws CException
     */
    public function actionBatchApplyRoles()
    {
        /* Need super admin roles */
        if (!Permission::model()->hasGlobalPermission('superadmin', 'create')) {
            throw new CHttpException(403, gT("You do not have permission to access this page."));
        }
        $aItems = json_decode(Yii::app()->request->getPost('sItems', '')) ?? [];
        $aUserRoleIds = Yii::app()->request->getPost('roleselector');
        $aResults = [];

        foreach ($aItems as $sItem) {
            $aResults[$sItem]['title'] = '';
            $model = $this->loadModel($sItem);
            $aResults[$sItem]['title'] = $model->users_name;
            if ($model->uid == Yii::app()->user->id) {
                $aResults[$sItem]['result'] = false;
                $aResults[$sItem]['error'] = gT("You are not allowed to update your own roles.");
                continue;
            }
            $userManager = new UserManager(Yii::app()->user, $model);
            if (!$userManager->canAssignRole()) {
                $aResults[$sItem]['result'] = false;
                $aResults[$sItem]['error'] = gT('You are not allowed to assign a role to this user.');
            } else {
                foreach ($aUserRoleIds as $iUserRoleId) {
                    $aResults[$sItem]['result'] = Permissiontemplates::model()->applyToUser($sItem, $iUserRoleId);
                }
            }
        }

        $tableLabels = array(gT('User ID'), gT('Username'), gT('Status'));

        Yii::app()->getController()->renderPartial(
            'ext.admin.survey.ListSurveysWidget.views.massive_actions._action_results',
            array(
                'aResults'     => $aResults,
                'successLabel' => gT('Role updated'),
                'tableLabels' =>  $tableLabels
            )
        );
    }

    /**
     * Takes ownership on user after confirmation
     *
     * @return void | string
     * @throws CException
     */
    public function actionTakeOwnership()
    {
        $userId = App()->request->getPost('userid');
        $oUser = User::model()->findByPk($userId);
        if (!$oUser) {
            App()->user->setFlash('error', gT("User does not exist"));
            $this->redirect(App()->request->urlReferrer);
        }
        $permission_superadmin = Permission::model()->hasGlobalPermission('superadmin', 'read');
        if (
            !($permission_superadmin
                && !(Permission::isForcedSuperAdmin($oUser->uid)
                    || $oUser->uid == App()->user->getId()
                )
                && $oUser->parent_id != App()->session['loginID']
            )
        ) {
            App()->user->setFlash('error', gT("Access denied"));
            $this->redirect(App()->createUrl("userManagement/index"));
        }

        $oUser->parent_id = App()->user->id;
        $oUser->save();
        $this->redirect(App()->createUrl("userManagement/index"));
    }

    /**
     * Attempt to delete the specified user while enforcing permission and safety checks.
     *
     * Performs permission checks (requires global `users:delete`), prevents deleting the current user,
     * blocks deletion of forced superadmin accounts, enforces ownership constraints for non-superadmins,
     * and prevents deletion if the user owns any surveys. If all checks pass, delegates deletion to
     * UserManager and returns the deletion result.
     *
     * @param int $uid The ID of the user to delete.
     * @return bool `true` if the user was deleted successfully, `false` otherwise.
     * @throws CException
     */
    public function deleteUser(int $uid): bool
    {
        if (!App()->getRequest()->getIsPostRequest()) {
            throw new CHttpException(400, gT('Your request is invalid.'));
        }
        $permission_users_delete = Permission::model()->hasGlobalPermission('users', 'delete');
        $permission_superadmin_read = Permission::model()->hasGlobalPermission('superadmin', 'read');
        if (!$permission_users_delete) {
            return false;
        }
        $userId = $uid;
        $oUser = User::model()->findByPk($userId);
        $currentUser = (int)App()->user->getId();
        if (!$oUser) {
            return false;
        }
        if ($permission_superadmin_read) {
            // Can't delete forced superadmins
            if (Permission::isForcedSuperAdmin($userId)) {
                return false;
            }
            // Can't delete yourself
            if ($userId === $currentUser) {
                return false;
            }
        }
        if (!$permission_superadmin_read) {
            // Can't delete yourself
            if ($userId === $currentUser) {
                return false;
            }
            // Dont have permission to delete users
            if (!$permission_users_delete) {
                return false;
            }
            // Can't delete users that are not owned by the current user
            if ((int)$oUser->parent_id !== $currentUser) {
                return false;
            }
            // Can't delete forced superadmins
            if (Permission::isForcedSuperAdmin($userId)) {
                return false;
            }
        }


        // Check if user owns a survey
        $aOwnedSurveys = Survey::model()->findAllByAttributes(['owner_id' => $userId]);
        if (count($aOwnedSurveys)) {
            return false;
        }

        $userManager = new UserManager();
        $result = $userManager->deleteUser($oUser);
        return $result->isSuccess();
    }

    /**
     * Returns the data model based on the primary key given in the GET variable.
     * If the data model is not found, an HTTP exception will be raised.
     * Why not a private function here ?
     *
     * @param int $id the ID of the model to be loaded
     *
     * @return User object
     * @throws CHttpException
     */
    public function loadModel(int $id): User
    {
        $model = User::model()->findByPk($id);

        if ($model === null) {
            throw new CHttpException(404, 'The requested page does not exist.');
        }

        return $model;
    }

    /**
     * Update admin-user
     * @todo : move to and private function, but need review unit test before.
     *
     * @param array $aUser array with user details
     * @return object user - updated user object
     * @throws CException
     */
    public function updateAdminUser(array $aUser): User
    {
        if (
            !App()->getRequest()->getIsPostRequest()
            && !(defined('PHP_ENV') && PHP_ENV == 'test') // For unit test
        ) {
            throw new CHttpException(400, gT('Your request is invalid.'));
        }
        $oUser = $this->loadModel($aUser['uid']);
        // Abort if logged in user has no access to this user.
        // Using same logic as User::getButtons().
        if (
            !$oUser->canEdit()
            || $aUser['uid'] == App()->user->id // To update self : must use personal settings
        ) {
            throw new CHttpException(403, gT("You do not have permission to access this page."));
        }

        $aUser['full_name'] = flattenText($aUser['full_name']); //to prevent xss ...
        $oUser->setAttributes($aUser);

        if (isset($aUser['password']) && $aUser['password']) {
            $oUser->password = password_hash((string) $aUser['password'], PASSWORD_DEFAULT);
        }
        $oUser->modified = date('Y-m-d H:i:s');
        $oUser->save();

        return  $oUser;
    }

    /**
     * This method creates a new admin user and returns success or error message
     *
     * @param array $aUser array with attributes from user model
     * @param boolean $sendEmail true if email should be send, false otherwise
     *
     * @return array
     *
     * @throws CException
     * @throws \PHPMailer\PHPMailer\Exception
     */
    private function createAdminUser(array $aUser, bool $sendEmail = true): array
    {
        if (!isset($aUser['uid']) || $aUser['uid'] == null) {
            $newUser = $this->createNewUser($aUser);
            $success = true;
            $sReturnMessage = gT('User successfully created', 'unescaped');

            if (Yii::app()->getConfig("sendadmincreationemail") && $sendEmail) {
                $user = User::model()->findByPk($newUser['uid']);
                $passwordManagement = new \LimeSurvey\Models\Services\PasswordManagement($user);
                $successData = $passwordManagement->sendPasswordLinkViaEmail(\LimeSurvey\Models\Services\PasswordManagement::EMAIL_TYPE_REGISTRATION);

                $sReturnMessage = $successData['sReturnMessage'];
                $success = $successData['success'];
            }

            if ($success) {
                $data = [
                    'success' => $success,
                    'message' => $sReturnMessage,
                    'href' => Yii::app()->getController()->createUrl('userManagement/userPermissions', ['userid' => $newUser['uid']]),
                    'modalsize' => 'modal-lg',
                ];
            } else {
                $data = [
                    'success' => $success,
                    'errors' => $sReturnMessage
                ];
            }

            return $data;
        }

        return [
            'success' => false,
            'errors' => CHtml::tag("p", array(), gT("Error: User was not created"))
        ];
    }

    /**
     * Create new user
     * @todo : move to private function
     *
     * @param array $aUser array with user details
     * @return array returns all attributes from model user as an array
     * @throws CException
     */
    public function createNewUser(array $aUser): array
    {
        if (!App()->getRequest()->getIsPostRequest()) {
            throw new CHttpException(400, gT('Your request is invalid.'));
        }
        if (!Permission::model()->hasGlobalPermission('users', 'create')) {
            return Yii::app()->getController()->renderPartial('/admin/super/_renderJson', [
                "data" => [
                    'success' => false,
                    'errors'  => gT("You do not have permission for this action."),
                ]
            ]);
        }

        $aUser['users_name'] = flattenText($aUser['users_name']);

        if (empty($aUser['users_name'])) {
            return Yii::app()->getController()->renderPartial('/admin/super/_renderJson', [
                "data" => [
                    'success' => false,
                    'errors'  => gT("A username was not supplied or the username is invalid."),
                ]
            ]);
        }

        if (User::model()->find("users_name=:users_name", array(':users_name' => $aUser['users_name']))) {
            return Yii::app()->getController()->renderPartial('/admin/super/_renderJson', [
                "data" => [
                    'success' => false,
                    'errors'  => gT("A user with this username already exists."),
                ]
            ]);
        }

        $event = new PluginEvent('createNewUser');
        $event->set('errorCode', AuthPluginBase::ERROR_NOT_ADDED);
        $event->set('errorMessageTitle', gT("Failed to add user"));
        $event->set('errorMessageBody', gT("Plugin is not active"));
        $event->set('preCollectedUserArray', $aUser);

        Yii::app()->getPluginManager()->dispatchEvent($event);

        if ($event->get('errorCode') != AuthPluginBase::ERROR_NONE) {
            return Yii::app()->getController()->renderPartial('/admin/super/_renderJson', [
                "data" => [
                    'success' => false,
                    'errors'  => $event->get('errorMessageTitle')
                                    . '<br/>'
                                    . $event->get('errorMessageBody'),
                    'debug'   => [
                        'title' => $event->get('errorMessageTitle'),
                        'body'  => $event->get('errorMessageBody'),
                        'code'  => $event->get('errorCode'),
                        'event' => $event
                    ],
                ]
            ]);
        }
        $iNewUID = $event->get('newUserID');
        // add default template to template rights for user
        Permission::model()->insertSomeRecords(
            array(
                'uid'         => $iNewUID,
                'permission'  => App()->getConfig('defaulttheme'),
                'entity'      => 'template',
                'read_p'      => 1,
                'entity_id'   => 0
            )
        );
        // add default usersettings to the user
        SettingsUser::applyBaseSettings($iNewUID);

        return User::model()->findByPk($iNewUID)->attributes;
    }

    /**
     * todo this should not be in a controller, find a better place for it (view)
     *
     *
     * @param array $errors
     *
     * @return string $errorDiv
     */
    private function renderErrors(array $errors): string
    {
        $errorDiv = '<ul class="list-unstyled">';
        foreach ($errors as $key => $error) {
            foreach ($error as $errorMessages) {
                $errorDiv .= '<li>' . print_r($errorMessages, true) . '</li>';
            }
        }
        $errorDiv .= '</ul>';
        return (string) $errorDiv;
    }

    /**
     * Creates a random unique username using prefix
     *
     * todo this should be moved to model user ...
     *
     * @param string $prefix the prefix to be used
     * @return string
     */
    protected function getRandomUsername(string $prefix): string
    {
        do {
            $rand = $this->getRandomString();
            $username = $prefix . '_' . substr($rand, rand(0, strlen($rand) - 6), 4);
            $oUser = User::model()->findByAttributes(['users_name' => $username]);
        } while ($oUser != null);
        return $username;
    }

    /**
     * Creates a random string
     *
     * todo REFACTORING this should be moved to model user ...see getRandomUsername
     *
     * @return string
     */
    protected function getRandomString(): string
    {
        if (is_callable('openssl_random_pseudo_bytes')) {
            $uiq = openssl_random_pseudo_bytes(128);
        } else {
            $uiq = decbin(rand(1000000, 9999999) * (rand(100, 999) . rand(100, 999) . rand(100, 999) . rand(100, 999)));
        }
        return hash('sha256', bin2hex($uiq));
    }

    /**
     * Adds permission to a users
     * Needs an array in the form of [PERMISSIONID][PERMISSION]
     *
     * todo REFACTORING this should be moved to model (user or permission)
     *
     * @param int $iUserId
     * @param array $aPermissionArray
     * @return array
     */
    protected function applyPermissionFromArray(int $iUserId, array $aPermissionArray): array
    {
        /**
         * Get current user permission to update only this permission
         * NEVER delete existing Permission !
         */
        $aGlobalPermissions = Permission::model()->getGlobalBasePermissions();
        /* Get only permission part */
        $aAllowedPermissions = array_map(
            function ($aGlobalPermission) {
                return array(
                    'create' => $aGlobalPermission['create'],
                    'read' => $aGlobalPermission['read'],
                    'update' => $aGlobalPermission['update'],
                    'delete' => $aGlobalPermission['delete'],
                    'import' => $aGlobalPermission['import'],
                    'export' => $aGlobalPermission['export'],
                );
            },
            $aGlobalPermissions
        );
        // superadmin permission always need create
        if (!Permission::model()->hasGlobalPermission('superadmin', 'create')) {
            unset($aAllowedPermissions['superadmin']);
        }
        $aCruds = array('create', 'read', 'update', 'delete', 'import', 'export');
        if (!Permission::model()->hasGlobalPermission('superadmin', 'read')) {
            // if not superadmin filter the available permissions as no admin may give more permissions than he owns
            $aFilteredPermissions = array();
            foreach ($aAllowedPermissions as $PermissionName => $aPermission) {
                foreach ($aPermission as $sPermissionKey => &$sPermissionValue) {
                    if (in_array($sPermissionKey, $aCruds) && !Permission::model()->hasGlobalPermission($PermissionName, $sPermissionKey)) {
                        $sPermissionValue = false;
                    }
                }
                // Only show a row for that permission if there is at least one permission he may give to other users
                if (
                    $aPermission['create'] || $aPermission['read'] || $aPermission['update']
                    || $aPermission['delete'] || $aPermission['import'] || $aPermission['export']
                ) {
                    $aFilteredPermissions[$PermissionName] = $aPermission;
                }
            }
            $aAllowedPermissions = $aFilteredPermissions;
        }
        $results = [];
        //Apply the permission array
        foreach ($aAllowedPermissions as $permissionKey => $aAllowedPermission) {
            /* get the current user permission or create */
            $oPermission = Permission::model()->find(
                "entity = :entity AND entity_id = :entity_id AND uid = :uid AND permission = :permission",
                array(
                    ":entity" => 'global',
                    ":entity_id" => 0,
                    ":uid" => $iUserId,
                    ":permission" => $permissionKey,
                )
            );
            if (empty($oPermission)) {
                $oPermission = new Permission();
                $oPermission->entity = 'global';
                $oPermission->entity_id = 0;
                $oPermission->uid = $iUserId;
                $oPermission->permission = $permissionKey;
            }
            foreach ($aAllowedPermission as $action => $havePermission) {
                if ($havePermission) {
                    $oPermission->setAttribute(
                        $action . '_p',
                        intval(!empty($aPermissionArray[$permissionKey][$action]))
                    );
                }
            }
            $results[$permissionKey] = [
                'descriptionData' => Permission::getGlobalPermissionData($permissionKey),
                'success' => $oPermission->save(),
                'storedValue' => $oPermission->attributes
            ];
        }
        return $results;
    }

    /**
     * CURRENTLY UNUSED
     * Add a tenplated permission to a users
     *
     * @param User $oUser
     * @param string $permissionclass
     * @param array $entity_ids
     * @return array
     */
    /*
    protected function applyPermissionTemplate($oUser, $permissionclass, $entity_ids = [])
    {
        if ($permissionclass == 'Group manager' && empty($entity_ids)) {
            return [
                "success" => false,
                "error" => "No survey selected for permissions",
            ];
        }
        $oCriteria = new CDbCriteria();
        $oCriteria->compare('uid', $oUser->uid);
        //Kill all Permissions
        $aPermissionsCurrently = Permission::model()->deleteAll($oCriteria);

        //Allow Login again
        Permission::model()->setGlobalPermission($oUser->uid, 'auth_db');

        $result = false;
        if (in_array($permissionclass, ['Survey manager', 'Scientist', 'combo'])) {
            $result = $this->applyGlobalPermissionTemplate($oUser, $permissionclass);
            $this->applyCorrectUsergroup($oUser->uid, ($permissionclass == 'combo' ? ['Survey manager', 'Scientist'] : [$permissionclass]));
        } elseif ($permissionclass == 'Group manager') {
            $result = $this->applySurveyPermissionTemplate($oUser, $permissionclass, $entity_ids);
            $this->applyCorrectUsergroup($oUser->uid, [$permissionclass]);
        }
        return $result;
    }*/

    /**
     * CURRENTLY UNUSED
     * Apply global permission from template
     *
     * @param User $oUser
     * @param string $permissionclass
     * @return array
     */
    /*
    protected function applyGlobalPermissionTemplate($oUser, $permissionclass)
    {
        $permissionTemplate = []; //PermissionTemplates::getPermissionTemplateBlock($permissionclass, $oUser->uid);
        $check = [];
        foreach ($permissionTemplate as $permission) {
            $oPermission = new Permission();
            array_walk($permission, function ($val, $key) use (&$oPermission) {
                $oPermission->$key = $val;
            });
            $check[$permission['permission']] = $oPermission->save(false);
        }
        return $check;
    }*/

    /**
     * CURRENTLY UNUSED
     * Add survey specific permissions by template
     *
     * @param User $oUser
     * @param string $permissionclass
     * @param array $entity_ids
     * @return array
     */
    /*
    protected function applySurveyPermissionTemplate($oUser, $permissionclass, $entity_ids)
    {
        $permissionTemplate = []; //PermissionTemplates::getPermissionTemplateBlock($permissionclass, $oUser->uid);
        $check = [];
        foreach ($permissionTemplate as $permission) {
            array_walk($entity_ids, function ($entity_id) use ($permission, &$check) {
                $oPermission = new Permission();
                $permission['entity_id'] = $entity_id;
                array_walk($permission, function ($val, $key) use (&$oPermission) {
                    $oPermission->$key = $val;
                });
                $check[$permission['permission'] . '/' . $entity_id] = $oPermission->save(false);
            });
        }
        return $check;
    }*/
}
