<?php

abstract class SwpmUtils {

    public static function is_ajax() {
        return defined('DOING_AJAX') && DOING_AJAX;
    }

    /*
     * This function handles various initial setup tasks that need to be executed very early on (before other functions of the plugin is called).
     */

    public static function do_misc_initial_plugin_setup_tasks() {

        //Management role/permission setup
        $admin_dashboard_permission = SwpmSettings::get_instance()->get_value('admin-dashboard-access-permission');
        if (empty($admin_dashboard_permission)) {
            //By default only admins can manage/see admin dashboard
            define("SWPM_MANAGEMENT_PERMISSION", "manage_options");
        } else {
            define("SWPM_MANAGEMENT_PERMISSION", $admin_dashboard_permission);
        }

        //Set timezone preference (if enabled in settings)
        $use_wp_timezone = SwpmSettings::get_instance()->get_value('use-wordpress-timezone');
        if (!empty($use_wp_timezone)) {//Set the wp timezone
            $wp_timezone_string = get_option('timezone_string');
            if (!empty($wp_timezone_string)) {
                date_default_timezone_set($wp_timezone_string);
            }
        }
    }

    public static function subscription_type_dropdown($selected) {
        return '<option ' . (($selected == SwpmMembershipLevel::NO_EXPIRY) ? 'selected="selected"' : "") . ' value="' . SwpmMembershipLevel::NO_EXPIRY . '">No Expiry</option>' .
                '<option ' . (($selected == SwpmMembershipLevel::DAYS) ? 'selected="selected"' : "") . ' value="' . SwpmMembershipLevel::DAYS . '">Day(s)</option>' .
                '<option ' . (($selected == SwpmMembershipLevel::WEEKS) ? 'selected="selected"' : "") . ' value="' . SwpmMembershipLevel::WEEKS . '">Week(s)</option>' .
                '<option ' . (($selected == SwpmMembershipLevel::MONTHS) ? 'selected="selected"' : "") . ' value="' . SwpmMembershipLevel::MONTHS . '">Month(s)</option>' .
                '<option ' . (($selected == SwpmMembershipLevel::YEARS) ? 'selected="selected"' : "") . ' value="' . SwpmMembershipLevel::YEARS . '">Year(s)</option>' .
                '<option ' . (($selected == SwpmMembershipLevel::FIXED_DATE) ? 'selected="selected"' : "") . ' value="' . SwpmMembershipLevel::FIXED_DATE . '">Fixed Date</option>';
    }

    // $subscript_period must be integer.
    public static function calculate_subscription_period_days($subcript_period, $subscription_duration_type) {
        if ($subscription_duration_type == SwpmMembershipLevel::NO_EXPIRY) {
            return 'noexpire';
        }
        if (!is_numeric($subcript_period)) {
            throw new Exception(" subcript_period parameter must be integer in SwpmUtils::calculate_subscription_period_days method");
        }
        switch (strtolower($subscription_duration_type)) {
            case SwpmMembershipLevel::DAYS:
                break;
            case SwpmMembershipLevel::WEEKS:
                $subcript_period = $subcript_period * 7;
                break;
            case SwpmMembershipLevel::MONTHS:
                $subcript_period = $subcript_period * 30;
                break;
            case SwpmMembershipLevel::YEARS:
                $subcript_period = $subcript_period * 365;
                break;
        }
        return $subcript_period;
    }

    public static function get_expiration_timestamp($user) {
        $permission = SwpmPermission::get_instance($user->membership_level);
        if (SwpmMembershipLevel::FIXED_DATE == $permission->get('subscription_duration_type')) {
            return strtotime($permission->get('subscription_period'));
        }
        $days = self::calculate_subscription_period_days($permission->get('subscription_period'), $permission->get('subscription_duration_type'));
        if ($days == 'noexpire') {
            return PHP_INT_MAX; // which is equivalent to
        }
        return strtotime($user->subscription_starts . ' ' . $days . ' days');
    }

    public static function is_subscription_expired($user) {
        $expiration_timestamp = SwpmUtils::get_expiration_timestamp($user);
        if ($expiration_timestamp < time()) {
            //Account expired.
            return true;
        }
        return false;
    }

    /*
     * Returns a formatted expiry date string (of a member). This can be useful to echo the date value.
     */

    public static function get_formatted_expiry_date($start_date, $subscription_duration, $subscription_duration_type) {
        if ($subscription_duration_type == SwpmMembershipLevel::FIXED_DATE) {
            //Membership will expire after a fixed date.
            return SwpmUtils::get_formatted_and_translated_date_according_to_wp_settings($subscription_duration);
        }

        $expires = self::calculate_subscription_period_days($subscription_duration, $subscription_duration_type);
        if ($expires == 'noexpire') {
            //Membership is set to no expiry or until cancelled.
            return SwpmUtils::_('Never');
        }

        //Membership is set to a duration expiry settings.
        return date_i18n(get_option('date_format'), strtotime($start_date . ' ' . $expires . ' days'));
    }

    public static function gender_dropdown($selected = 'not specified') {
        return '<option ' . ((strtolower($selected) == 'male') ? 'selected="selected"' : "") . ' value="male">Male</option>' .
                '<option ' . ((strtolower($selected) == 'female') ? 'selected="selected"' : "") . ' value="female">Female</option>' .
                '<option ' . ((strtolower($selected) == 'not specified') ? 'selected="selected"' : "") . ' value="not specified">Not Specified</option>';
    }

    public static function get_account_state_options() {
        return array('active' => SwpmUtils::_('Active'),
            'inactive' => SwpmUtils::_('Inactive'),
            'activation_required' => SwpmUtils::_('Activation Required'),
            'pending' => SwpmUtils::_('Pending'),
            'expired' => SwpmUtils::_('Expired'),);
    }

    public static function account_state_dropdown($selected = 'active') {
        $options = self::get_account_state_options();
        $html = '';
        foreach ($options as $key => $value) {
            $html .= '<option ' . ((strtolower($selected) == $key) ? 'selected="selected"' : "") . '  value="' . $key . '"> ' . $value . '</option>';
        }
        return $html;
    }

    public static function membership_level_dropdown($selected = 0) {
        $options = '';
        global $wpdb;
        $query = "SELECT alias, id FROM " . $wpdb->prefix . "swpm_membership_tbl WHERE id != 1";
        $levels = $wpdb->get_results($query);
        foreach ($levels as $level) {
            $options .= '<option ' . ($selected == $level->id ? 'selected="selected"' : '') . ' value="' . $level->id . '" >' . $level->alias . '</option>';
        }
        return $options;
    }

    public static function get_all_membership_level_ids() {
        global $wpdb;
        $query = "SELECT id FROM " . $wpdb->prefix . "swpm_membership_tbl WHERE id != 1";
        return $wpdb->get_col($query);
    }

    public static function get_membership_level_row_by_id($level_id) {
        global $wpdb;
        $query = $wpdb->prepare("SELECT * FROM " . $wpdb->prefix . "swpm_membership_tbl WHERE id=%d", $level_id);
        $level_resultset = $wpdb->get_row($query);
        return $level_resultset;
    }

    public static function membership_level_id_exists($level_id) {
        //Returns true if the specified membership level exists in the system. Returns false if the level has been deleted (or doesn't exist).
        $all_level_ids = SwpmUtils::get_all_membership_level_ids();
        if (in_array($level_id, $all_level_ids)) {
            //Valid level ID
            return true;
        } else {
            return false;
        }
    }

    public static function get_registration_complete_prompt_link($for = 'all', $send_email = false, $member_id = '') {
        $members = array();
        global $wpdb;
        switch ($for) {
            case 'one':
                if (empty($member_id)) {
                    return array();
                }
                $query = $wpdb->prepare("SELECT * FROM  {$wpdb->prefix}swpm_members_tbl WHERE member_id =  %d", $member_id);
                $members = $wpdb->get_results($query);
                break;
            case 'all':
                $query = "SELECT * FROM  {$wpdb->prefix}swpm_members_tbl WHERE reg_code != '' ";
                $members = $wpdb->get_results($query);
                break;
        }
        $settings = SwpmSettings::get_instance();
        $separator = '?';
        $url = $settings->get_value('registration-page-url');
        if (strpos($url, '?') !== false) {
            $separator = '&';
        }

        $links = array();
        foreach ($members as $member) {
            $reg_url = $url . $separator . 'member_id=' . $member->member_id . '&code=' . $member->reg_code;
            if ($send_email && empty($member->user_name)) {
                $tags = array("{first_name}", "{last_name}", "{reg_link}");
                $vals = array($member->first_name, $member->last_name, $reg_url);

                $subject = $settings->get_value('reg-prompt-complete-mail-subject');
                if (empty($subject)) {
                    $subject = "Please complete your registration";
                }

                $body = $settings->get_value('reg-prompt-complete-mail-body');
                if (empty($body)) {
                    $body = "Please use the following link to complete your registration. \n {reg_link}";
                }
                $body = html_entity_decode($body);
                $email_body = str_replace($tags, $vals, $body);

                $from_address = $settings->get_value('email-from');
                $headers = 'From: ' . $from_address . "\r\n";

                $subject = apply_filters('swpm_email_complete_your_registration_subject', $subject);
                $email_body = apply_filters('swpm_email_complete_your_registration_body', $email_body);
                wp_mail($member->email, $subject, $email_body, $headers);
                SwpmLog::log_simple_debug('Prompt to complete registration email sent to: ' . $member->email . '. From email address value used: ' . $from_address, true);
            }
            $links[] = $reg_url;
        }
        return $links;
    }

    /* This function is deprecated and will be removed in the future. Use SwpmMemberUtils::update_wp_user_role() instead */

    public static function update_wp_user_Role($wp_user_id, $role) {
        // Deprecated function.
        SwpmMemberUtils::update_wp_user_role($wp_user_id, $role);
    }

    public static function update_wp_user($wp_user_name, $swpm_data) {
        $wp_user_info = array();
        if (isset($swpm_data['email'])) {
            $wp_user_info['user_email'] = $swpm_data['email'];
        }
        if (isset($swpm_data['first_name'])) {
            $wp_user_info['first_name'] = $swpm_data['first_name'];
        }
        if (isset($swpm_data['last_name'])) {
            $wp_user_info['last_name'] = $swpm_data['last_name'];
        }
        if (isset($swpm_data['plain_password'])) {
            $wp_user_info['user_pass'] = $swpm_data['plain_password'];
        }

        $wp_user = get_user_by('login', $wp_user_name);

        if ($wp_user) {
            $wp_user_info['ID'] = $wp_user->ID;
            return wp_update_user($wp_user_info);
        }
        return false;
    }

    public static function create_wp_user($wp_user_data) {

        //Check if the email belongs to an existing wp user account.
        $wp_user_id = email_exists($wp_user_data['user_email']);
        if ($wp_user_id) {
            //A wp user account exist with this email.
            //Check if the user has admin role.
            $admin_user = SwpmMemberUtils::wp_user_has_admin_role($wp_user_id);
            if ($admin_user) {
                //This email belongs to an admin user. Update is not allowed on admin users. Show error message then exit.
                $error_msg = '<p>This email address (' . $wp_user_data['user_email'] . ') belongs to an admin user. This email cannot be used to register a new account on this site.</p>';
                wp_die($error_msg);
            }
        }

        //At this point 1) A WP User with this email doesn't exist. Or 2) The associated wp user doesn't have admin role
        //Lets create a new wp user record or attach the SWPM profile to an existing user accordingly.

        if (self::is_multisite_install()) {
            //WP Multi-Sit install
            global $blog_id;
            if ($wp_user_id) {
                //If user exists then just add him to current blog.
                add_existing_user_to_blog(array('user_id' => $wp_user_id, 'role' => 'subscriber'));
                return $wp_user_id;
            }
            $wp_user_id = wpmu_create_user($wp_user_data['user_login'], $wp_user_data['password'], $wp_user_data['user_email']);
            $role = 'subscriber'; //TODO - add user as a subscriber first. The subsequent update user role function to update the role to the correct one
            add_user_to_blog($blog_id, $wp_user_id, $role);
        } else {
            //WP Single site install
            if ($wp_user_id) {
                return $wp_user_id;
            }
            $wp_user_id = wp_create_user($wp_user_data['user_login'], $wp_user_data['password'], $wp_user_data['user_email']);
        }
        $wp_user_data['ID'] = $wp_user_id;
        wp_update_user($wp_user_data); //Core WP function. Updates the user info and role.

        return $wp_user_id;
    }

    public static function is_multisite_install() {
        if (function_exists('is_multisite') && is_multisite()) {
            return true;
        } else {
            return false;
        }
    }

    public static function _($msg) {
        return __($msg, 'simple-membership');
    }

    public static function e($msg) {
        _e($msg, 'simple-membership');
    }

    /*
     * Deprecated. Instead use SwpmUtils::has_admin_management_permission()
     */

    public static function is_admin() {
        //This function returns true if the current user has WordPress admin management permission (not to be mistaken with SWPM admin permission.
        //This function is NOT like the WordPress's is_admin() function which determins if we are on the admin end of the site.
        //TODO - rename this function to something like is_admin_user()
        return current_user_can('manage_options');
    }

    public static function has_admin_management_permission() {
        if (current_user_can(SWPM_MANAGEMENT_PERMISSION)) {
            return true;
        } else {
            return false;
        }
    }

    /*
     * Formats the given date value according to the WP date format settings. This function is useful for displaying a human readable date value to the user.
     */
    public static function get_formatted_date_according_to_wp_settings($date) {
        $date_format = get_option('date_format');
        if (empty($date_format)) {
            //WordPress's date form settings is not set. Lets set a default format.
            $date_format = 'Y-m-d';
        }

        $date_obj = new DateTime($date);
        $formatted_date = $date_obj->format($date_format); //Format the date value using date format settings
        return $formatted_date;
    }

    /*
     * Formats and Translates the given date value according to the WP date format settings. This function is useful for displaying a human readable date value to the user.
     * The $date argument value must be in nromal date format (2025-01-15). The function will use strtotime() function to convert it to unix time then use it.
     */
    public static function get_formatted_and_translated_date_according_to_wp_settings($date) {
        $date_format = get_option('date_format');
        if (empty($date_format)) {
            //WordPress's date form settings is not set. Lets set a default format.
            $date_format = 'Y-m-d';
        }

        $formatted_translated_date = date_i18n( $date_format, strtotime( $date ) );
        return $formatted_translated_date;
    }
    
    public static function swpm_username_exists($user_name) {
        global $wpdb;
        $member_table = $wpdb->prefix . 'swpm_members_tbl';
        $query = $wpdb->prepare('SELECT member_id FROM ' . $member_table . ' WHERE user_name=%s', sanitize_user($user_name));
        return $wpdb->get_var($query);
    }

    public static function get_free_level() {
        $encrypted = filter_input(INPUT_POST, 'level_identifier');
        if (!empty($encrypted)) {
            return SwpmPermission::get_instance($encrypted)->get('id');
        }

        $is_free = SwpmSettings::get_instance()->get_value('enable-free-membership');
        $free_level = absint(SwpmSettings::get_instance()->get_value('free-membership-id'));

        return ($is_free) ? $free_level : null;
    }

    public static function is_paid_registration() {
        $member_id = filter_input(INPUT_GET, 'member_id', FILTER_SANITIZE_NUMBER_INT);
        $code = filter_input(INPUT_GET, 'code', FILTER_SANITIZE_STRING);
        if (!empty($member_id) && !empty($code)) {
            return true;
        }
        return false;
    }

    public static function get_paid_member_info() {
        $member_id = filter_input(INPUT_GET, 'member_id', FILTER_SANITIZE_NUMBER_INT);
        $code = filter_input(INPUT_GET, 'code', FILTER_SANITIZE_STRING);
        global $wpdb;
        if (!empty($member_id) && !empty($code)) {
            $query = 'SELECT * FROM ' . $wpdb->prefix . 'swpm_members_tbl WHERE member_id= %d AND reg_code=%s';
            $query = $wpdb->prepare($query, $member_id, $code);
            return $wpdb->get_row($query);
        }
        return null;
    }

    public static function get_incomplete_paid_member_info_by_ip() {
        global $wpdb;
        $user_ip = SwpmUtils::get_user_ip_address();
        if (!empty($user_ip)) {
            //Lets check if a payment has been confirmed from this user's IP and the profile needs to be completed (where username is empty).
            $username = '';
            $query = "SELECT * FROM " . $wpdb->prefix . "swpm_members_tbl WHERE last_accessed_from_ip=%s AND user_name=%s";
            $query = $wpdb->prepare($query, $user_ip, $username);
            $result = $wpdb->get_row($query);
            return $result;
        }
        return null;
    }

    public static function account_delete_confirmation_ui($msg = "") {
        ob_start();
        include(SIMPLE_WP_MEMBERSHIP_PATH . 'views/account_delete_warning.php');
        ob_get_flush();
        wp_die("", "", array('back_link' => true));
    }

    public static function delete_account_button() {
        $allow_account_deletion = SwpmSettings::get_instance()->get_value('allow-account-deletion');
        if (empty($allow_account_deletion)) {
            return "";
        }

        $account_delete_link = '<div class="swpm-profile-account-delete-section">';
        $account_delete_link .= '<a href="' . SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL . '/?swpm_delete_account=1"><div class="swpm-account-delete-button">' . SwpmUtils::_("Delete Account") . '</div></a>';
        $account_delete_link .= '</div>';
        return $account_delete_link;
    }

    public static function encrypt_password($plain_password) {
        include_once(ABSPATH . WPINC . '/class-phpass.php');
        $wp_hasher = new PasswordHash(8, TRUE);
        $password_hash = $wp_hasher->HashPassword(trim($plain_password));
        return $password_hash;
    }

    public static function get_restricted_image_url() {
        return SIMPLE_WP_MEMBERSHIP_URL . '/images/restricted-icon.png';
    }

    /*
     * Checks if the string exists in the array key value of the provided array. If it doesn't exist, it returns the first key element from the valid values.
     */

    public static function sanitize_value_by_array($val_to_check, $valid_values) {
        $keys = array_keys($valid_values);
        $keys = array_map('strtolower', $keys);
        if (in_array($val_to_check, $keys)) {
            return $val_to_check;
        }
        return reset($keys); //Return he first element from the valid values
    }

    public static function get_user_ip_address() {
        $user_ip = '';
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $user_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        } else {
            $user_ip = $_SERVER['REMOTE_ADDR'];
        }

        if (strstr($user_ip, ',')) {
            $ip_values = explode(',', $user_ip);
            $user_ip = $ip_values['0'];
        }

        return apply_filters('swpm_get_user_ip_address', $user_ip);
    }

    public static function is_first_click_free(&$content) {
        $is_first_click = false;
        $args = array($is_first_click, $content);
        $filtered = apply_filters('swpm_first_click_free', $args);
        list($is_first_click, $content) = $filtered;
        return $is_first_click;
    }

    private static function crypt_fallback($string, $action = 'e') {
        if ($action === 'e') {
            return base64_encode($string);
        } else {
            return base64_decode($string);
        }
    }

    public static function crypt($string, $action = 'e') {
        //check if openssl module is enabled
        if (!extension_loaded('openssl')) {
            // no openssl extension loaded. Can't ecnrypt
            return self::crypt_fallback($string, $action);
        }
        //check if encrypt method is supported
        $encrypt_method = "aes-256-ctr";
        $available_methods = openssl_get_cipher_methods();
        if (!in_array($encrypt_method, $available_methods)) {
            // no ecryption method supported. Can't encrypt
            return self::crypt_fallback($string, $action);
        }

        $output = false;
        $secret_key = wp_salt('auth');
        $secret_iv = wp_salt('secure_auth');
        $key = hash('sha256', $secret_key);
        $iv = substr(hash('sha256', $secret_iv), 0, 16);

        if ($action == 'e') {
            $output = base64_encode(openssl_encrypt($string, $encrypt_method, $key, 0, $iv));
        } else if ($action == 'd') {
            $output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv);
        }

        return $output;
    }

}
