<?php
/**
 * Theme update functions.
 */

// File Security Check
if ( ! defined( 'ABSPATH' ) ) { exit; }

if ( ! class_exists( 'Presscore_Modules_ThemeUpdateModule', false ) ) :

	class Presscore_Modules_ThemeUpdateModule {

		const PAGE_ID = 'the7-dashboard';

		/**
		 * Request marker for a Theme Upgrade (force-install of the newer line).
		 */
		const UPGRADE_REQUEST_MARKER = 'the7-theme-upgrade';

		/**
		 * Validated Upgrade Channel metadata for the current request.
		 *
		 * @var array|null
		 */
		private static $theme_upgrade_info;

		public static function execute() {
			if ( ! ( defined( 'THE7_PREVENT_THEME_UPDATE' ) && THE7_PREVENT_THEME_UPDATE ) ) {
				add_filter( 'pre_set_site_transient_update_themes', array(
					__CLASS__,
					'pre_set_site_transient_update_themes'
				) );
			}

			// Theme Upgrade: force-install the newer line from the Upgrade Channel on user command.
			add_action( 'load-update.php', [ __CLASS__, 'prepare_theme_upgrade' ] );
			add_filter( 'site_transient_update_themes', array( __CLASS__, 'force_theme_upgrade_transient_filter' ) );

			// Rewrite the theme-details "Update Available" box copy when the offer crosses a line.
			add_filter( 'wp_prepare_themes_for_js', array( __CLASS__, 'replace_cross_line_offer_message' ) );

			// React to a 402 Feature Lock returned by the repo on the theme package download.
			add_filter(
				'upgrader_pre_download',
				array( __CLASS__, 'register_theme_update_feature_lock_listener' ),
				10,
				3
			);

			// After a successful Theme Upgrade, refetch Feature Access so the stale "upgrade
			// available" verdict is reconciled with the server's answer for the new version.
			add_action( 'upgrader_process_complete', array( __CLASS__, 'refetch_feature_access_after_theme_upgrade' ) );

			// Backup lang files.
			add_filter( 'upgrader_pre_install', array( __CLASS__, 'backup_lang_files' ), 10, 2 );
			add_filter( 'upgrader_post_install', array( __CLASS__, 'restore_lang_files' ), 10, 3 );

			add_action( 'admin_init', array( __CLASS__, 'register_settings' ) );
			add_filter( 'pre_update_site_option_the7_purchase_code', array( __CLASS__, 'check_for_empty_code' ), 10, 2 );
			add_action(
				'add_site_option_the7_purchase_code',
				array( __CLASS__, 'handle_purchase_code_persisted' ),
				10,
				2
			);
			add_action(
				'update_site_option_the7_purchase_code',
				array( __CLASS__, 'handle_purchase_code_persisted' ),
				10,
				2
			);

			if ( ! defined( 'THE7_IGNORE_THEME_DOWNLOAD_REQUIREMENTS' ) || ! THE7_IGNORE_THEME_DOWNLOAD_REQUIREMENTS ) {
				add_filter(
					'upgrader_pre_download',
					array( __CLASS__, 'upgrader_pre_download_theme_requirements_filter' ),
					10,
					3
				);
			}

			if ( ! class_exists( 'The7_Install', false ) ) {
				include dirname( __FILE__ ) . '/class-the7-install.php';
			}

			The7_Install::init();

			if ( ! class_exists( 'The7_Registration_Warning', false ) ) {
				include dirname( __FILE__ ) . '/class-the7-registration-warning.php';
			}

			add_action( 'admin_notices', array( 'The7_Registration_Warning', 'add_admin_notices' ) );
			add_action( 'the7_after_theme_deactivation', array( 'The7_Registration_Warning', 'dismiss_admin_notices' ) );
			add_action( 'the7_after_theme_registration', array( 'The7_Registration_Warning', 'setup_registration_warning' ) );
		}

		/**
		 * Setup page hooks.
		 */
		public static function setup_hooks( $page ) {
			add_action( 'load-' . $page, array( __CLASS__, 'update_settings' ) );
		}

		public static function register_settings() {
			register_setting( 'the7_theme_registration', 'the7_purchase_code', array( __CLASS__, 'theme_activation_action' ) );
		}

		/**
		 * Theme registration action.
		 *
		 * @param $code
		 *
		 * @return string
		 */
		public static function theme_activation_action( $code ) {
			$code = is_string( $code ) ? trim( $code ) : '';

			if ( isset( $_POST['register_theme'] ) ) {
				$code = self::register_action( $code );
			} elseif ( $_POST['deregister_theme'] ) {
				$code = self::de_register_action();
			}
			do_action( 'the7_theme_activation_action' );
			return $code;
		}

		public static function check_for_empty_code( $val = false, $old_val = false ) {
			if ( ! $val && $val === $old_val ) {
				add_settings_error( 'the7_theme_registration', 'the7_invalid_purchase_code', 'none' );
			}

			return $val;
		}

		/**
		 * Refresh license-dependent repository data after the purchase code is persisted.
		 *
		 * @param string $option Option name.
		 * @param mixed  $value  Persisted option value.
		 */
		public static function handle_purchase_code_persisted( $option, $value ) {
			self::refresh_repo_data( (bool) $value );
		}

		/**
		 * Refresh license-dependent repository data.
		 *
		 * @param bool $refresh_theme_update Whether to refresh Theme Update data synchronously.
		 */
		protected static function refresh_repo_data( $refresh_theme_update ) {
			The7_Remote_API::flush_repo_lists_cache();
			if ( $refresh_theme_update ) {
				do_action( 'wp_update_themes' );
			}
		}

		protected static function register_action( $code ) {
			if ( ! $code ) {
				presscore_deactivate_theme();
				self::check_for_empty_code();
				return '';
			}

			$the7_remote_api = new The7_Remote_API( $code );

			$the7_remote_api_response = $the7_remote_api->register_purchase_code();
			if ( is_wp_error( $the7_remote_api_response ) ) {
				add_settings_error( 'the7_theme_registration', 'the7_registration_error', $the7_remote_api_response->get_error_message() );
				return '';
			}

			presscore_activate_theme();

			if ( presscore_get_purchase_code() === $code ) {
				self::refresh_repo_data( true );
			}

			do_action( 'the7_after_theme_registration', $the7_remote_api_response );

			return $code;
		}

		protected static function de_register_action() {
			$code = presscore_get_purchase_code();

			$the7_remote_api = new The7_Remote_API( $code );

			$the7_remote_api_response = $the7_remote_api->de_register_purchase_code();
			if ( is_wp_error( $the7_remote_api_response ) ) {
				add_settings_error( 'the7_theme_registration', 'the7_registration_error', $the7_remote_api_response->get_error_message() );
				return $code;
			}

			presscore_deactivate_theme();
			add_settings_error( 'the7_theme_registration', 'the7_deregistration_success', 'none' );

			return '';
		}

		public static function update_settings() {
			if ( ! isset( $_POST['option_page'] ) || 'the7_theme_registration' !== $_POST['option_page'] ) {
				return;
			}

			if ( ! isset( $_POST['action'] ) || 'update' !== $_POST['action'] ) {
				return;
			}

			if ( ! current_user_can( 'edit_theme_options' ) ) {
				return;
			}

			check_admin_referer( 'the7_theme_registration-options' );

			global $new_whitelist_options;
			$options = $new_whitelist_options['the7_theme_registration'];

			foreach ( $options as $option ) {
				$option = trim( $option );
				$value = null;
				if ( isset( $_POST[ $option ] ) ) {
					$value = $_POST[ $option ];
					if ( ! is_array( $value ) ) {
						$value = trim( $value );
					}
					$value = wp_unslash( $value );
				}

				update_site_option( $option, $value );
			}

			/**
			 * Handle settings errors.
			 */
			set_transient('settings_errors', get_settings_errors(), 30);

			$goback = add_query_arg( 'settings-updated', 'true', wp_get_referer() );
			wp_redirect( $goback );
			exit;
		}

		/**
		 * Adjust update_themes site transient to honor theme update from Envato server.
		 *
		 * @param $transient
		 *
		 * @return mixed
		 */
		public static function pre_set_site_transient_update_themes( $transient ) {
			if ( ! is_object( $transient ) || ! presscore_theme_is_activated() ) {
				return $transient;
			}

			$code            = presscore_get_purchase_code();
			$the7_remote_api = new The7_Remote_API( $code );

			// Check The7 version.
			$response = $the7_remote_api->check_theme_update();
			if ( is_wp_error( $response ) || ! isset( $response['version'] ) ) {
				return $transient;
			}

			$theme_template  = get_template();
			$current_version = wp_get_theme( $theme_template )->get( 'Version' );
			$new_version     = $response['version'];
			$item            = [
				'theme'        => $theme_template,
				'new_version'  => $current_version,
				'url'          => presscore_theme_update_get_changelog_url(),
				'package'      => '',
				'requires'     => isset( $response['requires'] ) ? $response['requires'] : '',
				'requires_php' => isset( $response['requires_php'] ) ? $response['requires_php'] : '',
			];

			if ( version_compare( $current_version, $new_version, '<' ) ) {
				// The offered build is the Requested Build: the package must resolve to the
				// build WordPress is advertising, not to whatever the server considers latest.
				$item['package']                        = $the7_remote_api->get_theme_download_url( $new_version );
				$item['new_version']                    = $new_version;
				$transient->response[ $theme_template ] = $item;
			} else {
				$transient->no_update[ $theme_template ] = $item;
			}

			return $transient;
		}

		/**
		 * Rewrite the theme-details "Update Available" box copy for a cross-line offer.
		 *
		 * Runs on the JS-driven themes screen (details modal + grid card). Keyed on the
		 * **line predicate**, not on the `theme_updates` capability verdict: that capability
		 * describes entitlement to cross a line, while in-line updates are unconditional, so
		 * keying on it here would strip "Update now" from exactly the sites that need to take
		 * the next in-line build. See docs/adr/0010-client-version-and-requested-version.md.
		 *
		 * A cross-line offer cannot be installed from this screen, so `hasPackage` is cleared
		 * and the copy points at the The7 dashboard. An in-line offer is left entirely alone —
		 * plain WordPress behavior. Note this filter only reaches Appearance → Themes; the
		 * Dashboard → Updates screen and auto-updates keep the package and resolve the offer
		 * against the server as usual.
		 *
		 * @param array $themes Prepared themes, keyed by stylesheet.
		 *
		 * @return array
		 */
		public static function replace_cross_line_offer_message( $themes ) {
			$stylesheet = get_template();
			if ( empty( $themes[ $stylesheet ]['update'] ) ) {
				return $themes;
			}

			$new_version = self::get_pending_offer_version( $stylesheet );
			if ( ! self::offer_crosses_line( $new_version ) ) {
				return $themes;
			}

			$themes[ $stylesheet ]['hasPackage'] = false;

			$themes[ $stylesheet ]['update'] = '<br>'
				. '<strong>' . \The7\Admin\Notices\Version_Update_Notice::get_headline( self::get_major_version( $new_version ) ) . '!</strong> '
				. \The7\Admin\Notices\Version_Update_Notice::get_review_options_sentence();

			return $themes;
		}

		/**
		 * The version advertised by the pending update offer, read from the update transient.
		 *
		 * @param string $stylesheet Theme directory the offer belongs to.
		 *
		 * @return string Offered version, or an empty string when there is no offer.
		 */
		private static function get_pending_offer_version( $stylesheet ) {
			$themes_updates = get_site_transient( 'update_themes' );

			if ( ! is_object( $themes_updates ) || ! isset( $themes_updates->response[ $stylesheet ]['new_version'] ) ) {
				return '';
			}

			$new_version = $themes_updates->response[ $stylesheet ]['new_version'];

			return is_scalar( $new_version ) ? (string) $new_version : '';
		}

		/**
		 * Whether an offered build sits on a different version line than the running build.
		 *
		 * The Line boundary is the major-version frontier (v14 → v15). A missing or
		 * unparsable version on either side counts as **not** crossing, so a parse failure
		 * can never suppress an update affordance.
		 *
		 * @param string $new_version Offered build.
		 *
		 * @return bool
		 */
		public static function offer_crosses_line( $new_version ) {
			$offered   = self::get_major_version( $new_version );
			$installed = self::get_major_version( THE7_VERSION );

			if ( null === $offered || null === $installed ) {
				return false;
			}

			return $offered !== $installed;
		}

		/**
		 * Leading integer of a version string, or null when there is none to read.
		 *
		 * @param mixed $version Version string.
		 *
		 * @return int|null
		 */
		public static function get_major_version( $version ) {
			if ( ! is_scalar( $version ) || ! preg_match( '/^\s*(\d+)/', (string) $version, $matches ) ) {
				return null;
			}

			return (int) $matches[1];
		}


		/**
		 * Build the nonce'd URL that triggers a Theme Upgrade through WP's theme upgrader.
		 *
		 * The button this URL backs is theme-owned; the surrounding notice copy is server-authored.
		 *
		 * @return string
		 */
		public static function get_theme_upgrade_url() {
			$theme_template = get_template();
			$url            = add_query_arg(
				[
					'action'                     => 'upgrade-theme',
					'theme'                      => $theme_template,
					self::UPGRADE_REQUEST_MARKER => '1',
				],
				self_admin_url( 'update.php' )
			);

			return wp_nonce_url( $url, 'upgrade-theme_' . $theme_template );
		}

		/**
		 * Validate Upgrade Channel metadata before WordPress renders the upgrader screen.
		 */
		public static function prepare_theme_upgrade() {
			self::$theme_upgrade_info = null;

			if ( ! self::is_theme_upgrade_request() ) {
				return;
			}

			$info = self::fetch_valid_theme_upgrade_info();
			if ( is_wp_error( $info ) ) {
				wp_die(
					esc_html( $info->get_error_message() ),
					esc_html__( 'Theme Upgrade Unavailable', 'the7mk2' ),
					[
						'response'  => 503,
						'back_link' => true,
					]
				);
			}

			self::$theme_upgrade_info = $info;
		}

		/**
		 * Force WP to see the latest Upgrade Channel build as an available update for this theme.
		 *
		 * Fired only on a nonce'd, capable, activated upgrade request. Reads the Upgrade Channel
		 * info document fresh (real version + requirements), and points the package at the
		 * Upgrade Channel download endpoint (always latest build of the newer line).
		 *
		 * @param mixed $transient Site transient value.
		 *
		 * @return mixed
		 */
		public static function force_theme_upgrade_transient_filter( $transient ) {
			if ( ! self::is_theme_upgrade_request() ) {
				return $transient;
			}

			$info = self::$theme_upgrade_info;
			if ( ! is_array( $info ) ) {
				$info = self::fetch_valid_theme_upgrade_info();
				if ( is_wp_error( $info ) ) {
					return $transient;
				}

				self::$theme_upgrade_info = $info;
			}

			if ( ! is_object( $transient ) ) {
				$transient           = new stdClass();
				$transient->response = [];
			}

			$theme_template  = get_template();
			$the7_remote_api = new The7_Remote_API( presscore_get_purchase_code() );

			$item = [
				'theme'        => $theme_template,
				'new_version'  => $info['version'],
				'url'          => presscore_theme_update_get_changelog_url(),
				'package'      => $the7_remote_api->get_theme_upgrade_download_url(),
				'requires'     => $info['requires'],
				'requires_php' => $info['requires_php'],
			];

			$transient->response[ $theme_template ] = $item;

			return $transient;
		}

		/**
		 * Whether the current request is an authorized Theme Upgrade.
		 *
		 * @return bool
		 */
		private static function is_theme_upgrade_request() {
			return isset( $_GET[ self::UPGRADE_REQUEST_MARKER ] )
				&& check_ajax_referer( 'upgrade-theme_' . get_template(), false, false )
				&& current_user_can( 'update_themes' )
				&& presscore_theme_is_activated();
		}

		/**
		 * Fetch and validate the complete Upgrade Channel metadata document.
		 *
		 * @return array|WP_Error
		 */
		private static function fetch_valid_theme_upgrade_info() {
			$the7_remote_api = new The7_Remote_API( presscore_get_purchase_code() );
			$info            = $the7_remote_api->get_theme_upgrade_info();

			if ( is_wp_error( $info ) || ! is_array( $info ) ) {
				return self::invalid_theme_upgrade_info_error( $info );
			}

			$required_fields = [ 'version', 'requires', 'requires_php' ];
			$validated_info  = [];

			foreach ( $required_fields as $field ) {
				if ( ! array_key_exists( $field, $info ) || ! is_string( $info[ $field ] ) ) {
					return self::invalid_theme_upgrade_info_error( $info );
				}

				$value = trim( $info[ $field ] );
				if ( ! self::is_valid_upgrade_version( $value ) ) {
					return self::invalid_theme_upgrade_info_error( $info );
				}

				$validated_info[ $field ] = $value;
			}

			return $validated_info;
		}

		/**
		 * Validate a theme, WordPress, or PHP version supplied by the Upgrade Channel.
		 *
		 * @param string $version Version string.
		 *
		 * @return bool
		 */
		private static function is_valid_upgrade_version( $version ) {
			return (bool) preg_match(
				'/^[1-9]\d*(?:\.\d+)*(?:[-_.]?(?:alpha|beta|rc|dev|patch|pl|p)[-_.]?\d*)?$/i',
				$version
			);
		}

		/**
		 * Build the administrator-facing error for unavailable or incomplete metadata.
		 *
		 * @param mixed $info Raw response or transport error.
		 *
		 * @return WP_Error
		 */
		private static function invalid_theme_upgrade_info_error( $info ) {
			return new WP_Error(
				'the7_theme_upgrade_metadata_unavailable',
				__(
					'Theme Upgrade could not start because The7 could not verify the target version and compatibility requirements. No files were changed. Please try again.',
					'the7mk2'
				),
				$info
			);
		}

		/**
		 * Backup files from language dir to temporary folder in uploads.
		 */
		public static function backup_lang_files( $res = true, $hook_extra = array() ) {
			if ( is_wp_error( $res ) || ! isset( $hook_extra['theme'] ) || 'dt-the7' !== $hook_extra['theme'] ) {
				return $res;
			}

			$upload_dir = wp_get_upload_dir();
			$from = get_template_directory() . '/languages';
			$to = $upload_dir['basedir'] . '/the7-language-tmp';

			if ( wp_mkdir_p( $to ) ) {
				copy_dir( $from, $to, array( 'the7mk2.pot', 'the7mk2.mo' ) );
			}

			return $res;
		}

		/**
		 * Restore stored language files.
		 */
		public static function restore_lang_files( $res = true, $hook_extra = array(), $result = array() ) {
			/**
			 * @var $wp_filesystem WP_Filesystem_Base
			 */
			global $wp_filesystem;

			if ( is_wp_error( $res ) || ! isset( $hook_extra['theme'] ) || 'dt-the7' !== $hook_extra['theme'] ) {
				return $res;
			}

			$upload_dir = wp_get_upload_dir();
			$from = $upload_dir['basedir'] . '/the7-language-tmp';
			$to = get_template_directory() . '/languages';

			// Proceed only if both copy and destination folders exists.
			if ( $wp_filesystem->exists( $from ) && $wp_filesystem->exists( $to ) ) {
				$copy_result = copy_dir( $from, $to );

				// Remove backup.
				if ( ! is_wp_error( $copy_result ) ) {
					$wp_filesystem->delete( $from, true );
				}
			}

			return $res;
		}

		/**
		 * @param mixed          $return
		 * @param string         $package
		 * @param Theme_Upgrader $upgrader
		 *
		 * @return bool|WP_Error
		 */
		public static function upgrader_pre_download_theme_requirements_filter( $return, $package, $upgrader ) {
			if ( $return !== false ) {
				return $return;
			}

			if ( ! is_a( $upgrader, 'Theme_Upgrader' ) ) {
				return $return;
			}

			$theme = get_template();
			$themes_updates = get_site_transient( 'update_themes' );
			if ( ! is_object( $themes_updates ) || ! isset( $themes_updates->response[ $theme ] ) ) {
				return $return;
			}

			$the7_remote_api = new The7_Remote_API( presscore_get_purchase_code() );
			if ( ! self::is_the7_theme_download_url( $the7_remote_api, $package ) ) {
				return $return;
			}

			$validation_result = self::validate_theme_requirements( $themes_updates->response[ $theme ] );
			if ( is_wp_error( $validation_result ) ) {
				return $validation_result;
			}

			return $return;
		}

		/**
		 * Check whether the given URL is one of our theme package download endpoints — either
		 * the legacy `get_theme_download_url()` or the v2 Upgrade Channel's
		 * `get_theme_upgrade_download_url()`. Both are gated identically server-side, so they
		 * share the same Feature Lock handling.
		 *
		 * @param The7_Remote_API $the7_remote_api Remote API instance.
		 * @param string          $url             URL to check.
		 *
		 * @return bool
		 */
		private static function is_the7_theme_download_url( The7_Remote_API $the7_remote_api, $url ) {
			$download_url = strtok( $the7_remote_api->get_theme_download_url(), '?' );
			$upgrade_url  = strtok( $the7_remote_api->get_theme_upgrade_download_url(), '?' );

			return strpos( $url, $download_url ) !== false
				|| strpos( $url, $upgrade_url ) !== false;
		}

		/**
		 * Register a scoped `http_response` listener for the duration of our theme's package
		 * download, so we can react to a live 402 Feature Lock from the repo. The listener is
		 * attached only when the package being downloaded is our theme ZIP — either the legacy
		 * download URL or the v2 Upgrade Channel download URL; core keeps owning the actual
		 * download, so the base WP update flow is never altered on a 200.
		 *
		 * @param mixed       $return   Short-circuit value from earlier filters.
		 * @param string      $package  Package URL being downloaded.
		 * @param WP_Upgrader $upgrader Upgrader instance.
		 *
		 * @return mixed
		 */
		public static function register_theme_update_feature_lock_listener( $return, $package, $upgrader ) {
			if ( $return !== false ) {
				return $return;
			}

			if ( ! is_string( $package ) || $package === '' ) {
				return $return;
			}

			$the7_remote_api = new The7_Remote_API( presscore_get_purchase_code() );
			if ( ! self::is_the7_theme_download_url( $the7_remote_api, $package ) ) {
				return $return;
			}

			if ( ! has_filter( 'http_response', array( __CLASS__, 'theme_update_feature_lock_http_response_filter' ) ) ) {
				add_filter( 'http_response', array( __CLASS__, 'theme_update_feature_lock_http_response_filter' ), 10, 3 );
			}

			return $return;
		}

		/**
		 * React to a live 402 on the theme package download. The server is the gate: we never
		 * preempt from a cached verdict, never touch the 200 success path, and only act when the
		 * server itself answers 402 for our package URL — either the legacy download URL or the
		 * v2 Upgrade Channel download URL, both gated identically server-side.
		 *
		 * On a matching 402 we write a theme-authored message into the response status-message slot
		 * so core surfaces it inline as plain text, then schedule an asynchronous Feature Access
		 * refresh so the ambient notice can catch up with the live server verdict.
		 *
		 * @param array  $response HTTP response array.
		 * @param array  $args     Request args.
		 * @param string $url      Request URL.
		 *
		 * @return array
		 */
		public static function theme_update_feature_lock_http_response_filter( $response, $args, $url ) {
			if ( 402 !== (int) wp_remote_retrieve_response_code( $response ) ) {
				return $response;
			}

			$the7_remote_api = new The7_Remote_API( presscore_get_purchase_code() );
			if ( ! is_string( $url ) || ! self::is_the7_theme_download_url( $the7_remote_api, $url ) ) {
				return $response;
			}

			The7_Feature_Access::schedule_single_fetch();
			$response['response']['message'] = 'This update requires active The7 update access. Your site remains on the installed version.';

			return $response;
		}

		/**
		 * Reconcile the Feature Access verdict after a successful Theme Upgrade.
		 *
		 * The cached verdict is never expired by age, so a completed cross-line upgrade would
		 * otherwise keep showing the stale "upgrade available" notice until the next twicedaily
		 * cron tick. We schedule a non-blocking single fetch (the same idiom the 402 path uses)
		 * so the ambient verdict refreshes on the next cron spawn — which runs under the newly
		 * installed version, letting the server return the correct post-upgrade answer.
		 *
		 * Guards keep this scoped and cheap:
		 * - `is_theme_upgrade_request()` fires only for our nonce'd cross-line upgrade, so an
		 *   ordinary within-line update (which carries no upgrade nonce) never refetches and the
		 *   per-release request herd stays off.
		 * - `has_verdict()` skips the fetch when nothing is cached: with no verdict the notice
		 *   cannot be showing and the fail policy already default-allows, so a refetch is waste.
		 *
		 * A synchronous refetch here is deliberately avoided: this runs in the outgoing version's
		 * PHP process, whose `THE7_VERSION` still reports the old build, so it would receive the
		 * same stale verdict. See docs/adr/0009-refetch-feature-access-on-theme-upgrade.md.
		 */
		public static function refetch_feature_access_after_theme_upgrade() {
			if ( ! self::is_theme_upgrade_request() ) {
				return;
			}

			if ( ! The7_Feature_Access::has_verdict() ) {
				return;
			}

			The7_Feature_Access::schedule_single_fetch();
		}

		/**
		 * @param array $the7_update_data
		 *
		 * @return bool|WP_Error
		 */
		public static function validate_theme_requirements( $the7_update_data ) {
			$the7_update_data = wp_parse_args(
				$the7_update_data,
				array(
					'requires'     => '',
					'requires_php' => '',
				)
			);

			$wp_is_compatible  = is_wp_version_compatible( $the7_update_data['requires'] );
			$php_is_compatible = is_php_version_compatible( $the7_update_data['requires_php'] );

			if ( ! $wp_is_compatible && ! $php_is_compatible ) {
				return new WP_Error(
					'the7_need_to_update_wp_and_php',
					sprintf(
					/* translators: 1: WP version, 2: PHP version */
						_x(
							'Error: Current WordPress and PHP versions do not meet minimum requirements for The7. The latest theme version requires WP %1$s and PHP %2$s.',
							'admin',
							'the7mk2'
						),
						$the7_update_data['requires'],
						$the7_update_data['requires_php']
					)
				);
			}

			if ( ! $wp_is_compatible ) {
				return new WP_Error(
					'the7_requires_wp_upadate',
					sprintf(
					/* translators: %s: WP version */
						_x(
							'Error: The minimum supported WP version for The7 is %s. Please upgrade.',
							'admin',
							'the7mk2'
						),
						$the7_update_data['requires']
					)
				);
			}

			if ( ! $php_is_compatible ) {
				return new WP_Error(
					'the7_requires_php_upadate',
					sprintf(
					/* translators: %s: PHP version */
						_x(
							'Error: The minimum supported PHP version for The7 is %s. Please upgrade.',
							'admin',
							'the7mk2'
						),
						$the7_update_data['requires_php']
					)
				);
			}

			return true;
		}
	}

	Presscore_Modules_ThemeUpdateModule::execute();

endif;

if ( ! function_exists( 'presscore_theme_update_get_changelog_url' ) ) :

	function presscore_theme_update_get_changelog_url() {
		return 'https://the7.io/changelog/';
	}

endif;
