<?php
declare(strict_types=1);

namespace WP_Rocket\Engine\Preload;

use WP_Rocket\Admin\Options_Data;
use WP_Rocket\Event_Management\Subscriber_Interface;

class PreloadSubscriber implements Subscriber_Interface {

	/**
	 * Homepage Preload instance
	 *
	 * @var Homepage
	 */
	private $homepage_preloader;

	/**
	 * WP Rocket Options instance.
	 *
	 * @var Options_Data
	 */
	private $options;

	/**
	 * Constructor.
	 *
	 * @param Homepage     $homepage_preloader Homepage Preload instance.
	 * @param Options_Data $options            WP Rocket Options instance.
	 */
	public function __construct( Homepage $homepage_preloader, Options_Data $options ) {
		$this->homepage_preloader = $homepage_preloader;
		$this->options            = $options;
	}

	/**
	 * Return an array of events that this subscriber wants to listen to.
	 *
	 * @return array
	 */
	public static function get_subscribed_events() {
		return [
			'admin_notices'                          => [
				[ 'notice_preload_triggered' ],
				[ 'notice_preload_running' ],
				[ 'notice_preload_complete' ],
			],
			'admin_post_rocket_stop_preload'         => [ 'do_admin_post_stop_preload' ],
			'pagely_cache_purge_after'               => [ 'run_preload', 11 ],
			'update_option_' . WP_ROCKET_SLUG        => [
				[ 'maybe_launch_preload', 11, 2 ],
				[ 'maybe_cancel_preload', 10, 2 ],
			],
			'rocket_after_preload_after_purge_cache' => [
				[ 'maybe_preload_mobile_homepage', 10, 3 ],
			],
			'admin_post_rocket_rollback'             => [ 'stop_homepage_preload', 9 ],
			'wp_rocket_upgrade'                      => [ 'stop_homepage_preload', 9 ],
			'rocket_options_changed'                 => 'preload_after_options_change',
			'rocket_after_clean_used_css'            => 'preload_after_clean_used_css',
		];
	}

	/**
	 * Launches the homepage preload
	 *
	 * @since 3.2
	 *
	 * @param string $lang The language code to preload.
	 *
	 * @return void
	 */
	protected function preload( $lang = '' ) {
		if ( $lang ) {
			$urls = (array) get_rocket_i18n_home_url( $lang );
		} else {
			$urls = get_rocket_i18n_uri();
		}

		$this->homepage_preloader->preload( $urls );
	}

	/**
	 * Launches the homepage preload if the option is active
	 *
	 * @since 3.2
	 *
	 * @return void
	 */
	public function run_preload() {
		if ( ! $this->options->get( 'manual_preload' ) ) {
			return;
		}

		delete_transient( 'rocket_preload_errors' );
		$this->preload();
	}

	/**
	 * Cancels any preload currently running if the option is deactivated
	 *
	 * @since 3.2
	 *
	 * @param array $old_value Previous option values.
	 * @param array $value     New option values.
	 *
	 * @return void
	 */
	public function maybe_cancel_preload( $old_value, $value ) {
		if ( isset( $old_value['manual_preload'], $value['manual_preload'] ) && $old_value['manual_preload'] !== $value['manual_preload'] && 0 === (int) $value['manual_preload'] ) {
			delete_transient( 'rocket_preload_errors' );
			$this->homepage_preloader->cancel_preload();
		}
	}

	/**
	 * Launches the preload if the option is activated
	 *
	 * @since 3.2
	 *
	 * @param array $old_value Previous option values.
	 * @param array $value     New option values.
	 *
	 * @return void
	 */
	public function maybe_launch_preload( $old_value, $value ) {
		if ( $this->homepage_preloader->is_process_running() ) {
			return;
		}

		// These values are ignored because they don't impact the cache content.
		$ignored_options = [
			'cache_mobile'                => true,
			'purge_cron_interval'         => true,
			'purge_cron_unit'             => true,
			'sitemap_preload'             => true,
			'sitemaps'                    => true,
			'database_revisions'          => true,
			'database_auto_drafts'        => true,
			'database_trashed_posts'      => true,
			'database_spam_comments'      => true,
			'database_trashed_comments'   => true,
			'database_all_transients'     => true,
			'database_optimize_tables'    => true,
			'schedule_automatic_cleanup'  => true,
			'automatic_cleanup_frequency' => true,
			'do_cloudflare'               => true,
			'cloudflare_email'            => true,
			'cloudflare_api_key'          => true,
			'cloudflare_zone_id'          => true,
			'cloudflare_devmode'          => true,
			'cloudflare_auto_settings'    => true,
			'cloudflare_old_settings'     => true,
			'heartbeat_admin_behavior'    => true,
			'heartbeat_editor_behavior'   => true,
			'varnish_auto_purge'          => true,
			'analytics_enabled'           => true,
			'sucury_waf_cache_sync'       => true,
			'sucury_waf_api_key'          => true,
		];

		// Create 2 arrays to compare.
		$old_value_diff = array_diff_key( $old_value, $ignored_options );
		$value_diff     = array_diff_key( $value, $ignored_options );

		// If it's different, preload.
		if ( md5( wp_json_encode( $old_value_diff ) ) === md5( wp_json_encode( $value_diff ) ) ) {
			return;
		}

		if ( isset( $value['manual_preload'] ) && 1 === (int) $value['manual_preload'] ) {
			$this->preload();
		}
	}

	/**
	 * After automatically preloading the homepage (after purging the cache), also preload the homepage for mobile.
	 *
	 * @since  3.5
	 *
	 * @param string $home_url URL to the homepage being preloaded.
	 * @param string $lang     The lang of the homepage.
	 * @param array  $args     Arguments used for the preload request.
	 *
	 * @return void
	 */
	public function maybe_preload_mobile_homepage( $home_url, $lang, $args ) {
		if ( ! $this->homepage_preloader->is_mobile_preload_enabled() ) {
			return;
		}

		if ( empty( $args['user-agent'] ) ) {
			$args['user-agent'] = 'WP Rocket/Homepage_Preload_After_Purge_Cache';
		}

		$args['user-agent'] = $this->homepage_preloader->get_mobile_user_agent_prefix() . ' ' . $args['user-agent'];

		wp_safe_remote_get( $home_url, $args );
	}

	/**
	 * This notice is displayed when the preload is triggered from a different page than WP Rocket settings page
	 *
	 * @since 3.2
	 *
	 * @return void
	 */
	public function notice_preload_triggered() {
		if ( ! current_user_can( 'rocket_preload_cache' ) ) {
			return;
		}

		$screen = get_current_screen();

		if ( 'settings_page_wprocket' === $screen->id ) {
			return;
		}

		if ( ! $this->options->get( 'manual_preload', 0 ) ) {
			return;
		}

		if ( false === get_transient( 'rocket_preload_triggered' ) ) {
			return;
		}

		delete_transient( 'rocket_preload_triggered' );

		$message = __( 'Preload: WP Rocket has started preloading your website.', 'rocket' );

		if ( current_user_can( 'rocket_manage_options' ) ) {
			$message .= ' ' . sprintf(
				// Translators: %1$s = opening link tag, %2$s = closing link tag.
				__( 'Go to the %1$sWP Rocket settings%2$s page to track progress.', 'rocket' ),
				'<a href="' . esc_url( admin_url( 'options-general.php?page=' . WP_ROCKET_PLUGIN_SLUG ) ) . '">',
				'</a>'
			);
		}

		\rocket_notice_html(
			[
				'status'  => 'info',
				'message' => $message,
			]
		);
	}

	/**
	 * This notice is displayed when the preload is running
	 *
	 * @since 3.2
	 *
	 * @return void
	 */
	public function notice_preload_running() {
		if ( ! current_user_can( 'rocket_preload_cache' ) ) {
			return;
		}

		$screen = get_current_screen();

		if ( 'settings_page_wprocket' !== $screen->id ) {
			return;
		}

		$homepage_count = get_transient( 'rocket_homepage_preload_running' );
		$sitemap_count  = get_transient( 'rocket_sitemap_preload_running' );

		if ( false === $homepage_count && false === $sitemap_count ) {
			return;
		}

		$running = $homepage_count + $sitemap_count;
		$status  = 'info';
		// translators: %1$s = Number of pages preloaded.
		$message  = '<p>' . sprintf( _n( 'Preload: %1$s uncached page has now been preloaded. (refresh to see progress)', 'Preload: %1$s uncached pages have now been preloaded. (refresh to see progress)', $running, 'rocket' ), number_format_i18n( $running ) );
		$message .= ' <em> - (' . date_i18n( get_option( 'date_format' ) ) . ' @ ' . date_i18n( get_option( 'time_format' ) ) . ') </em></p>';

		if ( defined( 'WP_ROCKET_DEBUG' ) && WP_ROCKET_DEBUG ) {

			$errors = get_transient( 'rocket_preload_errors' );

			if ( false !== $errors ) {
				$status   = 'warning';
				$message .= '<p>' . _n( 'The following error happened during gathering of the URLs to preload:', 'The following errors happened during gathering of the URLs to preload:', count( $errors['errors'] ), 'rocket' ) . '</p>';

				foreach ( $errors['errors'] as $error ) {
					$message .= '<p>' . $error . '</p>';
				}
			}
		}

		\rocket_notice_html(
			[
				'status'      => $status,
				'message'     => $message,
				'dismissible' => 'notice-preload-running',
				'action'      => 'stop_preload',
			]
		);
	}

	/**
	 * This notice is displayed after the sitemap preload is complete
	 *
	 * @since 3.2
	 *
	 * @return void
	 */
	public function notice_preload_complete() {
		if ( ! current_user_can( 'rocket_preload_cache' ) ) {
			return;
		}

		$screen = get_current_screen();

		if ( 'settings_page_wprocket' !== $screen->id ) {
			return;
		}

		$result = get_transient( 'rocket_preload_complete' );

		if ( false === $result ) {
			return;
		}

		$result_timestamp = get_transient( 'rocket_preload_complete_time' );

		if ( false === $result_timestamp ) {
			return;
		}

		delete_transient( 'rocket_preload_complete' );
		delete_transient( 'rocket_preload_errors' );
		delete_transient( 'rocket_preload_complete_time' );

		// translators: %d is the number of pages preloaded.
		$notice_message  = sprintf( __( 'Preload complete: %d pages have been cached.', 'rocket' ), $result );
		$notice_message .= ' <em> (' . $result_timestamp . ') </em>';

		\rocket_notice_html(
			[
				'message' => $notice_message,
			]
		);
	}

	/**
	 * Stops currently running preload from the notice action button
	 *
	 * @since 3.2
	 *
	 * @return void
	 */
	public function do_admin_post_stop_preload() {
		if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'rocket_stop_preload' ) ) {
			wp_nonce_ays( '' );
		}

		if ( ! current_user_can( 'rocket_preload_cache' ) ) {
			wp_safe_redirect( wp_get_referer() );
			die();
		}

		$this->homepage_preloader->cancel_preload();

		wp_safe_redirect( wp_get_referer() );
		die();
	}

	/**
	 * Stops homepage preload.
	 *
	 * @since 3.10
	 *
	 * @return void
	 */
	public function stop_homepage_preload() {
		$this->homepage_preloader->cancel_preload();
	}

	/**
	 * Preloads the homepage after changing the options
	 *
	 * @since 3.11
	 *
	 * @param array $value An array of submitted values for the settings.
	 *
	 * @return void
	 */
	public function preload_after_options_change( $value ) {
		$mobile = false;

		if (
			isset( $value['do_caching_mobile_files'] )
			&&
			1 === $value['do_caching_mobile_files']
		) {
			$mobile = true;
		}

		$this->preload_homepage( $mobile );
	}

	/**
	 * Preloads the homepage after cleaning the used CSS in the database
	 *
	 * @since 3.11
	 *
	 * @return void
	 */
	public function preload_after_clean_used_css() {
		$mobile = false;

		if ( $this->options->get( 'do_caching_mobile_files', 0 ) ) {
			$mobile = true;
		}

		$this->preload_homepage( $mobile );
	}

	/**
	 * Preloads the homepage (desktop & mobile if enabled)
	 *
	 * @since 3.11
	 *
	 * @param bool $mobile True to preload the mobile version, false otherwise.
	 *
	 * @return void
	 */
	private function preload_homepage( $mobile = false ) {
		wp_safe_remote_get(
			home_url(),
			[
				'timeout'    => 0.01,
				'blocking'   => false,
				'user-agent' => 'WP Rocket/Homepage Preload',
				'sslverify'  => apply_filters( 'https_local_ssl_verify', false ), // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
			]
		);

		if ( $mobile ) {
			wp_safe_remote_get(
				home_url(),
				[
					'timeout'    => 0.01,
					'blocking'   => false,
					'user-agent' => $this->homepage_preloader->get_mobile_user_agent_prefix() . ' WP Rocket/Homepage Preload',
					'sslverify'  => apply_filters( 'https_local_ssl_verify', false ), // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
				]
			);
		}
	}
}
