<?php
/**
 * Main admin controller for admin area initialization.
 *
 * @package Awesomemotive\Rewardswp\Controllers\Admin
 * @since 1.0.0
 */

namespace Awesomemotive\Rewardswp\Controllers\Admin;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use Awesomemotive\Rewardswp\Factories\OptionsFactory;
use Awesomemotive\Rewardswp\Factories\ViewsFactory;
use Awesomemotive\Rewardswp\Helpers\IconHelper;
use Awesomemotive\Rewardswp\Interfaces\ControllerInterface;
use Awesomemotive\Rewardswp\Interfaces\ViewInterface;
use Awesomemotive\Rewardswp\Services\AssetsService;
use Awesomemotive\Rewardswp\Services\NotificationService;
use Awesomemotive\Rewardswp\Services\RewardTypesService;
use Awesomemotive\Rewardswp\Services\ThemeCSSRemoverService;
use Awesomemotive\Rewardswp\Settings\Options\General\BrandColorOption;
use Awesomemotive\Rewardswp\Settings\Options\RewardsWidget\LauncherPositionOption;
use Awesomemotive\Rewardswp\Settings\Options\Referrals\ReferralVariableOption;
use Awesomemotive\Rewardswp\Settings\Options\Referrals\RewardsOption;
use Awesomemotive\Rewardswp\Views\Admin\AdminHeaderView;
use Awesomemotive\Rewardswp\Views\Admin\Components\NotificationDrawerComponentView;
use Awesomemotive\Rewardswp\Views\Panel\Advocate\RewardsWidgetView;
use Awesomemotive\Rewardswp\Views\Admin\Components\ToastView;
use Awesomemotive\Rewardswp\Views\Panel\RewardTypes\BaseRewardView;
use Awesomemotive\Rewardswp\Views\Panel\RewardTypes\DiscountCodeView;
use Awesomemotive\Rewardswp\Controllers\Admin\DashboardAdminController;
use Awesomemotive\Rewardswp\Services\MockDataService;
use DI\Container;
use DI\DependencyException;
use DI\NotFoundException;

use function Awesomemotive\Rewardswp\Helpers\can_access_rewardswp_feature;
use function Awesomemotive\Rewardswp\Helpers\get_available_rewardswp_features;
use function Awesomemotive\Rewardswp\Helpers\get_human_readable_date;
use function Awesomemotive\Rewardswp\Helpers\get_option_value;
use function Awesomemotive\Rewardswp\Helpers\get_rewardswp_plan_tier;
use function Awesomemotive\Rewardswp\Helpers\get_rewardswp_setup_template;
use function Awesomemotive\Rewardswp\Helpers\get_site_currency_symbol;
use function Awesomemotive\Rewardswp\Helpers\get_currency_position;
use function Awesomemotive\Rewardswp\Helpers\is_rewardswp_admin_page;
use function Awesomemotive\Rewardswp\Helpers\is_rewardswp_lite_plan;
use function Awesomemotive\Rewardswp\Helpers\is_rewardswp_pro_plan;
use function Awesomemotive\Rewardswp\Helpers\get_upgrade_url;

class MainAdminController implements ControllerInterface {
	/**
	 * Main admin menu slug.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	const MAIN_MENU_SLUG = 'rewardswp';

	/**
	 * Main admin menu position.
	 *
	 * @since 1.0.0
	 *
	 * @var int
	 */
	const MAIN_MENU_POSITION = 30;

	/**
	 * Upgrade submenu position.
	 *
	 * @since 1.0.0
	 *
	 * @var int
	 */
	const UPGRADE_SUBMENU_POSITION = 100;

	/**
	 * AJAX nonce for admin operations.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	const AJAX_NONCE = 'rewardswp-admin-nonce';

	/**
	 * Dismiss upgrade notice action name.
	 *
	 * @since 1.0.0
	 *
	 * @var string Action name for dismissing the upgrade notice.
	 */
	const DISMISS_UPGRADE_NOTICE_ACTION = 'rewardswp_dismiss_upgrade_notice';

	/**
	 * AJAX action name for rendering widget preview.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	const RENDER_WIDGET_PREVIEW_ACTION = 'rewardswp_render_widget_preview';

	/**
	 * DI container instance.
	 *
	 * @since 1.0.0
	 *
	 * @var Container
	 */
	private Container $container;

	/**
	 * Assets service instance.
	 *
	 * @since 1.0.0
	 *
	 * @var AssetsService
	 */
	private AssetsService $assets_service;

	/**
	 * Reward types service instance.
	 *
	 * @since 1.0.0
	 *
	 * @var RewardTypesService
	 */
	private RewardTypesService $reward_types_service;

	/**
	 * Options factory instance.
	 *
	 * @since 1.0.0
	 *
	 * @var OptionsFactory
	 */
	private OptionsFactory $options_factory;

	/**
	 * Views factory instance.
	 *
	 * @since 1.0.0
	 *
	 * @var ViewsFactory
	 */
	private ViewsFactory $views_factory;

	/**
	 * Notification service instance.
	 *
	 * @since 1.0.0
	 *
	 * @var NotificationService
	 */
	private NotificationService $notification_service;

	/**
	 * List of controller classes to be executed.
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	private array $controllers;

	/**
	 * Icon helper instance.
	 *
	 * @since 1.0.0
	 *
	 * @var IconHelper
	 */
	private IconHelper $icon_helper;

	/**
	 * Theme CSS remover service instance.
	 *
	 * @since 1.1.0
	 *
	 * @var ThemeCSSRemoverService
	 */
	private ThemeCSSRemoverService $theme_css_remover;

	/**
	 * Mock data service instance.
	 *
	 * @since 1.1.0
	 *
	 * @var MockDataService
	 */
	private MockDataService $mock_data_service;

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 *
	 * @param Container              $container
	 * @param AssetsService          $assets_service
	 * @param RewardTypesService     $reward_types_service
	 * @param OptionsFactory         $options_factory
	 * @param ViewsFactory           $views_factory
	 * @param IconHelper             $icon_helper
	 * @param NotificationService    $notification_service
	 * @param ThemeCSSRemoverService $theme_css_remover
	 *
	 * @throws NotFoundException|DependencyException If fails to get resource from container.
	 */
	public function __construct(
		Container $container,
		AssetsService $assets_service,
		RewardTypesService $reward_types_service,
		OptionsFactory $options_factory,
		ViewsFactory $views_factory,
		IconHelper $icon_helper,
		NotificationService $notification_service,
		ThemeCSSRemoverService $theme_css_remover,
		MockDataService $mock_data_service
	) {
		$this->container            = $container;
		$this->assets_service       = $assets_service;
		$this->reward_types_service = $reward_types_service;
		$this->options_factory      = $options_factory;
		$this->views_factory        = $views_factory;
		$this->controllers          = $container->get( 'admin_controllers_classes' );
		$this->icon_helper          = $icon_helper;
		$this->notification_service = $notification_service;
		$this->theme_css_remover    = $theme_css_remover;
		$this->mock_data_service    = $mock_data_service;
	}

	/**
	 * Load controllers.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 * @throws DependencyException
	 * @throws NotFoundException
	 */
	public function init() {
		add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
		add_action( 'admin_print_scripts', [ $this, 'hide_non_rewardswp_notices' ] );
		add_action( 'admin_head', [ $this, 'hide_non_rewardswp_notices' ] );
		add_action( 'in_admin_header', [ $this, 'admin_header' ], 1 );
		add_action( 'admin_footer', [ $this, 'render_notification_drawer' ] );
		add_action( 'admin_menu', [ $this, 'register_menu' ] );
		add_action( 'admin_menu', [ $this, 'action_maybe_add_upgrade_menu_item' ], self::UPGRADE_SUBMENU_POSITION );
		add_action( 'admin_init', [ $this, 'maybe_redirect_to_setup_page' ], 1 );
		add_action( 'admin_menu', [ $this, 'reorder_upgrade_menu_item' ], 999 );
		add_action( 'admin_print_footer_scripts', [ $this, 'upgrade_menu_item_new_tab_script' ] );
		add_action( 'admin_enqueue_scripts', [ $this, 'action_print_custom_css_variables' ], 20 );

		// AJAX Actions.
		add_action( 'wp_ajax_' . self::DISMISS_UPGRADE_NOTICE_ACTION, [ $this, 'ajax_dismiss_upgrade_notice' ] );
		add_action( 'wp_ajax_' . self::RENDER_WIDGET_PREVIEW_ACTION, [ $this, 'ajax_render_widget_preview' ] );

		// Add action to conditionally remove screen options.
		add_action( 'current_screen', [ $this, 'maybe_hide_screen_options' ] );

		add_filter( 'admin_footer_text', [ $this, 'admin_footer' ], 1, 2 );
		add_filter(
			sprintf(
				'plugin_action_links_%s',
				$this->container->get( 'plugin_info' )['basename']
			),
			[ $this, 'filter_add_plugin_links' ]
		);

		// Load controllers.
		foreach ( $this->controllers as $controller_class ) {
			$this->container->get( $controller_class )->init();
		}
	}

	/**
	 * Register the upgrade Menu for Lite plans.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function action_maybe_add_upgrade_menu_item(): void {
		if ( ! is_rewardswp_lite_plan() ) {
			return;
		}

		add_submenu_page(
			self::MAIN_MENU_SLUG,
			esc_html__( 'Upgrade to Pro', 'rewardswp' ),
			esc_html__( 'Upgrade to Pro', 'rewardswp' ),
			'manage_options',
			'rewardswp-upgrade-to-pro',
			'',
			self::UPGRADE_SUBMENU_POSITION
		);
	}

	/**
	 * (Maybe) Redirect to the setup page.
	 *
	 * Redirects new users to the setup wizard on first activation.
	 * Uses admin_init hook (not init) to ensure this runs on subsequent
	 * page loads after activation, avoiding "headers already sent" issues
	 * during ZIP plugin installation.
	 *
	 * @since AFFWPN
	 */
	public function maybe_redirect_to_setup_page(): void {
		// Don't redirect during AJAX, cron, or REST requests.
		if ( wp_doing_ajax() || wp_doing_cron() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
			return;
		}

		// Don't redirect if we're already on the setup page (dashboard with ?setup=1).
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( isset( $_GET['page'] ) && 'rewardswp' === $_GET['page'] && isset( $_GET['setup'] ) ) {
			return;
		}

		$setup_template = get_rewardswp_setup_template();

		if ( false !== get_option( sprintf( 'rewardswp_%s_setup_initiated', $setup_template ), false ) ) {
			return; // The setup has been initiated before.
		}

		// Only redirect on fresh activation, not on every admin page load.
		// Check if this is the first admin load after activation by looking for the transient.
		if ( ! get_transient( 'rewardswp_activation_redirect' ) ) {
			return;
		}

		// Validate setup endpoint is available before redirecting.
		$setup_url = sprintf(
			defined( 'REWARDSWP_LARAVEL_APP_REWARDSWP_SETUP_URL' )
				? trailingslashit( untrailingslashit( REWARDSWP_LARAVEL_APP_REWARDSWP_SETUP_URL ) . '/test' )
				: 'https://app.rewardswp.com/setup/%s/test/',
			$setup_template
		);

		$response = wp_remote_get( $setup_url );

		if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
			return; // Setup endpoint not available, don't redirect.
		}

		// Delete the transient so we only redirect once.
		delete_transient( 'rewardswp_activation_redirect' );

		// Redirect to the dashboard with setup dialog.
		wp_safe_redirect( admin_url( 'admin.php?page=rewardswp&setup=1' ) );
		exit;
	}

	/**
	 * Register the main menu.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function register_menu(): void {
		add_menu_page(
			esc_html__( 'Dashboard', 'rewardswp' ), // Page title for the screen.
			esc_html__( 'RewardsWP', 'rewardswp' ), // Menu title in the sidebar.
			'manage_options',
			self::MAIN_MENU_SLUG, // 'rewardswp-dashboard'.
			'', // Callback is handled by the first submenu item.
			$this->icon_helper->get_icon( 'menu-icon', '', 'regular', true ),
			self::MAIN_MENU_POSITION
		);

		// Add Dashboard as the first submenu item, making it the default page for the main menu item.
		add_submenu_page(
			self::MAIN_MENU_SLUG, // Parent slug.
			esc_html__( 'Dashboard', 'rewardswp' ), // Page title.
			esc_html__( 'Dashboard', 'rewardswp' ), // Menu title.
			'manage_options', // Capability.
			self::MAIN_MENU_SLUG, // Menu slug (same as parent to make it the default).
			[ $this->container->get( DashboardAdminController::class ), 'render_page' ], // Callback.
			1 // Position.
		);
	}

	/**
	 * Register global CSS and JS.
	 *
	 * Just register, enqueue must be done when necessary.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function enqueue_scripts(): void {
		if ( ! is_rewardswp_admin_page() ) {
			return; // Don't do anything if it is not a RewardsWP admin page.
		}

		$earn_drawer = $this->views_factory->get_view( \Awesomemotive\Rewardswp\Views\Admin\Parts\EarnPointsActionsDrawerView::class );
		$earn_data   = [];
		if ( $earn_drawer instanceof \Awesomemotive\Rewardswp\Interfaces\ScriptDataProviderInterface ) {
			$earn_data = $earn_drawer->get_script_data();
		}

		$spend_drawer = $this->views_factory->get_view( \Awesomemotive\Rewardswp\Views\Admin\Parts\SpendPointsActionsDrawerView::class );
		$spend_data   = [];
		if ( $spend_drawer instanceof \Awesomemotive\Rewardswp\Interfaces\ScriptDataProviderInterface ) {
			$spend_data = $spend_drawer->get_script_data();
		}

		$referrals_drawer = $this->views_factory->get_view( \Awesomemotive\Rewardswp\Views\Admin\Parts\ReferralsRewardsDrawerView::class );
		$referrals_data   = [];
		if ( $referrals_drawer instanceof \Awesomemotive\Rewardswp\Interfaces\ScriptDataProviderInterface ) {
			$referrals_data = $referrals_drawer->get_script_data();
		}

		$data = wp_json_encode(
			[
				'ajaxUrl'             => admin_url( 'admin-ajax.php' ),
				'mainCssUrl'          => $this->assets_service->get_file_url( $this->assets_service->get_main_css_filename() ),
				'brandColor'          => get_option_value( BrandColorOption::class ),
				'launcherPosition'    => $this->options_factory->get_option( LauncherPositionOption::class )->get_default_value(),
				'launcherOffset'      => [
					'horizontal' => 24,
					'vertical'   => 24,
				],
				'nonce'               => wp_create_nonce( self::AJAX_NONCE ),
				'isLoggedIn'          => is_user_logged_in(),
				'rewardTypes'         => $this->reward_types_service->get_reward_types_data(),
				'shouldOpenPanel'     => ! empty(
					filter_input(
						INPUT_GET,
						$this->options_factory->get_option( ReferralVariableOption::class )->get_value()
					)
				),
				'dismissNoticeAction' => self::DISMISS_UPGRADE_NOTICE_ACTION,
			]
		);

		// Admin scripts and styles.
		$this->assets_service->enqueue_script( $this->assets_service::ADMIN_SCRIPT_HANDLE );
		$this->assets_service->enqueue_style( $this->assets_service::ADMIN_STYLE_HANDLE );

		// Global config data (plan tier, currency, etc.).
		// Symbol position is a page-level constant, so it lives on the global config and every
		// admin money preview reads it through the JS applyCurrencySymbol() helper.
		$currency_position = get_currency_position();
		wp_localize_script(
			$this->assets_service::ADMIN_SCRIPT_HANDLE,
			'rewardswpConfig',
			[
				'planTier'                    => get_rewardswp_plan_tier(),
				'currencySymbol'              => get_site_currency_symbol(),
				'currencyPosition'            => $currency_position['position'],
				'currencySpace'               => $currency_position['space'],
				'hasProFeatures'              => can_access_rewardswp_feature( 'points_expiry' ),
				'hasRewardExpiration'         => can_access_rewardswp_feature( 'reward_expiration' ),
				'hasReferralRewardExpiration' => can_access_rewardswp_feature( 'referral_reward_expiration' ),
				'accessibleFeatures'          => get_available_rewardswp_features(),
			]
		);

		if ( ! empty( $earn_data ) || ! empty( $spend_data ) || ! empty( $referrals_data ) ) {
			$drawer_data = [];
			if ( ! empty( $earn_data ) ) {
				$drawer_data['earn'] = $earn_data;
			}
			if ( ! empty( $spend_data ) ) {
				$drawer_data['spend'] = $spend_data;
			}
			if ( ! empty( $referrals_data ) ) {
				$drawer_data['referrals'] = $referrals_data;
			}
			wp_localize_script(
				$this->assets_service::ADMIN_SCRIPT_HANDLE,
				'rewardswpDrawerData',
				$drawer_data
			);
		}

		// Localize script with AJAX data for widget preview (only on settings page).
		$screen = get_current_screen();
		if ( $screen && strpos( $screen->id, 'rewardswp' ) !== false && strpos( $screen->id, 'settings' ) !== false ) {
			// The script is already included in admin.js via import, just localize it.
			wp_localize_script(
				$this->assets_service::ADMIN_SCRIPT_HANDLE,
				'rewardswpWidgetPreviewData',
				[
					'action'  => self::RENDER_WIDGET_PREVIEW_ACTION,
					'nonce'   => wp_create_nonce( self::AJAX_NONCE ),
					'ajaxUrl' => admin_url( 'admin-ajax.php' ),
				]
			);

			// Add rewardsWP object for checklist AJAX calls on settings page.
			wp_localize_script(
				$this->assets_service::ADMIN_SCRIPT_HANDLE,
				'rewardsWP',
				[
					'nonce' => wp_create_nonce( 'rewardswp-nonce' ),
				]
			);
		}

		// Add inline script data for admin pages too.
		wp_add_inline_script(
			$this->assets_service::ADMIN_SCRIPT_HANDLE,
			"const rewardswpMainVars=$data",
			'before'
		);

		// Add the panel open variable for admin pages (settings page preview).
		wp_add_inline_script(
			$this->assets_service::ADMIN_SCRIPT_HANDLE,
			"window.rewardswpOpenPanelOnPageLoad = typeof rewardswpOpenPanelOnPageLoad !== 'undefined' ? rewardswpOpenPanelOnPageLoad : true;",
			'before'
		);
	}

	/**
	 * Print custom CSS variables.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function action_print_custom_css_variables(): void {
		$this->assets_service->print_brand_css_variables();
	}

	/**
	 * Simple removes any non-RewardsWP notices from our admin screens.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function hide_non_rewardswp_notices(): void {
		if ( ! is_rewardswp_admin_page() ) {
			return; // Bail if not a RewardsWP screen.
		}

		// Remove all notices.
		remove_all_actions( 'admin_notices' );
		remove_all_actions( 'all_admin_notices' );
	}

	/**
	 * Print the admin header common to all RewardsWP admin pages.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function admin_header(): void {
		if ( ! is_rewardswp_admin_page() ) {
			return;
		}

		$admin_header = $this->views_factory->get_view( AdminHeaderView::class );

		if ( ! is_subclass_of( $admin_header, ViewInterface::class ) ) {
			return;
		}

		$admin_header->render(
			[
				'show_upgrade_notice' => (
					is_rewardswp_lite_plan() &&
					empty(
						get_user_meta(
							get_current_user_id(),
							'rewardswp_upgrade_notice_dismissed',
							true
						)
					)
				),
			]
		);
	}

	/**
	 * Render the notification drawer in the admin footer.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function render_notification_drawer(): void {
		if ( ! is_rewardswp_admin_page() ) {
			return;
		}

		// Render toast component globally for all RewardsWP admin pages.
		$toast_view = $this->views_factory->get_view( ToastView::class );
		if ( is_subclass_of( $toast_view, ViewInterface::class ) ) {
			$toast_view->render();
		}

		$notification_drawer = $this->views_factory->get_view( NotificationDrawerComponentView::class );

		if ( ! is_subclass_of( $notification_drawer, ViewInterface::class ) ) {
			return;
		}

		$notification_drawer->render(
			[
				'notifications_state' => $this->notification_service->get_notifications_state(),
			]
		);
	}

	/**
	 * Display the Reward Panel on the settings page.
	 *
	 * The preview always starts in "logged_out" state. Switching between logged_out
	 * and logged_in is handled entirely by Alpine.js via state changes, eliminating
	 * the need for AJAX calls when toggling the preview state.
	 *
	 * @since 1.0.0
	 *
	 * @param string $preview_state Deprecated. Preview state is now controlled by Alpine.js.
	 * @param array  $preview_args Optional. Preview arguments to override default settings.
	 * @return void
	 */
	public function render_settings_panel( string $preview_state = 'logged_out', array $preview_args = [] ): void {
		$referral_slug = $this->options_factory->get_option( ReferralVariableOption::class );

		$available_rewards_demo = $this->mock_data_service->get_preview_available_rewards();
		$preview_member         = $this->mock_data_service->get_preview_member();

		// Get referral rewards config to check if friend reward is configured.
		$referral_rewards  = get_option_value( RewardsOption::class );
		$friend_reward     = $referral_rewards['friend'] ?? null;
		$has_friend_reward = ! empty( $friend_reward ) && ! empty( $friend_reward['type'] );

		/*
		 * Sample activity dates for the preview, formatted through the same helper
		 * the live widget uses so they render and translate exactly like real
		 * entries. Kept within a week so they stay relative ("today", "3 days ago")
		 * rather than falling back to an absolute date. Uses wp_date() (site
		 * timezone), not gmdate() (UTC): get_human_readable_date() parses the string
		 * with wp_timezone(), so a UTC string would shift dates across day
		 * boundaries on non-UTC sites.
		 */
		$preview_date = static function ( string $offset ): string {
			return get_human_readable_date( wp_date( 'Y-m-d H:i:s', strtotime( $offset ) ), true, false );
		};

		$args = [
			'referral_link'           => add_query_arg( $referral_slug->get_value(), 'ABC123', home_url( '/' ) ),
			'is_admin_preview'        => true,
			'has_friend_reward'       => $has_friend_reward,
			'points_balance'          => 0, // Start as logged out (0 points)
			'member_id'               => 0, // Start as logged out (no member)
			'available_rewards_count' => 0, // Start as logged out (no rewards)
			'available_rewards'       => [], // Start as logged out (no rewards)
			'friends_referred'        => 0, // Start as logged out
			// Mock member data for Alpine.js to use when switching to "Logged In" state.
			'mock_member_data'        => [
				'member_id'               => 1, // Fake member ID for preview.
				'points_balance'          => 1250,
				'user_name'               => $preview_member->get_first_name() ?: __( 'John', 'rewardswp' ),
				'user_email'              => $preview_member->email,
				'referral_link'           => add_query_arg( $referral_slug->get_value(), 'ABC123', home_url( '/' ) ),
				'available_rewards'       => $available_rewards_demo,
				'available_rewards_count' => count( $available_rewards_demo ),
				'friends_referred'        => 8,
			],
			// Mock activity data — curated happy path for store owner preview.
			// Math: 100+200+500+50+500-500+400 = 1,250 approved balance (+225 pending).
			'activity'                => [
				'points'    => [
					'items'         => [
						[ 'id' => 1, 'action' => __( 'Earned', 'rewardswp' ), 'target' => __( 'Made a purchase', 'rewardswp' ), 'points' => 400, 'is_deduction' => false, 'date' => $preview_date( 'now' ), 'status' => 'approved' ],
						[ 'id' => 2, 'action' => __( 'Spent', 'rewardswp' ), 'target' => sprintf( /* translators: %s: product name */ __( 'Redeemed: %s', 'rewardswp' ), 'Hydrating Rose Mist' ), 'points' => 500, 'is_deduction' => true, 'date' => $preview_date( '-1 day' ), 'status' => 'approved' ],
						[ 'id' => 3, 'action' => __( 'Earned', 'rewardswp' ), 'target' => __( 'Referred a friend', 'rewardswp' ), 'points' => 500, 'is_deduction' => false, 'date' => $preview_date( '-2 days' ), 'status' => 'approved' ],
						[ 'id' => 4, 'action' => __( 'Earned', 'rewardswp' ), 'target' => __( 'Made a purchase', 'rewardswp' ), 'points' => 225, 'is_deduction' => false, 'date' => $preview_date( '-3 days' ), 'status' => 'pending' ],
						[ 'id' => 5, 'action' => __( 'Earned', 'rewardswp' ), 'target' => __( 'Wrote a product review', 'rewardswp' ), 'points' => 50, 'is_deduction' => false, 'date' => $preview_date( '-4 days' ), 'status' => 'approved' ],
						[ 'id' => 6, 'action' => __( 'Earned', 'rewardswp' ), 'target' => __( 'Made a purchase', 'rewardswp' ), 'points' => 500, 'is_deduction' => false, 'date' => $preview_date( '-5 days' ), 'status' => 'approved' ],
						[ 'id' => 7, 'action' => __( 'Earned', 'rewardswp' ), 'target' => __( 'First order bonus', 'rewardswp' ), 'points' => 200, 'is_deduction' => false, 'date' => $preview_date( '-6 days' ), 'status' => 'approved' ],
						[ 'id' => 8, 'action' => __( 'Earned', 'rewardswp' ), 'target' => __( 'Created an account', 'rewardswp' ), 'points' => 100, 'is_deduction' => false, 'date' => $preview_date( '-6 days' ), 'status' => 'approved' ],
					],
					'total'         => 8,
					'status_counts' => [ 'total' => 8, 'approved' => 7, 'pending' => 1 ],
				],
				'referrals' => [
					'items'         => [
						[ 'id' => 101, 'friend_name' => 'Emma L.', 'action_text' => __( 'made a purchase', 'rewardswp' ), 'status' => 'completed', 'date' => $preview_date( '-1 day' ) ],
						[ 'id' => 102, 'friend_name' => 'j*****r@gmail.com', 'action_text' => __( 'claimed their reward', 'rewardswp' ), 'status' => 'pending', 'date' => $preview_date( '-3 days' ) ],
						[ 'id' => 103, 'friend_name' => 'Olivia K.', 'action_text' => __( 'placed an order', 'rewardswp' ), 'status' => 'pending', 'date' => $preview_date( '-5 days' ) ],
						[ 'id' => 104, 'friend_name' => 'Noah P.', 'action_text' => __( 'made a purchase', 'rewardswp' ), 'status' => 'completed', 'date' => $preview_date( '-6 days' ) ],
					],
					'total'         => 4,
					'status_counts' => [ 'total' => 4, 'completed' => 2, 'pending' => 2 ],
				],
			],
		];

		// Only include friend_claim if friend reward is configured.
		if ( $has_friend_reward ) {
			$args['friend_claim'] = [
				'enabled'         => false,
				'referral_code'   => 'preview123',
				'advocate'        => null,
				'advocate_name'   => __( 'Jane Doe', 'rewardswp' ),
				'advocate_email'  => 'jane@example.com',
				'reward_info'     => $friend_reward,
				'already_claimed' => false,
				'existing_reward' => null,
				'existing_coupon' => '',
			];
		}

		// Merge preview args (form values) into args.
		$args = array_merge( $args, $preview_args );

		// Render the toast component for notifications.
		/**
		 * Toast view instance.
		 *
		 * @var ToastView $toast_view
		 */
		$toast_view = $this->views_factory->get_view(
			ToastView::class
		);
		if ( is_subclass_of( $toast_view, ViewInterface::class ) ) {
			$toast_view->render();
		}

		/**
		 * Rewards widget view instance.
		 *
		 * @var RewardsWidgetView $rewards_widget_view
		 */
		$rewards_widget_view = $this->views_factory->get_view(
			RewardsWidgetView::class
		);

		if ( is_subclass_of( $rewards_widget_view, ViewInterface::class ) ) {
			// Always render in "logged_out" state initially.
			// Alpine.js handles the toggle between logged_out and logged_in via state changes.
			add_filter( 'rewardswp_current_member_override', '__return_false', 10, 1 );

			$rewards_widget_view->render( $args );

			remove_filter( 'rewardswp_current_member_override', '__return_false', 10 );
		}
	}

	/**
	 * Add plugin links to the Plugins page.
	 *
	 * @since 1.0.0
	 *
	 * @param $links
	 *
	 * @return mixed
	 */
	public function filter_add_plugin_links( $links ) {
		$settings_url = admin_url(
			sprintf(
				'admin.php?page=%s',
				SettingsAdminController::MENU_SLUG
			)
		);

		array_unshift(
			$links,
			sprintf(
				'<a href="%1$s">%2$s</a>',
				esc_url( $settings_url ),
				esc_html__( 'Settings', 'rewardswp' )
			)
		);

		return $links;
	}

	/**
	 * AJAX handler for dismissing the upgrade notice.
	 *
	 * Saves user meta to hide the notice permanently for the current user.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function ajax_dismiss_upgrade_notice(): void {
		check_ajax_referer( self::AJAX_NONCE, 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( esc_html__( 'You do not have permission to perform this action.', 'rewardswp' ), 403 );
		}

		update_user_meta( get_current_user_id(), 'rewardswp_upgrade_notice_dismissed', true );

		wp_send_json_success();
	}

	/**
	 * AJAX handler for rendering widget preview.
	 *
	 * @since 1.0.0
	 * @deprecated 1.1.0 Widget preview state is now handled via Alpine.js state changes.
	 *                   No AJAX call is needed - use the 'switch-preview-member-state' event instead.
	 *
	 * @return void
	 */
	public function ajax_render_widget_preview(): void {
		_doing_it_wrong(
			__METHOD__,
			esc_html__( 'Widget preview state is now handled via Alpine.js. Use the switch-preview-member-state event instead.', 'rewardswp' ),
			'1.1.0'
		);

		wp_send_json_error( esc_html__( 'This endpoint is deprecated. Widget preview now uses Alpine.js state changes.', 'rewardswp' ), 410 );
	}

	/**
	 * Reorder admin menu items to place Upgrade link last.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function reorder_upgrade_menu_item(): void {
		global $submenu;

		if ( ! isset( $submenu[ self::MAIN_MENU_SLUG ] ) ) {
			return; // No submenu items to reorder.
		}

		$upgrade_item  = null;
		$upgrade_index = null;

		foreach ( $submenu[ self::MAIN_MENU_SLUG ] as $index => $item ) {
			// Find the item by the placeholder slug used in add_submenu_page.
			if ( 'rewardswp-upgrade-to-pro' === $item[2] ) {
				$upgrade_item  = $item;
				$upgrade_index = $index;
				break;
			}
		}

		if ( null === $upgrade_item ) {
			return; // Upgrade item not found.
		}

		// Set the correct external URL and CSS class.
		$upgrade_url     = get_upgrade_url( 'upgrade_to_pro', 'wp-admin-menu' );
		$upgrade_item[2] = esc_url( $upgrade_url );
		$upgrade_item[4] = 'rewardswp-upgrade-menu-item';

		// Remove the upgrade item from the submenu.
		unset( $submenu[ self::MAIN_MENU_SLUG ][ $upgrade_index ] );

		// Re-index the submenu.
		$submenu[ self::MAIN_MENU_SLUG ] = array_values( $submenu[ self::MAIN_MENU_SLUG ] );

		// Add the modified upgrade item to the end of the submenu.
		$submenu[ self::MAIN_MENU_SLUG ][] = $upgrade_item;
	}

	/**
	 * Output inline script to make the upgrade menu link open in a new tab.
	 *
	 * @since 1.1.1
	 *
	 * @return void
	 */
	public function upgrade_menu_item_new_tab_script(): void {
		if ( ! is_rewardswp_lite_plan() ) {
			return;
		}
		?>
		<script>
		(function() {
			var link = document.querySelector( '#adminmenu li.rewardswp-upgrade-menu-item > a' );
			if ( link ) {
				link.setAttribute( 'target', '_blank' );
				link.setAttribute( 'rel', 'noopener noreferrer' );
			}
		})();
		</script>
		<?php
	}

	/**
	 * When a user is on a RewardsWP related admin page, display footer text
	 * that graciously asks them to rate us.
	 *
	 * @since 1.0.0
	 *
	 * @param string $text Footer text.
	 *
	 * @return string
	 * @noinspection HtmlUnknownTarget
	 */
	public function admin_footer( string $text ): string {
		if ( ! is_rewardswp_lite_plan() || ! is_rewardswp_admin_page() ) {
			return $text;
		}

		$url = 'https://rewardswp.com/go/review/';
		return sprintf(
			wp_kses(
				/* translators: $1$s - RewardsWP plugin name, $2$s - WP.org review link, $3$s - WP.org review link. */
				__( 'Please rate %1$s <a href="%2$s" target="_blank" rel="noopener noreferrer" style="text-decoration: underline;">&#9733;&#9733;&#9733;&#9733;&#9733;</a> on <a href="%3$s" target="_blank" rel="noopener" style="text-decoration: underline;">WordPress.org</a> to help us spread the word. Thank you!', 'rewardswp' ),
				[
					'a' => [
						'href'   => [],
						'target' => [],
						'rel'    => [],
						'style'  => [],
					],
				]
			),
			'<strong>RewardsWP</strong>',
			$url,
			$url
		);
	}

	/**
	 * Conditionally removes the "Screen Options" tab for specific views.
	 *
	 * @since RWPNEXT
	 *
	 * @return void
	 */
	public function maybe_hide_screen_options(): void {

		// Check for the specific admin page and action.
		// This is a GET request in admin area for navigation purposes only, nonce verification is not required.
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$get_action = isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : '';
		if (
			is_rewardswp_admin_page() &&
			'view_member' === $get_action
		) {
			add_filter( 'screen_options_show_screen', '__return_false' );
		}
	}
}
