<?php

if (! defined('ABSPATH')) {
    die('You are not allowed to call this page directly.');
}

class MeprReadyLaunchCtrl extends MeprBaseCtrl
{
    /**
     * Loads the hooks.
     *
     * @return void
     */
    public function load_hooks()
    {
        add_action('mepr_display_options_tabs', 'MeprReadyLaunchCtrl::display_option_tab', 100);
        add_action('mepr_display_options', 'MeprReadyLaunchCtrl::display_option_fields');
        add_action('admin_enqueue_scripts', 'MeprReadyLaunchCtrl::enqueue_admin_scripts', 20);
        add_action('wp_enqueue_scripts', 'MeprReadyLaunchCtrl::enqueue_scripts', 999999);
        add_action('wp_head', 'MeprReadyLaunchCtrl::theme_style');
        add_action('admin_head', 'MeprReadyLaunchCtrl::theme_style');
        add_action('mepr_rl_before_main', 'MeprReadyLaunchCtrl::add_header');
        add_action('mepr_rl_after_main', 'MeprReadyLaunchCtrl::add_footer');
        add_action('mepr_rl_enqueue_scripts', 'MeprReadyLaunchCtrl::enqueue_general_scripts');

        // FSE ReadyLaunch styling.
        add_action('enqueue_block_assets', 'MeprReadyLaunchCtrl::enqueue_fse_readylaunch_styles');
        add_action('wp_head', 'MeprReadyLaunchCtrl::maybe_dequeue_mp_pro_theme_for_fse_shell', 0);
        add_filter('mepr_validate_options', 'MeprReadyLaunchCtrl::validate_settings_fields');
        add_filter('template_include', [$this, 'override_page_templates'], 999999); // High priority so we have the last say here.
        add_filter('the_content', [$this, 'thankyou_page_content'], 99);
        add_filter('show_admin_bar', [$this, 'remove_admin_bar']);
        add_filter('mepr_membership_cant_purchase_string', [$this, 'cant_purchase_message']);
        add_filter('mepr_validate_account_ajax', [$this, 'validate_account_fields'], 10, 3);

        add_action('wp_ajax_prepare_editable_field', 'MeprReadyLaunchCtrl::account_profile_editable_fields');
        add_action('wp_ajax_load_more_subscriptions', [$this, 'load_more_subscriptions']);
        add_action('wp_ajax_load_more_payments', [$this, 'load_more_payments']);

        // Shortcodes!
        MeprHooks::add_shortcode('mepr_pro_login_form', [$this, 'login_form_shortcode']);
        MeprHooks::add_shortcode('mepr_pro_pricing_table', [$this, 'pricing_table_shortcode']);
        MeprHooks::add_shortcode('mepr_pro_account_tabs', [$this, 'account_shortcode']);
        MeprHooks::add_shortcode('mepr_pro_checkout', [$this, 'checkout_shortcode']);
    }

    /**
     * Renders Pro login form.
     *
     * @param array $atts Shortcode attributes.
     */
    public function login_form_shortcode($atts = [])
    {
        $show_welcome_image = 0;
        $welcome_image      = 0;

        // Show welcome image.
        if (isset($atts['show_welcome_image'])) {
            $show_welcome_image = filter_var($atts['show_welcome_image'], FILTER_VALIDATE_BOOLEAN);
        }

        // Get welcome image.
        if (isset($atts['welcome_image']) && ! empty($atts['welcome_image'])) {
            $welcome_image = $atts['welcome_image'];
        }

        add_filter('mepr_pro_templates_has_login_block', '__return_true');
        $content = do_shortcode('[mepr_login_form welcome_image="' . $welcome_image . '" show_welcome_image="' . $show_welcome_image . '" admin_view="' . $atts['admin_view'] . '"]');

        return $content;
    }

    /**
     * Render pricing table shortcode
     *
     * @param  array $atts Shortcode args.
     * @return string
     */
    public function pricing_table_shortcode($atts = [])
    {
        wp_enqueue_script('mepr-pro-pricing', MEPR_JS_URL . '/readylaunch/pricing.js', ['jquery'], MEPR_VERSION, true);


        if (! isset($atts['group_id']) || $atts['group_id'] <= 0) {
            return esc_html__('Please select group', 'memberpress');
        }

        $group = new MeprGroup($atts['group_id']);

        if (! $group->ID) {
            return esc_html__('No group found', 'memberpress');
        }

        // Force the ReadyLaunch pricing view in display_pricing_boxes().
        // REST-gated so it only affects the block's editor preview; the front end is unchanged.
        if (defined('REST_REQUEST') && REST_REQUEST) {
            add_filter('mepr_pro_templates_has_pricing_block', '__return_true');
        }
        $content = do_shortcode('[mepr_group_price_boxes group_id="' . $group->ID . '" show_title="' . $atts['show_title'] . '" button_highlight_color="' . $atts['button_highlight_color'] . '"] ');

        return $content;
    }

    /**
     * Render pricing table shortcode
     *
     * @param  array $atts Shortcode args.
     * @return string
     */
    public function account_shortcode($atts = [])
    {
        wp_enqueue_script('alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', [], MEPR_VERSION, true);
        wp_enqueue_script('mepr-accountjs', MEPR_JS_URL . '/readylaunch/account.js', ['jquery'], MEPR_VERSION, true);
        wp_localize_script(
            'mepr-accountjs',
            'MeprAccount',
            [
                'ajax_url'    => admin_url('admin-ajax.php'),
                'nonce'       => wp_create_nonce('mepr_account_update'),
                'current_url' => MeprUtils::get_current_url_without_params(),
            ]
        );

        // Show welcome image.
        if (isset($atts['show_welcome_image'])) {
            $show_welcome_image = filter_var($atts['show_welcome_image'], FILTER_VALIDATE_BOOLEAN);
        }

        // Get welcome image.
        $welcome_image = '';
        if (isset($atts['welcome_image']) && ! empty($atts['welcome_image'])) {
            $welcome_image = $atts['welcome_image'];
        }

        add_filter('mepr_pro_templates_has_account_block', '__return_true');
        $content = do_shortcode('[mepr_account_form  welcome_image="' . $welcome_image . '" show_welcome_image="' . $show_welcome_image . '"]');

        if (MeprUtils::is_user_logged_in()) {
            $content = "<div class='mp_wrapper alignwide wp-block wp-shortcode'>" . $content . '</div>';
        }

        return $content;
    }

    /**
     * Checkout Shortcode
     *
     * @param  array $atts Array of attributes.
     * @return string
     */
    public function checkout_shortcode($atts = [])
    {
        wp_enqueue_script('mepr-signupjs', MEPR_JS_URL . '/readylaunch/signup.js', ['jquery'], MEPR_VERSION, true);

        wp_localize_script(
            'mepr-signupjs',
            'MeprProTemplateSignup',
            [
                'spc_enabled'     => true,
                'is_product_page' => true, // ReadyLaunch block should always use ReadyLaunch template.
            ]
        );

        if (! isset($atts['membership_id']) || $atts['membership_id'] <= 0) {
            return esc_html__('Please select membership', 'memberpress');
        }

        $prd = new MeprProduct($atts['membership_id']);

        if (! $prd->ID) {
            return esc_html__('No membership found', 'memberpress');
        }

        // Force the ReadyLaunch two-column checkout view in display_signup_form().
        // REST-gated so it only affects the block's editor preview; the front end is unchanged.
        if (defined('REST_REQUEST') && REST_REQUEST) {
            add_filter('mepr_pro_templates_has_checkout_block', '__return_true');
        }
        $content = do_shortcode('[mepr_membership_registration_form id="' . $prd->ID . '"]');

        return $content;
    }

    /**
     * Override default template with the courses page template
     *
     * @param  string $template Current template.
     * @return string $template modified template
     */
    public function override_page_templates($template)
    {
        global $post;
        $mepr_options        = MeprOptions::fetch();
        $logout_url          = MeprUtils::logout_url();
        $account_url         = $mepr_options->account_page_url();
        $delim               = MeprAppCtrl::get_param_delimiter_char($account_url);
        $change_password_url = MeprHooks::apply_filters('mepr_rl_change_password_url', $account_url . $delim . 'action=newpassword');
        $logo_id             = $mepr_options->design_logo_img;
        $logo                = esc_url(wp_get_attachment_url($logo_id));
        $site_name           = get_bloginfo('name', 'display');
        $logo_alt            = get_post_meta($logo_id, '_wp_attachment_image_alt', true);
        // Translators: %s is the site's title option value.
        $logo_alt            = $logo_alt ? $logo_alt : sprintf(_x('%s logo', 'Site logo alternative text', 'memberpress'), $site_name);
        $user                = MeprUtils::get_currentuserinfo();
        $wrapper_classes     = '';

        if (
            self::template_enabled('pricing')
            && ! MeprFseHelper::post_has_memberpress_fse_template()
            && ! MeprFseHelper::request_uses_memberpress_fse_cpt_option_template()
        ) {
            // Defer to FSE when the group maps to a ReadyLaunch FSE template (per-post or CPT-wide); otherwise this terminal include+exit short-circuits the FSE template and the classic unstyled header shows. Mirrors the branches below.
            $user              = MeprUtils::get_currentuserinfo();
            $has_welcome_image = $mepr_options->design_login_welcome_img;
            $group_ctrl        = MeprCtrlFactory::fetch('groups');

            $template = \MeprView::file('/readylaunch/layout/app');
            include $template;
            exit;
        }

        if (
            self::template_enabled('login')
            && ! MeprFseHelper::post_has_memberpress_fse_template()
        ) {
            if ($post->ID === $mepr_options->login_page_id) {
                // Defer to FSE when the page has an FSE template assigned —
                // see the matching guard on the account branch below.
                $template = \MeprView::file('/readylaunch/layout/guest');
                include $template;
                exit;
            }
        }

        if (
            self::template_enabled('account')
            && ! MeprFseHelper::post_has_memberpress_fse_template()
        ) {
            // Classic Account PHP template — `include` + `exit` is terminal and runs
            // at template_include priority 999999, so without the FSE-template guard
            // it short-circuits the block-template renderer even when FSE is active
            // and the page has an FSE template assigned. Defer to FSE in that case.
            $is_account_page = true;
            $template        = MeprView::file('/readylaunch/layout/app');
            include $template;
            exit;
        }

        // Checkout Page Template.
        if (
            self::template_enabled('checkout')
            && ! MeprFseHelper::post_has_memberpress_fse_template()
        ) {
            // Defer to FSE when the page has an FSE template assigned —
            // see the matching guard on the account branch above.
            $template = MeprView::file('/readylaunch/layout/app');
            include $template;
            exit;
        }

        if (
            self::template_enabled('thankyou')
            && ! MeprFseHelper::post_has_memberpress_fse_template()
        ) {
            // Defer to FSE when the page has an FSE template assigned —
            // see the matching guard on the account branch above.
            $template = MeprView::file('/readylaunch/layout/app');
            include $template;
            exit;
        }

        $template = MeprHooks::apply_filters('mepr_rl_template_override', $template, get_defined_vars());

        return $template;
    }

    /**
     * Gets the page content for thankyou page (filter callback)
     *
     * @param  string $content The content.
     * @return string
     */
    public function thankyou_page_content($content)
    {
        if (
            self::template_enabled('thankyou')
            && ! MeprFseHelper::post_has_memberpress_fse_template()
        ) {
            $content = MeprTransactionsHelper::get_thankyou_content();
        }

        return $content;
    }

    /**
     * Enqueues scripts for admin view
     *
     * @param  string $hook Current page hook.
     * @return void
     */
    public static function enqueue_admin_scripts($hook)
    {
        if (strstr($hook, 'memberpress-options') !== false) {
            wp_enqueue_style('mp-readylaunch', MEPR_CSS_URL . '/admin-readylaunch.css', [], MEPR_VERSION);

            // Let's localize data for our drag and drop settings.
            $plupload_args = [
                'file_data_name'   => 'async-upload',
                'url'              => admin_url('admin-ajax.php'),
                'filters'          => [
                    'max_file_size' => wp_max_upload_size() . 'b',
                    'mime_types'    => [['extensions' => 'jpg,gif,png,jpeg']],
                ],
                'multi_selection'  => false, // Limit selection to just one.

                // Additional post data to send to our ajax hook.
                'multipart_params' => [
                    '_wpnonce' => wp_create_nonce('media-form'),
                    'action'   => 'upload-attachment',            // The ajax action name.
                ],
            ];
            wp_enqueue_style('wp-color-picker');
            wp_enqueue_script('mp-readylaunch', MEPR_JS_URL . '/admin-readylaunch.js', ['mepr-uploader', 'plupload-all', 'wp-color-picker', 'wp-i18n'], MEPR_VERSION);
            wp_localize_script('mp-readylaunch', 'MeproTemplates', $plupload_args);

            // Localize FSE template data when on a block theme with WP 6.7+.
            $fse_ctrl     = MeprCtrlFactory::fetch('FseTemplates');
            $mepr_options = MeprOptions::fetch();
            if ($fse_ctrl->can_run() && function_exists('wp_is_block_theme') && wp_is_block_theme()) {
                $fse_enabled = !empty($mepr_options->fse_mode_enabled);

                wp_localize_script('mp-readylaunch', 'MeprFseTemplates', [
                    'is_block_theme'   => true,
                    'fse_mode_enabled' => $fse_enabled,
                    'nonce'            => wp_create_nonce('mepr_fse_templates'),
                    'ajax_url'         => admin_url('admin-ajax.php'),
                    'templates'        => $fse_enabled ? $fse_ctrl->get_all_template_statuses() : [],
                    'i18n'             => [
                        // Translators: %1$s: template name, %2$d: number of posts, %3$s: post type label.
                        'apply_confirm_cpt'  => esc_html__('This will apply the %1$s template to all %2$d existing %3$s. Continue?', 'memberpress'),
                        // Translators: %1$s: template name, %2$d: number of posts, %3$s: post type label.
                        'remove_confirm_cpt' => esc_html__('This will remove the %1$s template from all %2$d %3$s. Continue?', 'memberpress'),
                        'edit_template'      => esc_html__('Edit Template', 'memberpress'),
                        'apply_template'     => esc_html__('Apply Template', 'memberpress'),
                        'remove_template'    => esc_html__('Remove', 'memberpress'),
                        'enable_confirm'     => esc_html__('This will apply FSE templates to all your MemberPress pages and membership posts. Continue?', 'memberpress'),
                        'disable_confirm'    => esc_html__('This will remove FSE templates from all your MemberPress pages and membership posts and revert to ReadyLaunch Classic mode. Continue?', 'memberpress'),
                        'error'              => esc_html__('An error occurred. Please try again.', 'memberpress'),
                    ],
                ]);
            }
        }
    }

    /**
     * Enqueues scripts for frontend view
     *
     * Login/account bundle rules: {@see MeprReadyLaunchAssetsHelper}.
     *
     * @return void
     */
    public static function enqueue_scripts()
    {
        global $post;

        if (MeprUser::is_account_page($post)) {
            wp_enqueue_script('mepr-popper', MEPR_JS_URL . '/vendor/popper.min.js', [], MEPR_VERSION, true);
        }

        $handles = ['dashicons', 'jquery-ui-timepicker-addon', 'jquery-magnific-popup'];

        // Login Scripts.
        if (MeprReadyLaunchAssetsHelper::should_enqueue_login_bundle()) {
            static::remove_styles($handles);
            static::add_template_scripts('login');
        }

        // Account Scripts.
        if (MeprReadyLaunchAssetsHelper::should_enqueue_account_bundle()) {
            static::remove_styles($handles);
            static::add_template_scripts('account');
        }

        // Pricing Scripts.
        if (self::template_enabled('pricing')) {
            if (
                isset($post) &&
                is_a($post, 'WP_Post') &&
                $post->post_type === MeprGroup::$cpt
            ) {
                static::remove_styles($handles);
                static::add_template_scripts('pricing');
            }
        }

        // Checkout Scripts.
        // Defer to FSE when the thankyou page has a MemberPress FSE template assigned —
        // see the matching guard on override_page_templates() and thankyou_page_content().
        if (
            self::template_enabled('checkout') ||
            (self::template_enabled('thankyou') && ! MeprFseHelper::post_has_memberpress_fse_template())
        ) {
            static::remove_styles($handles);
            static::add_template_scripts('checkout');
        }

        if (
            function_exists('bp_is_current_action') &&
            bp_is_current_action('mp-subscriptions')
        ) {
            wp_enqueue_style('mp-rl-buddyboss', MEPR_CSS_URL . '/readylaunch/compatibility.css', [], MEPR_VERSION);
            wp_enqueue_script('alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', [], MEPR_VERSION, true);
            wp_enqueue_script('mepr-accountjs', MEPR_JS_URL . '/readylaunch/account.js', ['jquery'], MEPR_VERSION, true);
        }
    }

    /**
     * Enqueues FSE ReadyLaunch styles
     *
     * @return void
     */
    public static function enqueue_fse_readylaunch_styles()
    {
        // If we're not on a MemberPress FSE template and not in the block editor, don't enqueue the styles.
        if (! self::should_enqueue_fse_readylaunch_styles()) {
            return;
        }

        $mepr_options         = MeprOptions::fetch();
        $raw_primary          = ! empty($mepr_options->design_primary_color) ?
            $mepr_options->design_primary_color : '#06429E';
        $design_primary_color = sanitize_hex_color($raw_primary) ?: '#06429E';
        $text_color           = self::getContrastColor($design_primary_color);

        $nav_text_color_rgb = self::hexToRgb($text_color, 0.7);

        wp_register_style('mp-fse-readylaunch', false, [], MEPR_VERSION);

        /*
         * ReadyLaunch typography on MemberPress FSE shells.
         *
         * Block themes declare a body font-family via theme.json (e.g. Manrope).
         * Classic ReadyLaunch normalized typography to a system stack via
         * readylaunch/theme.css, which is dequeued on FSE shells (see
         * MeprReadyLaunchAssetsHelper::should_dequeue_mp_pro_theme_for_fse_shell).
         * Re-assert that stack here so ReadyLaunch chrome stays consistent across
         * block themes; `html body` is one specificity notch above theme.json's
         * `body` rule. A paid theme can keep its own typography (the theme font
         * then applies page-wide, chrome included) with:
         *   add_filter('mepr_fse_readylaunch_global_font_reset', '__return_false');
         */
        $font_family_reset = '';
        if (MeprHooks::apply_filters('mepr_fse_readylaunch_global_font_reset', true)) {
            $font_family       = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif';
            $font_family_reset = <<<CSS
            html body, html body button, html body input, html body select, html body optgroup, html body textarea { font-family: {$font_family}; }
            CSS;
        }

        // Only the dynamic brand-color tokens stay inline; the static header/site-title
        // rules that consume them live in readylaunch/shell.css (mp-readylaunch-shell).
        wp_add_inline_style('mp-fse-readylaunch', <<<CSS
:root {
    --wp--preset--color--readylaunch-primary: {$design_primary_color};
    --wp--preset--color--readylaunch-primary-text: {$text_color};
}
{$font_family_reset}
/* Mirror the global primary color onto both the canonical shell variables
 * (consumed by shell.css) and the Account-specific variables (kept as a
 * customizer hook scoped to `.mepr-rl-account`). */
.mepr-rl-shell .mepr-rl-shell__menu {
    --mepr-rl-shell-menu-bg: {$design_primary_color};
    --mepr-rl-shell-button-text: rgba({$nav_text_color_rgb});
    --mepr-rl-shell-button-hover-text: {$text_color};
}
.mepr-rl-account #mepr-account-nav {
    --mepr-account-menu-bg: {$design_primary_color};
    --mepr-account-button-text: rgba({$nav_text_color_rgb});
    --mepr-account-button-hover-text: {$text_color};
}
.wp-block-memberpress-pro-profile-menu__dropdown {
    --mp-dropdown-bg: {$design_primary_color};
    --mp-dropdown-text: {$text_color};
}
CSS);
        wp_enqueue_style('mp-fse-readylaunch');

        // The readylaunch-header template part is used by every MemberPress FSE
        // template (membership, thankyou, login, etc.) — only the Account
        // template renders the readylaunch-account block, which is the other
        // place mp-readylaunch-shell is enqueued. Without this, the header
        // loses its flex layout and body margin normalize on non-Account
        // ReadyLaunch FSE pages.
        wp_enqueue_style('mp-readylaunch-shell');
    }

    /**
     * Strip ReadyLaunch / MemberPress global theme styles for FSE shells (runs early in wp_head after late enqueues).
     *
     * Default handles: mp-pro-theme (readylaunch/theme.css), mp-theme (ui/theme.css). Filter to adjust.
     *
     * @return void
     */
    public static function maybe_dequeue_mp_pro_theme_for_fse_shell()
    {
        if (! MeprReadyLaunchAssetsHelper::should_dequeue_mp_pro_theme_for_fse_shell()) {
            return;
        }

        $handles = MeprHooks::apply_filters(
            'mepr_fse_shell_styles_to_dequeue',
            ['mp-pro-theme', 'mp-theme']
        );

        foreach ((array) $handles as $handle) {
            if (! is_string($handle) || $handle === '') {
                continue;
            }
            wp_dequeue_style($handle);
            wp_deregister_style($handle);
        }
    }

    /**
     * Enqueues general ReadyLaunch scripts when called by other plugins
     *
     * @return void
     */
    public static function enqueue_general_scripts()
    {
        if (! MeprReadyLaunchAssetsHelper::should_dequeue_mp_pro_theme_for_fse_shell()) {
            wp_enqueue_style('mp-pro-theme', MEPR_CSS_URL . '/readylaunch/theme.css', null, MEPR_VERSION);
        }
        wp_enqueue_script('mepr-general', MEPR_JS_URL . '/readylaunch/general.js', ['jquery'], MEPR_VERSION, true);
    }

    /**
     * Add Design tab toadmin memberpress page
     *
     * @return void
     */
    public static function display_option_tab()
    {
        ?>
    <a class="nav-tab" id="design" href="#"><?php esc_html_e('ReadyLaunch™', 'memberpress'); ?></a>
        <?php
    }

    /**
     * Displays design option fields
     *
     * FILTER_VALIDATE_BOOLEAN is used to return boolean, it works as described below
     *
     * filter_var(true, FILTER_VALIDATE_BOOLEAN); // true
     * filter_var('true', FILTER_VALIDATE_BOOLEAN); // true
     * filter_var(1, FILTER_VALIDATE_BOOLEAN); // true
     * filter_var('1', FILTER_VALIDATE_BOOLEAN); // true
     * filter_var('on', FILTER_VALIDATE_BOOLEAN); // true
     * filter_var('yes', FILTER_VALIDATE_BOOLEAN); // true
     */
    public static function display_option_fields()
    {
        $mepr_options          = MeprOptions::fetch();
        $groups                = MeprCptModel::all('MeprGroup');
        $pricing_columns_limit = false;

        foreach ($groups as $group) {
            $products_count = count($group->products());
            if ($products_count > 5) {
                $pricing_columns_limit = true;
                break;
            }
        }


        MeprView::render('/admin/readylaunch/options', get_defined_vars());
    }

    /**
     * Validates all Design tab admin settings
     *
     * @param  array $errors The errors array.
     * @return array
     */
    public static function validate_settings_fields($errors)
    {
        $params       = $_POST;
        $mepr_options = MeprOptions::fetch();

        // When LoginTemplate is enabled and ShowWelcomeImage is checked but dude forgot to upload the image.
        if (
            isset($params[ $mepr_options->design_enable_login_template_str ]) &&
            isset($params[ $mepr_options->design_show_login_welcome_image_str ]) &&
            absint($params[ $mepr_options->design_login_welcome_img_str ]) === 0
        ) {
            $errors[] = esc_html__('Welcome Image should be uploaded if Show Welcome Image button is checked', 'memberpress');
        }

        return $errors;
    }

    /**
     * Dequeues and deregisters styles unrelated to pro mode templates.
     *
     * @param  array $allowed_handles CSS Handles that won't be deregistered and dequeued when using Pro Mode.
     * @return void
     */
    public static function remove_styles($allowed_handles = [])
    {
        global $wp_styles;
        $allowed_handles         = MeprHooks::apply_filters('mepr_design_style_handles', $allowed_handles);
        $allowed_handle_prefixes = MeprHooks::apply_filters('mepr_design_style_handle_prefixes', ['mepr-', 'mp-', 'mpca-', 'mpcs-', 'mpgft-', 'mcpd', 'ca-course']);

        // Keep the applied theme block style variations (e.g. section "Style 1"–"Style 5" on
        // Group/Row). `block-style-variation-styles` carries the variation's own CSS (background/
        // text scoped to the runtime `is-style-…` class). We allow-list only that handle; its
        // declared dependencies — `global-styles` and `wp-block-library` — ride along and survive
        // too (WordPress drops a handle whose dependency is missing). That is intentional: keeping
        // `global-styles` means the theme.json color presets the variation CSS resolves against
        // stay available, so this is deliberately NOT a fully clean slate.
        $allowed_handles = array_merge(
            $allowed_handles,
            ['block-style-variation-styles']
        );

        // Remove styles.
        foreach ($wp_styles->queue as $style) {
            $handle = $wp_styles->registered[ $style ]->handle;
            if (! in_array($handle, $allowed_handles, true)) {
                foreach ($allowed_handle_prefixes as $prefix) {
                    if (strpos($handle, $prefix) === 0) {
                        continue 2;
                    }
                }

                wp_deregister_style($handle);
                wp_dequeue_style($handle);
            }
        }
    }

    /**
     * Add scripts to full page template
     *
     * FSE-only login and FSE-only account omit mp-pro-theme ({@see MeprReadyLaunchAssetsHelper::should_skip_mp_pro_theme_for_fse_login()},
     * {@see MeprReadyLaunchAssetsHelper::should_skip_mp_pro_theme_for_fse_account()}).
     * Any front view using a MemberPress FSE page template omits it ({@see MeprReadyLaunchAssetsHelper::should_dequeue_mp_pro_theme_for_fse_shell()}).
     *
     * @param  string $page The template page.
     * @return void
     */
    public static function add_template_scripts($page = '')
    {
        global $post;

        $skip_mp_pro_theme = MeprReadyLaunchAssetsHelper::should_dequeue_mp_pro_theme_for_fse_shell();
        if (
            'login' === $page
            && MeprReadyLaunchAssetsHelper::should_skip_mp_pro_theme_for_fse_login()
        ) {
            $skip_mp_pro_theme = true;
        }
        if (
            'account' === $page
            && MeprReadyLaunchAssetsHelper::should_skip_mp_pro_theme_for_fse_account()
        ) {
            $skip_mp_pro_theme = true;
        }

        if (! $skip_mp_pro_theme) {
            wp_enqueue_style('mp-pro-theme', MEPR_CSS_URL . '/readylaunch/theme.css', null, MEPR_VERSION);
        }

        wp_enqueue_script('mepr-general', MEPR_JS_URL . '/readylaunch/general.js', ['jquery'], MEPR_VERSION, true);

        if ('login' === $page) {
            wp_enqueue_style('mp-pro-login', MEPR_CSS_URL . '/readylaunch/login.css', null, MEPR_VERSION);
        }

        if ('account' === $page) {
            wp_enqueue_style('mp-pro-login', MEPR_CSS_URL . '/readylaunch/login.css', null, MEPR_VERSION);
            wp_register_style('mp-pro-fonts', MEPR_CSS_URL . '/readylaunch/fonts.css', null, MEPR_VERSION);
            wp_enqueue_style('mp-pro-account', MEPR_CSS_URL . '/readylaunch/account.css', ['mp-pro-fonts'], MEPR_VERSION);
            wp_enqueue_script('alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', [], MEPR_VERSION, true);
            wp_enqueue_script('mepr-accountjs', MEPR_JS_URL . '/readylaunch/account.js', ['jquery'], MEPR_VERSION, true);

            wp_localize_script(
                'mepr-accountjs',
                'MeprAccount',
                [
                    'ajax_url'    => admin_url('admin-ajax.php'),
                    'nonce'       => wp_create_nonce('mepr_account_update'),
                    'account_url' => MeprUtils::get_current_url_without_params(),
                ]
            );
        }

        if ('pricing' === $page) {
            wp_enqueue_style('mp-pro-pricing', MEPR_CSS_URL . '/readylaunch/pricing.css', null, MEPR_VERSION);
            wp_enqueue_script('mepr-pro-pricing', MEPR_JS_URL . '/readylaunch/pricing.js', ['jquery'], MEPR_VERSION, true);
            wp_enqueue_script('alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', [], MEPR_VERSION, true);
        }

        if ('checkout' === $page) {
            wp_enqueue_style('mp-pro-checkout', MEPR_CSS_URL . '/readylaunch/checkout.css', null, MEPR_VERSION);
            wp_enqueue_script('mepr-signupjs', MEPR_JS_URL . '/readylaunch/signup.js', ['jquery'], MEPR_VERSION, true);
            wp_enqueue_script('alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', [], MEPR_VERSION, true);

            wp_localize_script(
                'mepr-signupjs',
                'MeprProTemplateSignup',
                [
                    'spc_enabled' => true,
                ]
            );
        }

        if ('thankyou' === $page) {
            wp_enqueue_style('mp-pro-checkout', MEPR_CSS_URL . '/readylaunch/checkout.css', null, MEPR_VERSION);
        }
    }

    /**
     * Adds the view path for the slug.
     *
     * @param  array  $paths         The paths.
     * @param  string $slug          The slug.
     * @param  array  $allowed_slugs The allowed slugs.
     * @return array
     */
    public function add_view_path_for_slug($paths, $slug, $allowed_slugs = [])
    {
        if (in_array($slug, $allowed_slugs, true) || empty($allowed_slugs)) {
            array_splice($paths, 1, 0, MEPR_PATH . '/app/views/readylaunch');
        }
        return $paths;
    }

    /**
     * Whether the front-end should hide the admin bar for full-page ReadyLaunch / ReadyLaunch views.
     *
     * Uses {@see MeprReadyLaunchAssetsHelper::should_enqueue_login_bundle()} and
     * {@see MeprReadyLaunchAssetsHelper::should_enqueue_account_bundle()} so FSE MemberPress templates
     * match classic template toggles for this behavior.
     *
     * @return boolean
     */
    public static function should_hide_admin_bar_on_front(): bool
    {
        return self::template_enabled('pricing')
            || MeprReadyLaunchAssetsHelper::should_enqueue_login_bundle()
            || MeprReadyLaunchAssetsHelper::should_enqueue_account_bundle()
            || self::template_enabled('checkout')
            || self::template_enabled('thankyou');
    }

    /**
     * Only remove admin bar when on readylaunch pro templates
     *
     * @param  boolean $show The show.
     * @return boolean
     */
    public function remove_admin_bar($show)
    {
        if (self::should_hide_admin_bar_on_front()) {
            $show = false;
        }

        return $show;
    }

    /**
     * Change the cant purchase message template
     *
     * @param  string $str Purchase message.
     * @return string
     */
    public function cant_purchase_message($str)
    {
        $errors[] = $str;

        // Is ReadyLaunch™️ checkout template is enabled?
        if (self::template_enabled('checkout')) {
            $str = MeprView::get_string('/readylaunch/shared/errors', get_defined_vars());
        }

        return '<div class="flex-centered">' . $str . '</div>';
    }

    /**
     * Checks if we should override the page template
     *
     * @param  string $template Template name.
     * @return boolean
     */
    public static function template_enabled($template)
    {
        global $post;
        global $wp_query;

        $page_name = $template . '_page_id';
        $options   = MeprOptions::fetch();

        if ('pricing' === $template) {
            return isset($post) &&
            is_a($post, 'WP_Post') &&
            $post->post_type === MeprGroup::$cpt &&
            self::template_active('pricing');
        }

        if ('checkout' === $template) {
            return self::template_active('checkout') &&
            ( isset($post) && is_a($post, 'WP_Post') && is_singular(MeprProduct::$cpt) );
        }

        return isset($wp_query) &&
        isset($options->$page_name) &&
        is_page($options->$page_name) &&
        self::template_active($template);
    }

    /**
     * Determines if the given template is active.
     *
     * @param string $template The template name (e.g. 'pricing', 'checkout', etc.).
     *
     * @return boolean
     */
    public static function template_active($template)
    {
        $options        = MeprOptions::fetch();
        $attribute_name = 'design_enable_' . $template . '_template';

        return isset($options->$attribute_name) && filter_var($options->$attribute_name, FILTER_VALIDATE_BOOLEAN);
    }

    /**
     * AJAX: Get the editable field HTML
     *
     * @return void.
     */
    public function account_profile_editable_fields()
    {
        check_ajax_referer('mepr_account_update', 'nonce');

        $user      = MeprUtils::get_currentuserinfo();
        $field_key = isset($_POST['field']) ? sanitize_text_field(wp_unslash($_POST['field'])) : '';

        $custom_fields = MeprUsersHelper::get_custom_fields();

        $key = array_search($field_key, array_column($custom_fields, 'field_key'), true);
        if (false === $key) {
            wp_send_json_error(null, 404);
        }

        $field = $custom_fields[ $key ];
        $value = $user ? get_user_meta($user->ID, $field->field_key, true) : '';

        ob_start();
        echo '<div><label class="mepr_modal_form__label">' . esc_html($field->field_name) . '</label></div>';
        echo MeprUsersHelper::render_custom_field($field, $value); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
        $content = ob_get_clean();

        wp_send_json_success($content);
    }

    /**
     * Validates the account fields.
     *
     * @param  array  $errors    The errors.
     * @param  object $user      The user.
     * @param  string $field_key The field key.
     * @return array
     */
    public function validate_account_fields($errors, $user, $field_key)
    {
        $mepr_options = MeprOptions::fetch();

        $errors = MeprUsersCtrl::validate_extra_profile_fields(null, true, $user, false, false, $field_key);

        // Validate first name and last name.
        if (isset($_POST['first_name']) || isset($_POST['last_name'])) {
            if ($mepr_options->require_fname_lname && (empty($_POST['first_name']) || empty($_POST['last_name']))) {
                $errors[] = __('You must enter both your First and Last name', 'memberpress');
            }
        }

        if (isset($_POST['user_email'])) {
            if (empty($_POST['user_email']) || !is_email(wp_unslash($_POST['user_email']))) {
                $errors[] = __('You must enter a valid email address', 'memberpress');
            }

            // Old email is not the same as the new, so let's make sure no else has it
            // $user = MeprUtils::get_currentuserinfo(); //Old user info is here since we haven't stored the new stuff yet.
            if ($user !== false && $user->user_email !== sanitize_email(wp_unslash($_POST['user_email'])) && email_exists(sanitize_email(wp_unslash($_POST['user_email'])))) {
                $errors[] = __('This email is already in use by another member', 'memberpress');
            }
        }

        return $errors;
    }

    /**
     * Load more subscriptions.
     *
     * @return void
     */
    public function load_more_subscriptions()
    {
        check_ajax_referer('mepr_account_update', 'nonce');

        $count     = isset($_POST['count']) ? absint(wp_unslash($_POST['count'])) : 1;
        $acct_ctrl = MeprCtrlFactory::fetch('account');

        ob_start();

        $acct_ctrl->subscriptions(
            '',
            [],
            [
                'mode'  => 'readylaunch',
                'count' => $count,
            ]
        );

        $content = ob_get_clean();
        wp_send_json_success($content);
    }

    /**
     * Load more payments.
     *
     * @return void
     */
    public function load_more_payments()
    {
        check_ajax_referer('mepr_account_update', 'nonce');

        $count     = isset($_POST['count']) ? absint(wp_unslash($_POST['count'])) : 1;
        $acct_ctrl = MeprCtrlFactory::fetch('account');

        ob_start();
        $acct_ctrl->payments(
            [
                'mode'  => 'readylaunch',
                'count' => $count,
            ]
        );

        $content = ob_get_clean();
        wp_send_json_success($content);
    }

    /**
     * Theme style.
     *
     * @return void
     */
    public static function theme_style()
    {
        $mepr_options = MeprOptions::fetch();

        $raw_primary    = ! empty($mepr_options->design_primary_color) ? $mepr_options->design_primary_color : '#06429e';
        $primary_color  = sanitize_hex_color($raw_primary) ?: '#06429e';
        $text_color     = self::getContrastColor($primary_color);
        $current_screen = is_admin() ? get_current_screen() : '';

        $is_block_editor = (
            isset($current_screen) && ! empty($current_screen) &&
            method_exists($current_screen, 'is_block_editor') &&
            $current_screen->is_block_editor()
        );

        // FSE drives the account sidebar background via --mepr-rl-shell-menu-bg (brand default + per-block "Menu Background" override); suppress the legacy !important rule below for FSE so it can't beat that override.
        $fse_active = class_exists('MeprFseHelper') && MeprFseHelper::is_fse_mode_active();

        if (
            self::template_enabled('pricing') ||
            self::template_enabled('login') ||
            self::template_enabled('account') ||
            self::template_enabled('checkout') ||
            self::template_enabled('thankyou') ||
            MeprHooks::apply_filters('mepr_rl_theme_style', false) ||
            $is_block_editor
        ) { // Full page templates.
            $html  = '<style type="text/css">';
            $html .= sprintf('body.mepr-guest-layout{background:%s!important}', $primary_color);
            $html .= sprintf('.app-layout .site-header, .guest-layout .site-header{background:%s!important}', $primary_color);
            if (! $fse_active) {
                $html .= sprintf('#mepr-account-nav{background:%s!important}', $primary_color);
            }
            $html .= sprintf('.mepr-price-menu .mepr-price-boxes .mepr-most-popular{background:%s!important}', $primary_color);
            $html .= sprintf('#mepr-account-nav .mepr-nav-item a{color:rgba(%s)}', self::hexToRgb($text_color, 0.7));
            $html .= sprintf('#mepr-account-nav .mepr-nav-item a:hover{color:%s}', $text_color);
            $html .= sprintf('.app-layout .profile-menu__text, .guest-layout .profile-menu__text, .app-layout .profile-menu__arrow_down, .guest-layout .profile-menu__arrow_down{color:%s}', $text_color);
            $html .= sprintf('.app-layout .profile-menu__text--small, .guest-layout .profile-menu__text--small{color:rgba(%s)}', self::hexToRgb($text_color, 0.7));
            $html .= '</style>';

            echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
        }

        if (is_singular() && has_block('memberpress/pro-account-tabs')) {
            $html  = '<style type="text/css">';
            $html .= sprintf('#mepr-account-nav{background:%s!important}', $primary_color);
            $html .= sprintf('#mepr-account-nav .mepr-nav-item a{color:rgba(%s)}', self::hexToRgb($text_color, 0.7));
            $html .= '</style>';

            echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
        }
    }

    /**
     * Add the header.
     *
     * @param  array $args The arguments.
     * @return void
     */
    public static function add_header($args)
    {
        extract($args, EXTR_SKIP);
        MeprView::render('/readylaunch/layout/header', get_defined_vars());
    }

    /**
     * Add the footer.
     *
     * @param  array $args The arguments.
     * @return void
     */
    public static function add_footer($args)
    {
        extract($args, EXTR_SKIP);
        MeprView::render('/readylaunch/layout/footer', get_defined_vars());
    }

    /**
     * Get the contrast color.
     *
     * @param  string $hex_color The hex color.
     * @return string
     */
    public static function getContrastColor($hex_color)
    {
        $hex_color     = trim($hex_color);
        $tmp_hex_color = trim($hex_color, '#');
        if (! ctype_xdigit($tmp_hex_color)) { // Validate HEX code.
            $hex_color = '#FFFFFF'; // Fallback to white color.
        }

        // HexColor RGB.
        $r1 = hexdec(substr($hex_color, 1, 2));
        $g1 = hexdec(substr($hex_color, 3, 2));
        $b1 = hexdec(substr($hex_color, 5, 2));

        // Black RGB.
        $black_color    = '#000000';
        $r2_black_color = hexdec(substr($black_color, 1, 2));
        $g2_black_color = hexdec(substr($black_color, 3, 2));
        $b2_black_color = hexdec(substr($black_color, 5, 2));

        // Calc contrast ratio.
        $l1 = 0.2126 * pow($r1 / 255, 2.2) +
        0.7152 * pow($g1 / 255, 2.2) +
        0.0722 * pow($b1 / 255, 2.2);

        $l2 = 0.2126 * pow($r2_black_color / 255, 2.2) +
        0.7152 * pow($g2_black_color / 255, 2.2) +
        0.0722 * pow($b2_black_color / 255, 2.2);

        $contrast_ratio = 0;
        if ($l1 > $l2) {
            $contrast_ratio = (int) ( ( $l1 + 0.05 ) / ( $l2 + 0.05 ) );
        } else {
            $contrast_ratio = (int) ( ( $l2 + 0.05 ) / ( $l1 + 0.05 ) );
        }

        // If contrast is more than 5, return black color.
        if ($contrast_ratio > 5) {
            return '#000000';
        } else {
            // If not, return white color.
            return '#FFFFFF';
        }
    }

    /**
     * Hex to RGB.
     *
     * @param  string  $hex   The hex.
     * @param  boolean $alpha The alpha.
     * @return string
     */
    public static function hexToRgb($hex, $alpha = false)
    {
        $hex      = str_replace('#', '', $hex);
        $length   = strlen($hex);
        $rgb['r'] = hexdec($length === 6 ? substr($hex, 0, 2) : ( $length === 3 ? str_repeat(substr($hex, 0, 1), 2) : 0 ));
        $rgb['g'] = hexdec($length === 6 ? substr($hex, 2, 2) : ( $length === 3 ? str_repeat(substr($hex, 1, 1), 2) : 0 ));
        $rgb['b'] = hexdec($length === 6 ? substr($hex, 4, 2) : ( $length === 3 ? str_repeat(substr($hex, 2, 1), 2) : 0 ));
        if ($alpha) {
            $rgb['a'] = $alpha;
        }
        return implode(',', $rgb);
    }

    /**
     * Checks if we should enqueue the FSE ReadyLaunch styles
     *
     * @return boolean
     */
    private static function should_enqueue_fse_readylaunch_styles(): bool
    {
        $current_screen = is_admin() && function_exists('get_current_screen') ?
            get_current_screen() : '';
        $fse_templates  = MeprCtrlFactory::fetch('FseTemplates');
        return $fse_templates->can_run() ?
            MeprHooks::apply_filters(
                'mepr_rl_should_enqueue_fse_readylaunch_styles_on_front',
                $fse_templates->post_has_memberpress_fse_template()
                    || $fse_templates->request_uses_memberpress_fse_cpt_option_template()
            ) ||
            (
                $current_screen instanceof \WP_Screen &&
                $current_screen->is_block_editor()
            ) :
        false;
    }
}
