<?php

class Settings extends Cf_Controller
{
    public function __construct()
    {
        parent::__construct();

        setCurrentApplication('settings');

        $this->load->library('form_validation');
        $this->load->library('ion_auth');
        $this->load->library('ldap_api');
        $this->load->helper('form');
        $this->load->helper(['url', 'cf_directory_helper']);
        $this->load->model('settings_rest_model');
        $this->settings_rest_model->setRestClient($this->getRestClient());
    }

    public function index()
    {
        redirect('settings/preferences'); //default action
    }

    /**
     * @rbacName Preferences
     * @rbacGroup Settings
     * @rbacAlias settings.manage
     */
    public function manage()
    {
        $requiredjs = [['widgets/notes.js']];
        $this->carabiner->js($requiredjs);

        try {
            $settingsValue = $this->settings_rest_model->get_app_settings();
            unset($settingsValue['ldapPasswordIsSet']);
        } catch (Exception $e) {
            // for now set the setting value as empty array whn error
            show_error($e->getMessage());
        }

        $this->form_validation->set_rules('mode', 'Authentication mode', 'xss_clean|trim');
        $this->form_validation->set_rules(
            'bluehost_threshold_global',
            'Blue host horizon',
            'callback_validate_bluehost_threshold',
        );
        $this->form_validation->set_rules(
            'hostsCollisionsThreshold',
            'hostsCollisionsThreshold',
            'xss_clean|trim|required|greater_than[1]',
        );
        $this->form_validation->set_rules(
            'passwordComplexity',
            'passwordComplexity',
            'xss_clean|trim|required|greater_than_equal_to[0]|less_than[5]',
        );
        $this->form_validation->set_rules(
            'minPasswordLength',
            'minPasswordLength',
            'xss_clean|trim|required|greater_than_equal_to[8]',
        );
        $this->form_validation->set_rules(
            'passwordExpirationAfterResetHours',
            'passwordExpirationAfterResetHours',
            'xss_clean|trim',
        );
        $this->form_validation->set_rules('enforce2FA', 'enforce2FA', 'xss_clean|trim');
        $this->form_validation->set_error_delimiters('<span>', '</span><br/>');

        if ($this->form_validation->run($this) == true) {
            log_message('debug', 'manage(): Mission Portal validation - OK');

            $returnData = $this->_updateData($settingsValue); //pass OLD data to compare new and old pass
            if (is_array($returnData) && $returnData['status']) {
                $this->session->set_flashdata('message', [
                    'content' => $returnData['message'],
                    'type' => 'alert-success',
                ]);
                redirect('settings/preferences');
            } else {
                $feedback = [
                    'type' => 'alert-error',
                    'content' => $returnData['message'],
                ];
            }
        } else {
            log_message('debug', 'manage(): Mission Portal validation - Failed');
            $feedback = validation_errors();
        }

        $bluehost_threshold_min = set_value('bluehost_threshold_global', $settingsValue['blueHostHorizon'] / 60);
        $mode = $settingsValue['ldapEnabled'] ? 'ldap' : 'internal';

        $form_data = [
            'appemail' => set_value('appemail', $settingsValue['appemail']),
            'mode' => set_value('mode', $mode),
            'logLevel' => $settingsValue['logLevel'],
            'bluehost_threshold_global' => set_value('bluehost_threshold_global', $bluehost_threshold_min),
            'hostsCollisionsThreshold' => $settingsValue['hostsCollisionsThreshold'],
            'passwordComplexity' => $settingsValue['passwordComplexity'],
            'minPasswordLength' => $settingsValue['minPasswordLength'],
            'passwordExpirationAfterResetHours' => $settingsValue['passwordExpirationAfterResetHours'],
            'enforce2FA' => $settingsValue['enforce2FA'],
        ];

        $data = [
            'title' => $this->lang->line('mission_portal_title') . ' - Preferences',
            'message' => $feedback,
        ];

        $data = array_merge($form_data, $data);
        $this->template->load('template', 'appsetting/missionportalpref', $data);
    }

    /**
     * updates the setting data
     */
    public function _updateData($currentSettings = [])
    {
        $returnData = [];
        $returnData['status'] = false;
        $returnData['message'] = 'Data update failed';

        $blueHostHorizon = set_value('bluehost_threshold_global') * 60;

        $data = [];
        $data['logLevel'] = $this->input->post('api_log_level');
        $mode = set_value('mode');

        $data['blueHostHorizon'] = $blueHostHorizon; // store it in seconds
        $data['hostsCollisionsThreshold'] = (int) $this->input->post('hostsCollisionsThreshold');
        $data['passwordComplexity'] = (int) $this->input->post('passwordComplexity');
        $data['minPasswordLength'] = (int) $this->input->post('minPasswordLength');
        $data['passwordExpirationAfterResetHours'] = (int) $this->input->post('passwordExpirationAfterResetHours');
        $data['enforce2FA'] = (bool) $this->input->post('enforce2FA');

        try {
            log_message('debug', '_updateData(): Trying to update data via REST ');
            $this->settings_rest_model->updateData($data);
            $returnData['status'] = true;
            $returnData['message'] = 'Data saved successfully.';
        } catch (Exception $e) {
            log_message('debug', '_updateData(): REST - Could not save your settings');
            $returnData['status'] = false;
            $returnData['message'] = 'Could not save your settings. ' . $e->getMessage();
        }

        return $returnData;
    }

    public function required_valid_role($value)
    {
        if ($value == 'select') {
            $this->form_validation->set_message('required_valid_role', 'A valid role is required.');

            return false;
        } else {
            return true;
        }
    }

    public function validate_ldap_search($value)
    {
        $pattern = "/^\((.*)\)$/"; //  regex looks for start and end with parentheses

        if ($value && !preg_match($pattern, $value)) {
            $this->form_validation->set_message(
                'validate_ldap_search',
                'The search term should be enclosed in parentheses e.g \'(objectClass=inetOrgPerson)\'',
            );

            return false;
        }

        return true;
    }

    public function validate_bluehost_threshold($value)
    {
        $value = trim($value);
        if (empty($value)) {
            $this->form_validation->set_message(
                'validate_bluehost_threshold',
                'Unreachable host threshold field is required.',
            );

            return false;
        }

        if (!ctype_digit($value)) {
            $this->form_validation->set_message(
                'validate_bluehost_threshold',
                'Unreachable host threshold field must contain a valid number of minutes.',
            );

            return false;
        }

        if ($value < 5 || $value > 50400) {
            $this->form_validation->set_message(
                'validate_bluehost_threshold',
                'Unreachable host threshold field must be in range of 5 minutes - 5 weeks (50400 minutes).',
            );

            return false;
        }

        return true;
    }

    public function __db_check()
    {
        if ($this->input->post('mode') && strtolower($this->input->post('mode')) == 'internal') {
            return '|required';
        } else {
            return '';
        }
    }

    public function get_native_roles()
    {
        $roles = $this->ion_auth->get_roles();
        $rolesarr = [];

        foreach ($roles as $role) {
            $rolesarr[$role['name']] = $role['name'];
        }
        $formData = form_dropdown('admin_role', $rolesarr);
        respond_ok($formData);
    }

    //for setting up usertime zone on fly.
    public function setUserTimeZone()
    {
        $timezone = $this->input->post('userTimeZone');
        $timezone = $this->input->post('userTimeZone');
        $this->session->set_userdata('user_timezone', $timezone);
    }

    /**
     * @rbacName Host identifier
     * @rbacGroup Settings
     * @rbacAlias settings.systemIdentifier
     */
    public function hostManagement()
    {
        $data = [
            'title' => $this->lang->line('mission_portal_title') . ' - Host management',
        ];
        $this->template->load('template', 'appsetting/hostmanagement', $data);
    }

    public function hostManagementRedirect()
    {
        redirect('/settings/host-management');
    }

    public function about()
    {
        $this->load->model('license/license_model');
        $this->license_model->setRestClient($this->ion_auth->getRestClient());
        $data = [];

        try {
            $licenseDetails = $this->license_model->getLicenseDetails()->toArray();
            $data = [
                'title' => $this->lang->line('mission_portal_title') . ' - About CFEngine ',
                'details' => $licenseDetails,
                'licenseGranted' => isset($licenseDetails['granted']) ? $licenseDetails['granted'] : '',

                'licenseOwner' => isset($licenseDetails['owner']) ? $licenseDetails['owner'] : '',
                'licenseType' => isset($licenseDetails['licenseType']) ? $licenseDetails['licenseType'] : '',
                'expiry' => isset($licenseDetails['expires']) ? $licenseDetails['expires'] : '',
            ];
        } catch (Exception $e) {
            log_message('license error:' . $e->getMessage());
        }

        $this->template->load('template', 'appsetting/about', $data);
    }

    /**
     * @rbacName Version control repository
     * @rbacGroup Settings
     * @rbacAlias settings.vcs
     */
    public function vcs()
    {
        $this->load->model('settings_model');
        $this->load->model('vcsSettingsModel');

        $feedback = [];

        if ($this->input->post('update')) {
            try {
                $feedback = $this->handleFormUploadForGitSettings();
            } catch (Exception $e) {
                $output = $e->getMessage();
                $feedback = [
                    'type' => 'alert-error',
                    'content' => $output,
                ];
            }

            if ($feedback === true) {
                redirect('settings/vcs');
            }
        }

        try {
            $userSettings = $this->vcsSettingsModel->getSettings();
        } catch (Exception $e) {
            $userSettings = [];
        }

        $data = [
            'title' => $this->lang->line('mission_portal_title') . ' - Version control repository',
            'message' => $feedback,
            'hasSettings' => element_object('GIT_URL', $userSettings, null),
        ];

        $data = array_merge($userSettings, $data);

        $data['gitRefspec'] = !$data['gitRefspec'] ? 'master' : $data['gitRefspec'];

        $this->template->load('template', 'appsetting/vcs', $data);
    }

    /**
     * @rbacName Reset version control repository
     * @rbacGroup Settings
     * @rbacAlias settings.vcsReset
     */
    public function vcsReset()
    {
        $this->load->model('settings_model');
        $this->load->model('vcsSettingsModel');

        try {
            $this->vcsSettingsModel->deleteSettings(); // its admin setting
        } catch (Exception $e) {
            $this->session->set_flashdata('message', [
                'content' => 'There was some error deleting git configuration',
                'type' => 'alert-error',
            ]);
        }

        redirect('settings/vcs');
    }

    /**
     * @return array|bool|string
     */
    private function handleFormUploadForGitSettings()
    {
        $this->form_validation->set_rules('vcsType', 'VCS type', 'xss_clean|trim|required');
        $this->form_validation->set_rules('gitServer', 'git server url', 'xss_clean|trim|required');
        $this->form_validation->set_rules('gitRefspec', 'Git refspec', 'xss_clean|required');
        $this->form_validation->set_rules('projectSubdirectory', 'Project subdirectory', 'xss_clean|trim');
        $this->form_validation->set_rules('gitUsername', 'git username', 'xss_clean|trim');
        $this->form_validation->set_rules('gitPassword', 'git password', 'xss_clean|trim');

        if (
            $this->vcsSettingsModel->isSshUrl($this->input->post('gitServer')) &&
            empty($_FILES['gitPrivateKey']['name'])
        ) {
            $this->form_validation->set_rules('gitPrivateKey', 'Git private key', 'xss_clean|trim|required');
        }

        if ($this->form_validation->run() === true) {
            $gitPrivateKey =
                isset($_FILES['gitPrivateKey']) && !empty($_FILES['gitPrivateKey']['tmp_name'])
                    ? $this->vcsSettingsModel->readSshKey($_FILES['gitPrivateKey']['tmp_name'])
                    : '';

            try {
                $postData = [
                    'vcsType' => $this->input->post('vcsType', true),
                    'gitServer' => $this->input->post('gitServer', true),
                    'gitRefspec' => $this->input->post('gitRefspec', true),
                    'projectSubdirectory' => $this->input->post('projectSubdirectory', true),
                    'gitUsername' => $this->input->post('gitUsername', true),
                    'gitPassword' => $this->input->post('gitPassword', true),
                    'gitPrivateKey' => $gitPrivateKey,
                ];

                $this->vcsSettingsModel->createSettings($postData);

                $this->session->set_flashdata('message', [
                    'content' => 'The git repository has been successfully configured.',
                    'type' => 'alert-success',
                ]);
                $feedback = true;
            } catch (Exception $e) {
                $message = json_decode($e->getMessage(), true);
                log_message('error', $message);
                $feedback = [
                    'type' => 'alert-error',
                    'content' => $message,
                ];
            }
        } else {
            $feedback = validation_errors();
        }

        return $feedback;
    }

    /**
     * @rbacName Active tokens
     * @rbacGroup Settings
     * @rbacAlias settings.online_users
     */
    public function online_users()
    {
        $data = [
            'title' => $this->lang->line('mission_portal_title') . ' - Logged in users',
        ];

        $this->load->model('Authentication_model');

        $data['activeSessions'] = $this->Authentication_model->getActiveSession();

        $this->template->load('template', 'appsetting/users_online', $data);
    }

    /**
     * @rbacName Revoke tokens
     * @rbacGroup Settings
     * @rbacAlias settings.revoke_token
     */
    public function revoke_token($tokens)
    {
        if (!$this->isDeleteMethod()) {
            respond_not_acceptable();
            return;
        }

        $tokens = urldecode($tokens);
        $tokens = explode('|', $tokens);
        $this->load->model('Authentication_model');
        try {
            $this->Authentication_model->unsetToken($tokens);
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), 'ERROR revoking auth tokens. Error:: ' . $e->getMessage());
            show_error('Unexpected error while performing token revoke.');
        }
        respond_ok();
    }

    /**
     * @rbacName View
     * @rbacGroup Notification scripts
     * @rbacAlias settings.notification_scripts
     */
    public function notification_scripts($action = null, $id = null)
    {
        if (!empty($action) && ($action == 'edit' || $action == 'add')) {
            redirect('settings/notification-scripts-update/' . $id);
        } elseif (!empty($action) && $action == 'delete') {
            redirect('settings/notification-scripts-delete/' . $id);
        }

        $data = [
            'title' => $this->lang->line('mission_portal_title') . ' - Custom notification scripts',
        ];

        $this->load->model('dashboard/alerts_model');

        try {
            $data['scripts'] = $this->alerts_model->getAllNotificationScripts();
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), 'Error getting scripts:: ' . $e->getMessage());
            show_error('Unexpected error while retrieving list of custom scripts.');
        }

        $this->template->load('template', 'appsetting/notification_scripts', $data);
    }

    /**
     * @rbacName Delete
     * @rbacGroup Notification scripts
     * @rbacAlias settings.notification_scripts_delete
     */
    public function notification_scripts_delete($id)
    {
        if (!$this->isDeleteMethod()) {
            respond_not_acceptable();
            return;
        }

        $this->load->model('dashboard/alerts_model');
        $this->alerts_model->deleteAlertScript($id);
        respond_ok();
    }

    /**
     * @rbacName Create
     * @rbacGroup Notification scripts
     * @rbacAlias settings.notification_scripts_update
     */
    public function notification_scripts_update($id = null)
    {
        $this->load->model('dashboard/alerts_model');

        $data = [
            'title' => $this->lang->line('mission_portal_title') . ' - Custom notification scripts',
        ];

        $feedback = false;
        $script = null;

        try {
            if (!empty($id)) {
                $script = $this->alerts_model->getNotificationScript($id);
            }
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), 'Error getting scripts:: ' . $e->getMessage());
            show_error('Unexpected error while retrieving details.');
        }

        $hasObjectValue = !empty($script);
        $data['script'] = $script;

        if ($this->input->post('update')) {
            try {
                $feedback = $this->handleFormUploadForNotificationScripts($script);
            } catch (Exception $e) {
                $output = $e->getMessage();
                $feedback = [
                    'type' => 'alert-error',
                    'content' => $output,
                ];
            }

            if ($feedback === true) {
                redirect('settings/notification-scripts');
            }
        }

        $formData = [
            'scriptname' => set_value('scriptname', $hasObjectValue ? $script->getName() : null),
            'scriptdescription' => set_value('scriptdescription', $hasObjectValue ? $script->getDescription() : null),
            'scriptfilename' => set_value('scriptfilename', $hasObjectValue ? $script->getScriptFileName() : null),
            'id' => set_value('id', $hasObjectValue ? $script->getId() : null),
        ];

        $data['message'] = $feedback;
        $data['form_data'] = $formData;

        $this->template->load('template', 'appsetting/notification_scripts_update', $data);
    }

    private function handleFormUploadForNotificationScripts($script)
    {
        $feedback = true;
        $this->form_validation->set_rules('scriptname', 'name', 'xss_clean|trim|required');
        $this->form_validation->set_rules('scriptdescription', 'description', 'xss_clean|trim');
        if (empty($script) && empty($_FILES['userfile']['name'])) {
            $this->form_validation->set_rules('userfile', 'file', 'required');
        }

        if ($this->form_validation->run() === true) {
            try {
                $data['name'] = $_POST['scriptname'];
                $data['description'] = $_POST['scriptdescription'];
                $uploadOk = true;
                if (!empty($_FILES['userfile']['name'])) {
                    //try upload the files

                    $config['upload_path'] = $this->config->item('notification_script_dir');
                    $config['allowed_types'] = '*';

                    if (!file_exists($config['upload_path'])) {
                        if (!mkdir($config['upload_path'], 0755)) {
                            throw new Exception('Cannot make directory on path: ' . $config['upload_path']);
                        }
                    }

                    $this->load->library('upload', $config);

                    if (!$this->upload->do_upload()) {
                        $feedback = [
                            'type' => 'alert-error',
                            'content' => $this->upload->display_errors(),
                        ];
                        $uploadOk = false;
                    } else {
                        $fileData = $this->upload->data();
                        $data['scriptFileName'] = $fileData['file_name'];
                        chmod($fileData['full_path'], 0750); // make it executable
                    }
                }
                if ($uploadOk) {
                    if ($script) {
                        $this->alerts_model->updateAlertScripts($script, $data);
                    } else {
                        $this->alerts_model->insertAlertScripts($data);
                    }
                }
            } catch (Exception $e) {
                $message = $e->getMessage();
                log_message('error', $message);
                $feedback = [
                    'type' => 'alert-error',
                    'content' => $message,
                ];
            }
        } else {
            $feedback = validation_errors();
        }

        return $feedback;
    }

    /**
     * @rbacName Update mail settings
     * @rbacGroup Settings
     * @rbacAlias settings.mail
     */
    public function mail()
    {
        $this->load->model('MailSettingsModel');
        $settings = $this->MailSettingsModel->getSettings();
        $data = [
            'title' => $this->lang->line('mission_portal_title') . ' - Mail settings CFEngine ',
        ];
        $data = array_merge($settings, $data);

        if ($this->input->method() == 'post') {
            $data = array_merge($data, $this->input->post());

            $this->form_validation->set_rules('protocol', 'Mail protocol', 'required');
            $this->form_validation->set_rules(
                'max_email_attach_size',
                'Max email attachment size (MB)',
                'required|integer|less_than_equal_to[100]',
            );
            $this->form_validation->set_rules('default_from_email', 'Default from email', 'valid_email');
            $this->form_validation->set_rules('email_link_domain', 'Email links domain', 'valid_url');

            if ($this->input->post('protocol') == 'smtp') {
                $this->form_validation->set_rules('smtp_host', 'SMTP host', 'required');
                $this->form_validation->set_rules('smtp_user', 'SMTP user', 'required');
                $this->form_validation->set_rules('smtp_port', 'SMTP port', 'required');
            }

            if ($this->form_validation->run($this) == true) {
                $updateData = $this->input->post();
                unset($updateData['submit']);

                if (
                    isset($updateData['mailpath']) &&
                    !empty($updateData['mailpath']) &&
                    !$this->MailSettingsModel->validateMailpath($updateData['mailpath'])
                ) {
                    $this->session->set_flashdata('message', [
                        'content' =>
                            'Invalid mailpath: must be an existing file in an allowed directory: ' .
                            implode(', ', $this->MailSettingsModel->allowedMailpathDirs),
                        'type' => 'alert-error',
                    ]);
                    redirect('settings/mail');
                    return;
                }

                $this->MailSettingsModel->updateSettings($this->input->post());
                $changedFields = deduct_credentials(array_diff_assoc($updateData, $settings), [
                    'smtp_pass',
                    'smtp_user',
                ]);
                log_audit(
                    actor: $this->session->userdata('username'),
                    objectType: AUDIT_LOG_TYPE_SETTINGS,
                    action: AUDIT_LOG_ACTION_UPDATE,
                    objectName: AUDIT_LOG_MAIL_SETTINGS_NAME,
                    details: ['Updated mail settings', ['changed fields' => $changedFields]],
                );
                if ($this->input->is_ajax_request()) {
                    return true;
                }
            } else {
                $errors = validation_errors();
                if ($this->input->is_ajax_request()) {
                    $this->output->set_status_header(419, 'validation error');
                    echo $errors['content'];

                    return false;
                }
                $this->session->set_flashdata('message', validation_errors());
            }
        }

        $this->template->load('template', 'appsetting/mail', $data);
    }

    public function ai()
    {
        if (!isActionAllowed('ai-settings.update')) {
            redirect('auth/permission_deny', 'refresh');
            exit();
        }

        $data = ['title' => $this->lang->line('mission_portal_title') . ' - AI settings'];
        $this->template->load('template', 'appsetting/ai', $data);
    }

    public function profile()
    {
        $requiredjs = [['dist/profile.bundle.js']];

        $this->carabiner->js($requiredjs, $requiredjs, $combine = true, $minify = false);

        $this->load->model('ProfileModel');
        $userName = $this->session->userdata('username');

        if ($this->input->method() == 'post') {
            $postData = $this->input->post();
            if (isset($postData['time_zone'])) {
                $this->ProfileModel->updateTimeZone($postData, $userName);
            }

            $neverAsk = isset($postData['neverAskAgain']) ? (int) $postData['neverAskAgain'] : 1;
            $this->ProfileModel->setNeverAskTimeZoneChange($neverAsk, $userName);

            $useBrowserTime = isset($postData['useBrowserTime']) ? (int) $postData['useBrowserTime'] : 0;
            $this->ProfileModel->setUseBrowserTime($useBrowserTime, $userName);

            redirect('settings/profile');
        }

        $data = [
            'title' => $this->lang->line('mission_portal_title') . ' - My profile CFEngine',
            'timeZonesList' => $this->ProfileModel->getTimeZoneList(),
            'profile' => $this->ProfileModel->getProfileData($userName),
            'neverAskAgain' => $this->ProfileModel->getNeverAskTimeZoneChange($userName),
            'useBrowserTime' => $this->ProfileModel->getUseBrowserTime($userName),
        ];

        $this->template->load('template', 'appsetting/profile', $data);
    }

    /**
     * @rbacName Send test mail
     * @rbacGroup Settings
     * @rbacAlias settings.sendTestEmail
     */
    public function sendTestEmail()
    {
        $this->load->model('MailSettingsModel');
        $defaultEmail = $this->MailSettingsModel->getSetting('default_from_email');

        $data = [
            'title' => $this->lang->line('mission_portal_title') . ' - Send test email ',
            'defaultEmail' => $defaultEmail,
        ];

        $this->form_validation->set_rules('from', 'From', 'required|valid_email');
        $this->form_validation->set_rules('to', 'To', 'required|valid_email');

        if ($this->input->post() && $this->form_validation->run($this) == true) {
            $this->load->library('email');
            $this->email->from($this->input->post('from'));
            $this->email->to($this->input->post('to'));
            $this->email->subject('CFEngine Enterprise hub test email');
            $this->email->message(
                'Test email sent from ' . gethostname() . ' by user ' . $this->session->userdata('username') . '.',
            );
            $result = $this->email->send();
            if (!$result) {
                $data['debugInfo'] = $this->email->print_debugger();
                log_message('debug', 'send test email error:' . $data['debugInfo']);
            } else {
                $data['debugInfo'] = 'Email successfully sent';
            }
        } else {
            $this->session->set_flashdata('message', validation_errors());
        }
        $data = array_merge($data, $this->input->post());
        $this->template->load('template', 'appsetting/sendTestEmail', $data);
    }

    public const ADMIN_USERNAME_KEY = 'admin_username';
    public const ADMIN_PASSWORD_KEY = 'admin_password';

    /**
     * @rbacName Update LDAP auth settings
     * @rbacGroup Settings
     * @rbacAlias settings.ldap_api_settings
     */
    public function ldap_api_settings()
    {
        $ldapApiUrl = $this->config->item('ldapApiUrl');
        $pestClient = new Http_client(['base_url' => $ldapApiUrl]);
        $apiServerLDAPSettings = $this->ldap_api->getSettings($pestClient);
        $is_ldap_available = $this->ldap_api->testLdapUrl($pestClient);

        $settingsValue = $this->settings_rest_model->get_app_settings();

        if (!$is_ldap_available) {
            log_message(
                'error',
                'Could not communicate with LDAP API at ldapApiUrl=' .
                    $ldapApiUrl .
                    ' check configuration in /var/cfengine/share/GUI/application/config/appsettings.php',
            );
        }

        $settingsValue['ldap_roles_list_to_sync'] = isset($settingsValue['ldap_roles_list_to_sync'])
            ? implode(PHP_EOL, $settingsValue['ldap_roles_list_to_sync'])
            : '';

        $data = [
            'title' => $this->lang->line('mission_portal_title') . ' - Authentication settings',
            'is_correct_api_url' => (bool) $apiServerLDAPSettings,
            'is_ldap_available' => $this->ldap_api->testLdapUrl($pestClient),
            'mode' => $settingsValue['ldapEnabled'] ? 'ldap' : 'internal',
            'settings' => $settingsValue,
        ];

        $post = $this->input->post();
        if ($post) {
            $this->form_validation->set_rules('domain_controller', 'Ldap Host', 'required');
            $this->form_validation->set_rules('port', 'Port', 'required|integer');
            $this->form_validation->set_rules('base_dn', 'Base DN', 'required');
            $this->form_validation->set_rules('login_attribute', 'Login attribute', 'required');
            $this->form_validation->set_rules('ldap_filter', 'Ldap filter', 'trim');
            $this->form_validation->set_rules('logout_users', 'Log out all LDAP users', 'trim');
            if (empty($this->input->post('anonymous_bind'))) {
                $this->form_validation->set_rules(self::ADMIN_USERNAME_KEY, 'LDAP bind user name', 'required');
                $this->form_validation->set_rules(self::ADMIN_PASSWORD_KEY, 'LDAP bind password', 'required');
            } else {
                unset($post[self::ADMIN_USERNAME_KEY]);
                unset($post[self::ADMIN_PASSWORD_KEY]);
            }
            $this->form_validation->set_rules('anonymous_bind', 'Anonymous bind', 'trim');
            $this->form_validation->set_rules('group_attribute', 'User group attribute', 'trim');
            $this->form_validation->set_rules(
                'settings[ldap_enable_role_syncing]',
                'settings[ldap_enable_role_syncing]',
                'trim',
            );
            $this->form_validation->set_rules(
                'settings[ldap_roles_list_to_sync]',
                'List of groups to sync',
                'trim|regex_match[/^[A-Za-z0-9_ \-\.\s]+$/]',
            );
            $this->form_validation->set_rules(
                'settings[ldap_perform_sync_on_login]',
                'settings[ldap_perform_sync_on_login]',
                'trim',
            );
            $this->form_validation->set_rules(
                'settings[ldap_remove_role_on_syncing]',
                'settings[ldap_remove_role_on_syncing]',
                'trim',
            );
            $this->form_validation->set_message(
                'regex_match',
                'The {field} field contains invalid characters. Only letters, spaces, dashes, numbers, dots and underscores are allowed.',
            );
            if ($this->form_validation->run($this) == true) {
                $logoutExternalUsers = isset($post['logout_users']) && $post['logout_users'] === '1';
                // update general LDAP settings
                $settingsToSave = $post['settings'];
                $settingsToSave['ldap_enable_role_syncing'] = key_exists('ldap_enable_role_syncing', $settingsToSave);
                $settingsToSave['ldap_perform_sync_on_login'] = key_exists(
                    'ldap_perform_sync_on_login',
                    $settingsToSave,
                );
                $settingsToSave['ldap_remove_role_on_syncing'] = key_exists(
                    'ldap_remove_role_on_syncing',
                    $settingsToSave,
                );
                $settingsToSave['ldap_roles_list_to_sync'] = key_exists('ldap_roles_list_to_sync', $settingsToSave)
                    ? json_encode(explode("\r\n", $settingsToSave['ldap_roles_list_to_sync']))
                    : '';
                $restModelResponse = $this->settings_rest_model->updateData($settingsToSave);

                if (!$restModelResponse['success']) {
                    $this->handleUpdateError($restModelResponse);
                    redirect('settings/authentication');
                }

                unset($post['settings']);
                $response = $this->ldap_api->saveSettings($pestClient, $post);
                if (!$response['success']) {
                    $message = [
                        'content' => ucfirst($response['message']),
                        'type' => 'alert-error',
                    ];
                } else {
                    $message = [
                        'content' => 'Settings were successfully saved.',
                        'type' => 'alert-success',
                    ];

                    if ($logoutExternalUsers) {
                        $this->load->model('authentication_model');
                        $this->authentication_model->revokeExternalUsersTokens();
                    }

                    // exclude anonymous_bind and submit properties as they are not actual fields
                    $changedFields = array_filter(
                        array_diff_assoc($post, $apiServerLDAPSettings),
                        fn($key) => !in_array($key, ['submit', 'anonymous_bind']),
                        ARRAY_FILTER_USE_KEY,
                    );
                    $changedFields = deduct_credentials($changedFields, [
                        self::ADMIN_PASSWORD_KEY,
                        self::ADMIN_USERNAME_KEY,
                    ]);

                    log_audit(
                        actor: $this->session->userdata('username'),
                        objectType: AUDIT_LOG_TYPE_SETTINGS,
                        action: AUDIT_LOG_ACTION_UPDATE,
                        objectName: AUDIT_LOG_AUTHENTICATION_SETTINGS_NAME,
                        details: ['Updated authentication settings.', ['changed fields' => $changedFields]],
                    );
                }
                $apiServerLDAPSettings = $this->input->post();
                $this->session->set_flashdata('message', $message);
                redirect('settings/authentication');
            } else {
                $apiServerLDAPSettings = $this->input->post();
                $this->session->set_flashdata('message', validation_errors());
            }
        }

        $data = array_merge($apiServerLDAPSettings, $data);
        $data[self::ADMIN_PASSWORD_KEY] =
            isset($data[self::ADMIN_PASSWORD_KEY]) && $data[self::ADMIN_PASSWORD_KEY] != ''
                ? 'Password is set'
                : 'Password is not set';
        $data['anonymous_bind'] = isset($data['anonymous_bind']) && $data['anonymous_bind'];
        $this->template->load('template', 'appsetting/auth_settings', $data);
    }

    private function handleUpdateError($response): void
    {
        $formatErrorMessages = function ($errors): string {
            if (!is_array($errors) || empty($errors)) {
                return '';
            }

            $messages = array_map(function ($error) {
                return is_array($error) ? implode(': ', array_filter($error)) : (string) $error;
            }, $errors);

            return implode('<br />', array_filter($messages));
        };

        $content = $formatErrorMessages($response['errors'] ?? []);
        $this->session->set_flashdata('message', [
            'content' => $content ?: 'Failed to update settings.',
            'type' => 'alert-error',
        ]);
    }

    /**
     * @rbacName Change authentication method
     * @rbacGroup Settings
     * @rbacAlias settings.saveAuthenticationMethod
     */
    public function saveAuthenticationMethod()
    {
        $returnData = $data = [];
        if (!empty($this->input->post('mode'))) {
            $data['ldapEnabled'] = boolval($this->input->post('mode') == 'ldap');
        }

        try {
            log_message('debug', 'saveAuthenticationMethod(): Trying to update data via REST ');
            $this->settings_rest_model->updateData($data);
            $returnData['status'] = true;
            $returnData['message'] = 'Data saved successfully.';
        } catch (Exception $e) {
            log_message('debug', 'saveAuthenticationMethod(): REST - Could not save your settings');
            $returnData['status'] = false;
            $returnData['message'] = 'Could not save your settings. ' . $e->getMessage();
        }
        echo respond_ok(json_encode($returnData));
    }
}
