<?php
declare(strict_types=1);

namespace WP_Rocket\Engine\License;

use WP_Rocket\Engine\License\API\User;
use WP_Rocket\Engine\License\API\UserClient;
use WP_Rocket\Event_Management\Subscriber_Interface;

class Subscriber implements Subscriber_Interface {
	/**
	 * Cron hook used to refresh the customer data transient shortly after a trial license expires.
	 *
	 * @var string
	 */
	const CRON_REFRESH_TRIAL_CUSTOMER_DATA = 'rocket_cron_refresh_trial_customer_data';

	/**
	 * Upgrade instance
	 *
	 * @var Upgrade
	 */
	private $upgrade;

	/**
	 * Renewal instance
	 *
	 * @var Renewal
	 */
	private $renewal;

	/**
	 * Revoked instance
	 *
	 * @var Revoked
	 */
	private $revoked;

	/**
	 * User instance
	 *
	 * @var User
	 */
	private $user;

	/**
	 * UserClient instance
	 *
	 * @var UserClient
	 */
	private $user_client;

	/**
	 * Instantiate the class
	 *
	 * @param Upgrade    $upgrade     Upgrade instance.
	 * @param Renewal    $renewal     Renewal instance.
	 * @param Revoked    $revoked     Revoked instance.
	 * @param User       $user        User instance.
	 * @param UserClient $user_client UserClient instance.
	 */
	public function __construct( Upgrade $upgrade, Renewal $renewal, Revoked $revoked, User $user, UserClient $user_client ) {
		$this->upgrade     = $upgrade;
		$this->renewal     = $renewal;
		$this->revoked     = $revoked;
		$this->user        = $user;
		$this->user_client = $user_client;
	}

	/**
	 * Events this subscriber listens to.
	 *
	 * @return array
	 */
	public static function get_subscribed_events() {
		return [
			'rocket_account_license_info'           => 'display_upgrade_section',
			'rocket_settings_page_footer'           => 'display_upgrade_popin',
			'rocket_menu_title'                     => [
				[ 'add_notification_bubble', 10, 2 ],
				[ 'add_notification_bubble_expired' ],
				[ 'maybe_add_revoked_bubble' ],
			],
			'admin_footer-settings_page_wprocket'   => [
				[ 'dismiss_notification_bubble' ],
				[ 'set_dashboard_seen_transient' ],
			],
			'rocket_dashboard_sidebar_license'      => [
				[ 'add_renewal_soon_banner', 11 ],
				[ 'add_renewal_expired_banner', 12 ],
				[ 'add_revoked_banner', 13 ],
			],
			'rocket_dashboard_sidebar_promos'       => [ 'add_promo_banner' ],
			'wp_ajax_rocket_dismiss_promo'          => 'dismiss_promo_banner',
			'wp_ajax_rocket_dismiss_renewal'        => 'dismiss_renewal_banner',
			'rocket_localize_admin_script'          => 'add_localize_script_data',
			'wp_rocket_upgrade'                     => [ 'clean_user_transient', 15, 2 ],
			'rocket_before_add_field_to_settings'   => [
				[ 'maybe_disable_ocd', 11 ],
				[ 'add_license_expire_warning' ],
			],
			'get_rocket_option_remove_unused_css'   => [ 'maybe_disable_option', PHP_INT_MAX ],
			'get_rocket_option_async_css'           => [ 'maybe_disable_option', PHP_INT_MAX ],
			'admin_notices'                         => 'maybe_display_revoked_notice',
			'set_transient_wp_rocket_customer_data' => 'maybe_rocket_license_expired',
			'current_screen'                        => 'maybe_schedule_trial_customer_data_refresh',
			self::CRON_REFRESH_TRIAL_CUSTOMER_DATA  => 'refresh_trial_customer_data',
		];
	}

	/**
	 * Displays the upgrade section in the license info block
	 *
	 * @since 3.7.3
	 *
	 * @return void
	 */
	public function display_upgrade_section() {
		$this->upgrade->display_upgrade_section();
	}

	/**
	 * Displays the upgrade popin
	 *
	 * @since 3.7.3
	 *
	 * @return void
	 */
	public function display_upgrade_popin() {
		$this->upgrade->display_upgrade_popin();
	}

	/**
	 * Adds the notification bubble to the menu title if a promotion is active
	 *
	 * @since 3.7.4
	 *
	 * @param string $menu_title The text to be used for the menu.
	 * @param string $context Filter context.
	 * @return string
	 */
	public function add_notification_bubble( $menu_title, $context = 'adminmenu' ) {
		return $this->upgrade->add_notification_bubble( $menu_title, $context );
	}

	/**
	 * Prevents the notification bubble from showing once the user accessed the dashboard once
	 *
	 * @since 3.7.4
	 *
	 * @return void
	 */
	public function dismiss_notification_bubble() {
		$this->upgrade->dismiss_notification_bubble();
	}

	/**
	 * Adds the promotions banner to the dashboard sidebar Promos category when a promotion is active
	 *
	 * @since 3.24
	 *
	 * @param string $html Current HTML of the category.
	 * @return string
	 */
	public function add_promo_banner( $html ): string {
		if ( '' !== trim( (string) $html ) ) {
			return $html;
		}

		return $this->upgrade->get_promo_banner();
	}

	/**
	 * AJAX callback to dismiss the promotion banner
	 *
	 * @since 3.7.4
	 *
	 * @return void
	 */
	public function dismiss_promo_banner() {
		$this->upgrade->dismiss_promo_banner();
	}

	/**
	 * Adds the current time and promotion end time to WP Rocket localize script data
	 *
	 * @since 3.7.5 Add the renewal localize data
	 * @since 3.7.4
	 *
	 * @param array $data Localize script data.
	 * @return array
	 */
	public function add_localize_script_data( $data ) {
		$data = $this->upgrade->add_localize_script_data( $data );

		return $this->renewal->add_localize_script_data( $data );
	}

	/**
	 * Deletes the user data transient on 3.7.4 update
	 *
	 * @since 3.7.4
	 *
	 * @param string $new_version New version of the plugin.
	 * @param string $old_version Installed version of the plugin.
	 * @return void
	 */
	public function clean_user_transient( $new_version, $old_version ) {
		if ( version_compare( $old_version, '3.7.4', '>' ) ) {
			return;
		}

		delete_transient( 'wp_rocket_customer_data' );
	}

	/**
	 * Adds the renewal banner for users expiring in less than 30 days to the dashboard sidebar License category.
	 *
	 * The first non-empty banner wins, so only one License banner is displayed.
	 *
	 * @since 3.24
	 *
	 * @param string $html Current HTML of the category.
	 * @return string
	 */
	public function add_renewal_soon_banner( $html ): string {
		if ( '' !== trim( (string) $html ) ) {
			return $html;
		}

		return $this->renewal->get_renewal_soon_banner();
	}

	/**
	 * Adds the renewal banner for expired users to the dashboard sidebar License category.
	 *
	 * The first non-empty banner wins, so only one License banner is displayed.
	 *
	 * @since 3.24
	 *
	 * @param string $html Current HTML of the category.
	 * @return string
	 */
	public function add_renewal_expired_banner( $html ): string {
		if ( '' !== trim( (string) $html ) ) {
			return $html;
		}

		return $this->renewal->get_renewal_expired_banner();
	}

	/**
	 * AJAX callback to dismiss the renewal banner
	 *
	 * @since 3.7.5
	 *
	 * @return void
	 */
	public function dismiss_renewal_banner() {
		$this->renewal->dismiss_renewal_expired_banner();
	}

	/**
	 * Add license expiring warning to OCD label
	 *
	 * @param array $args Setting field arguments.
	 *
	 * @return array
	 */
	public function add_license_expire_warning( $args ): array {
		return $this->renewal->add_license_expire_warning( $args );
	}

	/**
	 * Adds the notification bubble to WP Rocket menu item when expired
	 *
	 * @param string $menu_title Menu title.
	 *
	 * @return string
	 */
	public function add_notification_bubble_expired( $menu_title ) {
		return $this->renewal->add_expired_bubble( $menu_title );
	}

	/**
	 * Sets the dashboard seen transient to hide the expired bubble
	 *
	 * @return void
	 */
	public function set_dashboard_seen_transient() {
		$this->renewal->set_dashboard_seen_transient();
	}

	/**
	 * Disable optimize CSS delivery setting
	 *
	 * @param array $args Array of setting field arguments.
	 *
	 * @return array
	 */
	public function maybe_disable_ocd( $args ) {
		return $this->renewal->maybe_disable_ocd( $args );
	}

	/**
	 * Disables the RUCSS & Async CSS options if license is expired
	 *
	 * @param mixed $value Current option value.
	 *
	 * @return mixed
	 */
	public function maybe_disable_option( $value ) {
		return $this->renewal->maybe_disable_option( $value );
	}

	/**
	 * Adds the revoked website banner to the dashboard sidebar License category if the site's license is revoked.
	 *
	 * The first non-empty banner wins, so only one License banner is displayed.
	 *
	 * @since 3.24
	 *
	 * @param string $html Current HTML of the category.
	 * @return string
	 */
	public function add_revoked_banner( $html ): string {
		if ( '' !== trim( (string) $html ) ) {
			return $html;
		}

		return $this->revoked->get_revoked_banner();
	}

	/**
	 * Displays a revoked notice in the admin area if the website license is revoked.
	 *
	 * @since 3.20.4
	 * @return void
	 */
	public function maybe_display_revoked_notice() {
		$this->revoked->maybe_display_revoked_notice();
	}

	/**
	 * Adds a revoked license notification bubble to the WP Rocket menu item title if the website's license is revoked.
	 *
	 * @since 3.20.4
	 *
	 * @param string $menu_title The current menu title.
	 * @return string Modified menu title with revoked notification bubble if applicable.
	 */
	public function maybe_add_revoked_bubble( $menu_title ) {
		return $this->revoked->maybe_add_revoked_bubble( $menu_title );
	}

	/**
	 * Fires licence status transition actions when the customer data transient is refreshed.
	 *
	 * @since 3.23.4
	 *
	 * @param object $user_data Rocket customer data.
	 * @return void
	 */
	public function maybe_rocket_license_expired( $user_data ) {
		if ( empty( $user_data->status ) ) {
			return;
		}

		$is_revoked = $user_data->licence->is_revoked ?? false;

		if ( 'expired' === $user_data->status || $is_revoked ) {
			/**
			 * Fires when the WP Rocket licence transitions to expired or revoked.
			 *
			 * Fires before the more specific rocket_license_expired / rocket_license_revoked
			 * actions, so a callback here runs regardless of which of the two applies.
			 *
			 * @since 3.23.4
			 */
			do_action( 'rocket_license_expired_or_revoked' );

			if ( 'expired' === $user_data->status ) {
				/**
				 * Fires when the WP Rocket licence has expired.
				 *
				 * @since 3.23.4
				 */
				do_action( 'rocket_license_expired' );
			}

			if ( $is_revoked ) {
				/**
				 * Fires when the WP Rocket licence has been revoked.
				 *
				 * @since 3.23.4
				 */
				do_action( 'rocket_license_revoked' );
			}
		}

		if ( 'active' === $user_data->status ) {
			/**
			 * Fires when the WP Rocket licence is active.
			 *
			 * @since 3.23.4
			 */
			do_action( 'rocket_license_active' );
		}
	}

	/**
	 * Ensures a refresh of the customer data transient is scheduled shortly after a trial
	 * customer's license expiration.
	 *
	 * @since 3.23.5
	 *
	 * @return void
	 */
	public function maybe_schedule_trial_customer_data_refresh() {
		if ( ! current_user_can( 'rocket_manage_options' ) ) {
			return;
		}

		if ( ! $this->user->is_trial_customer() ) {
			return;
		}

		if ( wp_next_scheduled( self::CRON_REFRESH_TRIAL_CUSTOMER_DATA ) ) {
			return;
		}

		$expiration = $this->user->get_license_expiration();

		if ( 0 === $expiration || $expiration <= time() ) {
			return;
		}

		wp_schedule_single_event( $expiration + 5 * MINUTE_IN_SECONDS, self::CRON_REFRESH_TRIAL_CUSTOMER_DATA );
	}

	/**
	 * Flushes and refetches the customer data transient.
	 *
	 * Cron callback for self::CRON_REFRESH_TRIAL_CUSTOMER_DATA.
	 *
	 * @since 3.23.5
	 *
	 * @return void
	 */
	public function refresh_trial_customer_data() {
		$this->user_client->flush_cache();
		$this->user_client->get_user_data();
	}
}
