<?php

namespace ProfilePress\Core\Classes;

use ProfilePress\Libsodium\UserModeration\UserModeration;
use ProfilePress\Libsodium\UserModeration\UserModerationNotification;
use WP_Error;

class RegistrationAuth
{
    /**
     * Wrapper function for call to the welcome email class
     *
     * @param int $user_id
     * @param string $password
     * @param string $form_id
     */
    public static function send_welcome_email($user_id, $password = '', $form_id = '')
    {
        $status = apply_filters('ppress_activate_send_welcome_email', ppress_get_setting('welcome_message_email_enabled', 'on'), $user_id, $form_id);

        if ($status == 'on') {

            do_action('ppress_before_send_welcome_mail', $user_id, $form_id);

            new WelcomeEmailAfterSignup($user_id, $password);

            do_action('ppress_after_send_welcome_mail', $user_id, $form_id);
        }
    }

    /**
     *
     * Wrapper function for call to the automatic login after reg function
     *
     * @param int $user_id
     * @param int $form_id
     * @param string $redirect redirect url after registration
     *
     * @return mixed
     */
    public static function auto_login_after_reg($user_id, $form_id, $redirect)
    {
        if (!empty($redirect)) {
            return Autologin::initialize($user_id, $form_id, $redirect);
        }

        $auto_login_option = apply_filters('ppress_activate_auto_login_after_signup', ppress_get_setting('set_auto_login_after_reg', ''), $form_id, $user_id);

        if ($auto_login_option == 'on') {
            return Autologin::initialize($user_id, $form_id);
        }
    }

    /**
     * Perform redirect after registration without logging the user in.
     *
     * @param int $form_id
     * @param string $no_login_redirect URL to redirect to.
     *
     * @return array
     */
    public static function no_login_redirect_after_reg($form_id, $no_login_redirect)
    {
        esc_url_raw($no_login_redirect);

        do_action('ppress_before_no_login_redirect_after_reg', $no_login_redirect, $form_id);
        if (wp_doing_ajax()) {
            // we are returning array to uniquely identify redirect.
            return [$no_login_redirect];
        }

        nocache_headers();

        wp_safe_redirect($no_login_redirect);
        exit;
    }

    /**
     * Register new users
     *
     * @param array $post user form submitted data
     * @param int $form_id Registration builder ID
     * @param string $redirect URL to redirect to after registration.
     *
     * @param bool $is_melange
     * @param string $no_login_redirect
     *
     * @return string|mixed|void
     */
    public static function register_new_user($post, $form_id = 0, $redirect = '', $is_melange = false, $no_login_redirect = '')
    {
        if (!get_option('users_can_register')) return;

        $files = $_FILES;

        // create an array of acceptable userdata for use by wp_insert_user
        $valid_userdata = array(
            'reg_username',
            'reg_password',
            'reg_password2',
            'reg_email2',
            'reg_password_present',
            'reg_email',
            'reg_website',
            'reg_nickname',
            'reg_display_name',
            'reg_first_name',
            'reg_last_name',
            'reg_bio',
            'reg_select_role',
        );

        // get the data for userdata
        $segregated_userdata = array();

        // loop over the $_POST data and create an array of the wp_insert_user userdata
        foreach ($post as $key => $value) {
            if ($key == 'reg_submit') {
                continue;
            }

            if (in_array($key, $valid_userdata)) {

                if (in_array($key, ['reg_email', 'reg_email2'])) {
                    $segregated_userdata[$key] = sanitize_email($value);
                    continue;
                }

                // sanitize_textarea_field is used to preserve any line breaks
                $segregated_userdata[$key] = ppress_strip_shortcodes_clean($value);
            }
        }

        $email = $segregated_userdata['reg_email'] ?? '';

        $email2 = $segregated_userdata['reg_email2'] ?? null;

        // get convert the form post data to userdata for use by wp_insert_users
        $username = $segregated_userdata['reg_username'] ?? '';

        // Handle username creation when username requirement is disabled.
        if (ppress_is_signup_form_username_disabled($form_id, $is_melange)) {
            $username = sanitize_user(current(explode('@', $email)), true);
            // Ensure username is unique.
            $append = 1;
            $o_username = $username;
            while (username_exists($username)) {
                $username = $o_username . $append;
                $append++;
            }
        }

        $username = apply_filters('ppress_registration_username_value', $username, $form_id);

        $password = apply_filters('ppress_registration_password_value', $segregated_userdata['reg_password'] ?? '', $form_id);

        $flag_to_send_password_reset = false;

        // if the reg_password field isn't present in registration, generate a password for the user and set a flag to send a password reset message
        if (empty($password) && (empty($segregated_userdata['reg_password_present']) || $segregated_userdata['reg_password_present'] != 'true')) {
            $password = wp_generate_password(24);
            $flag_to_send_password_reset = apply_filters('ppress_enable_auto_send_password_reset_flag', true, $form_id);
        }

        $password2 = $segregated_userdata['reg_password2'] ?? null;
        $website = $segregated_userdata['reg_website'] ?? '';
        $nickname = $segregated_userdata['reg_nickname'] ?? '';
        $display_name = $segregated_userdata['reg_display_name'] ?? '';
        $first_name = $segregated_userdata['reg_first_name'] ?? '';
        $last_name = $segregated_userdata['reg_last_name'] ?? '';
        $bio = $segregated_userdata['reg_bio'] ?? '';
        $role = $segregated_userdata['reg_select_role'] ?? '';

        // real uer data
        $real_userdata = array(
            'user_login' => $username,
            'user_pass' => $password,
            'user_email' => apply_filters('ppress_registration_email_value', $email, $form_id),
            'user_url' => apply_filters('ppress_registration_website_value', $website, $form_id),
            'nickname' => apply_filters('ppress_registration_nickname_value', $nickname, $form_id),
            'display_name' => apply_filters('ppress_registration_display_name_value', $display_name, $form_id),
            'first_name' => apply_filters('ppress_registration_first_name_value', $first_name, $form_id),
            'last_name' => apply_filters('ppress_registration_last_name_value', $last_name, $form_id),
            'description' => apply_filters('ppress_registration_bio_value', $bio, $form_id),
        );

        if (!empty($role)) {
            // acceptable defined roles in reg-select-role shortcode.
            $accepted_role = (array)self::acceptable_defined_roles($form_id);

            if ($role != 'administrator' && in_array($role, $accepted_role)) {
                $real_userdata['role'] = $role;
            }
        } else {

            $builder_role = FormRepository::get_form_meta($form_id, FormRepository::REGISTRATION_TYPE, FormRepository::REGISTRATION_USER_ROLE);

            if (!empty($builder_role)) {
                // only set user role if the registration form has one set
                // otherwise no role is set for the user thus wp_insert_user will use the default user role set in Settings > General
                $real_userdata['role'] = $builder_role;
            }
        }

        /* start filter Hook */
        $reg_errors = new WP_Error();

        // --------START ---------   validation for required fields ----------------------//
        // loop through required fields and throw error if any is empty
        if (!empty($_POST['required-fields']) && is_array($_POST['required-fields'])) {
            foreach ($_POST['required-fields'] as $key => $value) {

                // ppressPOST_var($key) === 'false' for checkbox field because if unchecked, default value is "false".
                if ((empty($_POST[$key]) || ppressPOST_var($key) === 'false') && empty($_FILES[$key])) {
                    $reg_errors->add('required_field_empty', sprintf(__('%s field is required', 'wp-user-avatar'), sanitize_text_field($value)));
                    // stop looping if a required field is found empty.
                    break;
                }
            }
        }
        // --------END ---------   validation for required fields ----------------------//

        if (!validate_username($username)) {
            $reg_errors->add('invalid_username', esc_html__('<strong>ERROR</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.', 'wp-user-avatar'));
        }

        if (!is_email($real_userdata['user_email'])) {
            $reg_errors->add('invalid_email', esc_html__('Email address is not valid', 'wp-user-avatar'));
        }

        if (isset($password2) && ($password != $password2)) {
            $reg_errors->add('password_mismatch', esc_html__('Passwords do not match', 'wp-user-avatar'));
        }

        if (isset($email2) && ($email != $email2)) {
            $reg_errors->add('email_mismatch', esc_html__('Email addresses do not match', 'wp-user-avatar'));
        }

        if (isset($post['pp_enforce_password_meter']) && ($post['pp_enforce_password_meter'] != '1')) {
            $reg_errors->add('password_weak', esc_html__('Password is not strong', 'wp-user-avatar'));
        }

        // get the data for use by update_meta
        $custom_usermeta = array();

        if (ExtensionManager::is_premium()) {
            // loop over the $_POST data and create an array of the invalid userdata/ custom usermeta
            foreach ($post as $key => $value) {

                if ($key == 'reg_submit' || in_array($key, ppress_reserved_field_keys())) continue;

                if (!in_array($key, $valid_userdata)) {

                    if (in_array($key, array_keys(ppress_custom_fields_key_value_pair(true)))) {
                        $custom_usermeta[$key] = is_array($value) ? array_map('sanitize_textarea_field', $value) : sanitize_textarea_field($value);
                    }
                }
            }
        }

        // merge real data(for use by wp_insert_user()) and custom fields data
        // $real_userdata comes second so custom user meta won't override it.
        $user_data = array_merge($custom_usermeta, $real_userdata);

        /* Begin Filter Hook */
        // call validate reg from function
        $reg_form_errors = apply_filters('ppress_registration_validation', $reg_errors, $form_id, $user_data, $is_melange);
        if (is_wp_error($reg_form_errors) && $reg_form_errors->get_error_code() != '') {
            return '<div class="profilepress-reg-status">' . $reg_form_errors->get_error_message() . '</div>';
        }
        /* End Filter Hook */

        // --------START ---------   validation for file upload ----------------------//
        $uploads = FileUploader::init();
        $upload_errors = '';
        if (!empty($uploads)) {
            foreach ($uploads as $field_key => $uploaded_filename_or_wp_error) {
                if (is_wp_error($uploaded_filename_or_wp_error)) {
                    $upload_errors .= $uploaded_filename_or_wp_error->get_error_message() . '<br/>';
                }
            }

            if (!empty($upload_errors)) {
                return "<div class='profilepress-reg-status'>$upload_errors</div>";
            }
        }
        // --------END ---------   validation for file upload ----------------------//


        // --------START ---------   validation for avatar upload ----------------------//
        if (!empty($files['reg_avatar']['name'])) {
            $upload_avatar = ImageUploader::process($files['reg_avatar']);

            if (is_wp_error($upload_avatar)) {
                return "<div class='profilepress-reg-status'>" . $upload_avatar->get_error_message() . "</div>";
            }
        }
        // --------END ---------   validation for avatar upload ----------------------//


        // --------START ---------   validation for cover photo upload ----------------------//
        if (!empty($files['reg_cover_image']['name'])) {

            $upload_cover_image = ImageUploader::process($files['reg_cover_image'], ImageUploader::COVER_IMAGE, PPRESS_COVER_IMAGE_UPLOAD_DIR);

            if (is_wp_error($upload_cover_image)) {
                return "<div class='profilepress-reg-status'>" . $upload_cover_image->get_error_message() . "</div>";
            }
        }
        // --------END ---------   validation for cover photo upload ----------------------//

        do_action('ppress_before_registration', $form_id, $user_data);

        // proceed to registration using wp_insert_user method which return the new user id
        $user_id = wp_insert_user(apply_filters('ppress_registration_real_userdata', $real_userdata, $form_id, $post));

        if (is_wp_error($user_id)) {
            return '<div class="profilepress-reg-status">' . $user_id->get_error_message() . '</div>';
        }

        // --------START ---------   register custom field ----------------------//

        $custom_usermeta['pp_profile_avatar'] = $upload_avatar ?? null;
        $custom_usermeta['pp_profile_cover_image'] = $upload_cover_image ?? null;

        // if we get to this point, it means the files pass validation defined above.
        // array of files uploaded. Array key is the "custom field key" and the filename as the array value.
        $custom_usermeta['pp_uploaded_files'] = $uploads;

        $custom_usermeta = apply_filters('ppress_registration_custom_usermeta', $custom_usermeta, $user_id, $form_id, $post);

        // if $user_id is no WP_Error, add the extra user profile field
        if (is_array($custom_usermeta)) {

            foreach ($custom_usermeta as $key => $value) {

                if (!empty($value)) {

                    update_user_meta($user_id, $key, $value);
                    // the 'edit_profile' parameter is used to distinguish it from same action hook in RegistrationAuth
                    do_action('ppress_after_custom_field_update', $key, $value, $user_id, 'registration');
                }
            }
        }
        // --------END ---------   register custom field ----------------------//

        if ($flag_to_send_password_reset === true) {
            PasswordReset::retrieve_password_func($username);
        }

        // record signup via
        if ($is_melange) {
            add_user_meta($user_id, '_pp_signup_melange_via', $form_id);
        } else {
            add_user_meta($user_id, '_pp_signup_via', $form_id);
        }

        $should_send_welcome_email = true;

        // if moderation is active, set new registered users as pending
        if (class_exists('ProfilePress\Libsodium\UserModeration\UserModeration') && UserModeration::moderation_is_active()) {

            if (apply_filters('ppress_user_moderation_make_pending', true, $form_id, $user_data)) {

                $should_send_welcome_email = false;

                UserModeration::make_pending($user_id);
                UserModerationNotification::pending($user_id);
                UserModerationNotification::pending_admin_notification($user_id);
            }
        }

        if (apply_filters('ppress_registration_should_send_welcome_email', $should_send_welcome_email, $user_id, $user_data)) {
            self::send_welcome_email($user_id, $password, $form_id);
        }

        if (is_int($user_id)) {
            ppress_wp_new_user_notification($user_id, null, 'admin');
        }

        /**
         * Fires after a user registration is completed.
         *
         * @param int $form_id ID of the registration form.
         * @param mixed $user_data array of registered user info.
         * @param int $user_id ID of the registered user.
         * @param bool $is_melange
         */
        do_action('ppress_after_registration', $form_id, $user_data, $user_id, $is_melange);
        /* End Action Hook */

        if (!empty($no_login_redirect)) {
            $response = self::no_login_redirect_after_reg($form_id, $no_login_redirect);
        } else {
            /**
             * call auto-login
             *
             * @param int $user_id registered user ID
             * @param int $form_id registration form ID
             * @param string $redirect redirect url after login
             */
            $response = self::auto_login_after_reg($user_id, $form_id, $redirect);
        }

        if (wp_doing_ajax() && isset($response) && !empty($response) && is_array($response)) {
            // $response should be an array containing the url to redirect to.
            return $response;
        }

        $success_message = FormRepository::get_form_meta($form_id, FormRepository::REGISTRATION_TYPE, FormRepository::SUCCESS_MESSAGE);
        if ($is_melange) {
            $success_message = FormRepository::get_form_meta($form_id, FormRepository::MELANGE_TYPE, FormRepository::MELANGE_REGISTRATION_SUCCESS_MESSAGE);
        }

        $default_success_message = '<div class="profilepress-reg-status success">' . esc_html__('Registration successful.', 'wp-user-avatar') . '</div>';

        if (FormRepository::is_drag_drop($form_id, FormRepository::REGISTRATION_TYPE)) {
            // Drag and drop signup pages do not allow the use of div wrapper. only the message to be shown is entered.
            // so here, we are wrapping it in reg status div.
            if (!empty($success_message)) {
                $success_message = '<div class="profilepress-reg-status success">' . $success_message . '</div>';
            }
        }

        return apply_filters('ppress_registration_success_message', !empty($success_message) ? wp_kses_post($success_message) : $default_success_message, $user_id, $form_id, $user_data);
    }

    /**
     * Array list of acceptable defined roles.
     *
     * @param int $form_id ID of registration form
     *
     * @return array
     */
    public static function acceptable_defined_roles($form_id)
    {
        if (FormRepository::is_drag_drop($form_id, FormRepository::REGISTRATION_TYPE)) {
            $settings = FormRepository::form_builder_fields_settings($form_id, FormRepository::REGISTRATION_TYPE);
            $found_field = wp_list_filter($settings, ['fieldType' => 'reg-select-role']);
            if (empty($found_field)) return [];
            $reg_select_field_options = array_values(wp_list_pluck($found_field, 'options'));
            $options = $reg_select_field_options[0] ?? [];
        } else {
            $registration_structure = FormRepository::get_form_meta($form_id, FormRepository::REGISTRATION_TYPE, FormRepository::FORM_STRUCTURE);
            preg_match('/\[reg-select-role.*\]/', $registration_structure, $matches);
            if (empty($matches[0])) return [];
            $options = [];
            if (strpos($matches[0], 'options') !== false) {
                preg_match('/options=["\']([,\s\w-]+)["\']/', $matches[0], $matches2);
                if (empty($matches2[1])) return [];
                $options = $matches2[1];
            }
        }

        $acceptable_user_role = empty($options) ? array_keys(ppress_get_editable_roles()) : array_map('trim', explode(',', $options));

        return apply_filters('ppress_acceptable_user_role', $acceptable_user_role, $form_id);
    }
}