<?php
/**
 * Theme Update Checker
 *
 * @package wpmedia
 */

// Helper class to theme update json checker.
require get_template_directory() . '/inc/updater/class-theme-update.php'; // phpcs:ignore WPThemeReview.CoreFunctionality.FileInclude.FileIncludeFound

if ( ! class_exists( 'Theme_Update_Checker' ) ) :

	/**
	 * A custom theme update checker.
	 *
	 * @author Janis Elsts
	 * @copyright 2012
	 * @version 1.2
	 * @access public
	 */
	class Theme_Update_Checker {
		/**
		 * The theme associated with this update checker instance.
		 *
		 * @var string
		 */
		public $theme = '';
		/**
		 * The URL of the theme's metadata file.
		 *
		 * @var string
		 */
		public $metadata_url = '';
		/**
		 * Enable/disable automatic update checks.
		 *
		 * @var boolean
		 */
		public $enable_automatic_checking = true;
		/**
		 * Where to store update info.
		 *
		 * @var string
		 */
		protected $option_name = '';
		/**
		 * Auto check Update Done.
		 *
		 * @var string
		 */
		protected $automatic_check_done = false;
		/**
		 * Auto check Update Done.
		 *
		 * @var string
		 */
		protected static $filter_prefix = 'tuc_request_update_';

		/**
		 * Class constructor.
		 *
		 * @param string  $theme Theme slug, e.g. "twentyten".
		 * @param string  $metadata_url The URL of the theme metadata file.
		 * @param boolean $enable_automatic_checking Enable/disable automatic update checking. If set to FALSE, you'll need to explicitly call check_for_updates() to, err, check for updates.
		 */
		public function __construct( $theme, $metadata_url, $enable_automatic_checking = true ) {
			$this->metadata_url              = $metadata_url;
			$this->enable_automatic_checking = $enable_automatic_checking;
			$this->theme                     = $theme;
			$this->option_name               = 'external_theme_updates-' . $this->theme;

			$this->install_hooks();
		}

		/**
		 * Install the hooks required to run periodic update checks and inject update info
		 * into WP data structures.
		 *
		 * @return void
		 */
		public function install_hooks() {
			// Check for updates when WordPress does. We can detect when that happens by tracking
			// updates to the "update_themes" transient, which only happen in wp_update_themes().
			if ( $this->enable_automatic_checking ) {
				add_filter( 'pre_set_site_transient_update_themes', array( $this, 'on_transient_update' ) );
			}

			// Insert our update info into the update list maintained by WP.
			add_filter( 'site_transient_update_themes', array( $this, 'inject_update' ) );

			// Delete our update info when WP deletes its own.
			// This usually happens when a theme is installed, removed or upgraded.
			add_action( 'delete_site_transient_update_themes', array( $this, 'delete_stored_data' ) );
		}

		/**
		 * Retrieve update info from the configured metadata URL.
		 *
		 * Returns either an instance of Theme_Update, or NULL if there is
		 * no newer version available or if there's an error.
		 *
		 * @uses wp_remote_get()
		 *
		 * @param array $query_args Additional query arguments to append to the request. Optional.
		 * @return Theme_Update
		 */
		public function request_update( $query_args = array() ) {
			// Query args to append to the URL. Themes can add their own by using a filter callback (see add_query_arg_filter()).
			$query_args['installed_version'] = $this->get_installed_version();
			$query_args                      = apply_filters( self::$filter_prefix . 'query_args_' . $this->theme, $query_args );

			// Various options for the wp_remote_get() call. Themes can filter these, too.
			$options = array(
				'timeout' => 10, // seconds.
			);
			$options = apply_filters( self::$filter_prefix . 'options_' . $this->theme, $options );

			$url = $this->metadata_url;
			if ( ! empty( $query_args ) ) {
				$url = add_query_arg( $query_args, $url );
			}

			// Send the request.
			$result = wp_remote_get( $url, $options );

			// Try to parse the response.
			$theme_update = null;
			$code         = wp_remote_retrieve_response_code( $result );
			$body         = wp_remote_retrieve_body( $result );
			if ( ( 200 === $code ) && ! empty( $body ) ) {
				$theme_update = Theme_Update::from_json( $body );
				// The update should be newer than the currently installed version.
				if ( ( null !== $theme_update ) && version_compare( $theme_update->version, $this->get_installed_version(), '<=' ) ) {
					$theme_update = null;
				}
			}

			$theme_update = apply_filters( self::$filter_prefix . 'result_' . $this->theme, $theme_update, $result );
			return $theme_update;
		}

		/**
		 * Get the currently installed version of our theme.
		 *
		 * @return string Version number.
		 */
		public function get_installed_version() {
			if ( function_exists( 'wp_get_theme' ) ) {
				$theme = wp_get_theme( $this->theme );
				return $theme->get( 'Version' );
			}

			/** Noinspection PhpDeprecationInspection get_themes() used for compatibility with WP 3.3 and below. */
			foreach ( get_themes() as $theme ) { // phpcs:ignore
				if ( $theme['Stylesheet'] === $this->theme ) {
					return $theme['Version'];
				}
			}
			return '';
		}

		/**
		 * Check for theme updates.
		 *
		 * @return void
		 */
		public function check_for_updates() {
			$state = get_option( $this->option_name );
			if ( empty( $state ) ) {
				$state                  = new StdClass();
				$state->last_check      = 0;
				$state->checked_version = '';
				$state->update          = null;
			}

			$state->last_check      = time();
			$state->checked_version = $this->get_installed_version();
			update_option( $this->option_name, $state ); // Save before checking in case something goes wrong.

			$state->update = $this->request_update();
			update_option( $this->option_name, $state );
		}

		/**
		 * Run the automatic update check, but no more than once per page load.
		 * This is a callback for WP hooks. Do not call it directly.
		 *
		 * @param mixed $value Mixed.
		 * @return mixed
		 */
		public function on_transient_update( $value ) {
			if ( ! $this->automatic_check_done ) {
				$this->check_for_updates();
				$this->automatic_check_done = true;
			}
			return $value;
		}

		/**
		 * Insert the latest update (if any) into the update list maintained by WP.
		 *
		 * @param StdClass $updates Update list.
		 * @return array Modified update list.
		 */
		public function inject_update( $updates ) {
			$state = get_option( $this->option_name );

			if ( ! is_object( $updates ) ) {
				$updates           = new stdClass();
				$updates->response = array();
			}

			// Is there an update to insert?
			if ( ! empty( $state ) && isset( $state->update ) && ! empty( $state->update ) ) {
				$updates->response[ $this->theme ] = $state->update->to_wp_format();
			}

			return $updates;
		}

		/**
		 * Delete any stored book-keeping data.
		 *
		 * @return void
		 */
		public function delete_stored_data() {
			delete_option( $this->option_name );
		}

		/**
		 * Register a callback for filtering query arguments.
		 *
		 * The callback function should take one argument - an associative array of query arguments.
		 * It should return a modified array of query arguments.
		 *
		 * @param callable $callback Callback.
		 * @return void
		 */
		public function add_query_arg_filter( $callback ) {
			add_filter( self::$filter_prefix . 'query_args_' . $this->theme, $callback );
		}

		/**
		 * Register a callback for filtering arguments passed to wp_remote_get().
		 *
		 * The callback function should take one argument - an associative array of arguments -
		 * and return a modified array or arguments. See the WP documentation on wp_remote_get()
		 * for details on what arguments are available and how they work.
		 *
		 * @param callable $callback Callback.
		 * @return void
		 */
		public function add_http_request_arg_filter( $callback ) {
			add_filter( self::$filter_prefix . 'options_' . $this->theme, $callback );
		}

		/**
		 * Register a callback for filtering the theme info retrieved from the external API.
		 *
		 * The callback function should take two arguments. If a theme update was retrieved
		 * successfully, the first argument passed will be an instance of  Theme_Update. Otherwise,
		 * it will be NULL. The second argument will be the corresponding return value of
		 * wp_remote_get (see WP docs for details).
		 *
		 * The callback function should return a new or modified instance of Theme_Update or NULL.
		 *
		 * @param callable $callback Callback.
		 * @return void
		 */
		public function add_result_filter( $callback ) {
			add_filter( self::$filter_prefix . 'result_' . $this->theme, $callback, 10, 2 );
		}
	}

endif;

$files          = 'https://www.kentooz.com/files/';
$update_checker = new Theme_Update_Checker(
	'wpmedia',
	$files . 'wpmedia/b3liL4HWpM3d1Ay4n9A5L1A94rB3rk4h.json'
);
