<?php
/**
 * Masteriyo Pro Lemon Squeezy updater.
 *
 * @since 1.0.0
 * @package Masteriyo\Pro
 */

namespace Masteriyo\Pro;

defined( 'ABSPATH' ) || exit;


/**
 * Plugin updater class.
 */
class PluginUpdater {

	/**
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public $plugin_id;

	/**
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public $plugin_slug;

	/**
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public $version;

	/**
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public $api_url;

	/**
	 * @since 1.0.0
	 * @var string
	 */
	public $cache_key;

	/**
	 * @since 1.0.0
	 *
	 * @var boolean
	 */
	public $cache_allowed;

	/**
	 * @since 1.0.0
	 *
	 * @param string $plugin_id   The ID of the plugin.
	 * @param string $plugin_slug The slug of the plugin.
	 * @param string $version     The current version of the plugin.
	 * @param string $version     The API URL to the update server.
	 */
	public function __construct( $plugin_id, $plugin_slug, $version, $api_url ) {
		$this->plugin_id   = $plugin_id;
		$this->plugin_slug = $plugin_slug;
		$this->version     = $version;
		$this->api_url     = $api_url;

		$this->cache_key     = str_replace( '-', '_', $this->plugin_slug ) . '_updater';
		$this->cache_allowed = true; // Only disable this for debugging

		add_filter( 'plugins_api', array( $this, 'info' ), 20, 3 );
		add_filter( 'site_transient_update_plugins', array( $this, 'update' ) );
		add_action( 'upgrader_process_complete', array( $this, 'purge' ), 10, 2 );
		add_action( 'load-update-core.php', array( $this, 'check_force_update' ) );
	}

	/**
	 * Checks if the force update parameter is set and clears the update cache if necessary.
	 *
	 * @since 2.14.0
	 *
	 * @return void
	 */
	public function check_force_update() {
		if ( isset( $_GET['force-check'] ) && '1' === $_GET['force-check'] ) { // phpcs:ignore WordPress.Security.NonceVerification
			delete_transient( $this->cache_key );

			$this->request();
		}
	}

	/**
	 * Get the license key. Normally, your plugin would have a settings page where
	 * you ask for and store a license key. Fetch it here.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	protected function get_license_key() {
		return get_option( 'masteriyo_pro_license_key', null );
	}

	/**
	 * Fetch the update info from the remote server running the Lemon Squeezy plugin.
	 *
	 * @since 1.0.0
	 *
	 * @return object|bool
	 */
	public function request() {
		$lsq_license_key = $this->get_license_key();
		if ( ! $lsq_license_key ) {
			return false;
		}

		$remote = get_transient( $this->cache_key );
		if ( false !== $remote && $this->cache_allowed ) {
			if ( 'error' === $remote ) {
				return false;
			}

			return $remote;
		}

		$remote = wp_remote_get(
			$this->api_url . "/update?license_key={$lsq_license_key}",
			array(
				'timeout' => 10,
			)
		);

		if (
			is_wp_error( $remote )
			|| 200 !== wp_remote_retrieve_response_code( $remote )
			|| empty( wp_remote_retrieve_body( $remote ) )
		) {
			set_transient( $this->cache_key, 'error', MINUTE_IN_SECONDS * 10 );

			return false;
		}

		$remote = json_decode( wp_remote_retrieve_body( $remote ) );

		set_transient( $this->cache_key, $remote, 6 * HOUR_IN_SECONDS );

		return $remote;
	}

	/**
	 * Override the WordPress request to return the correct plugin info.
	 *
	 * @see https://developer.wordpress.org/reference/hooks/plugins_api/
	 *
	 * @since 1.0.0
	 *
	 * @param false|object|array $result
	 * @param string $action
	 * @param object $args
	 * @return object|bool
	 */
	public function info( $result, $action, $args ) {
		if ( 'plugin_information' !== $action ) {
			return false;
		}
		if ( $this->plugin_slug !== $args->slug ) {
			return false;
		}

		$remote = $this->request();
		if ( ! $remote || ! $remote->success || empty( $remote->update ) ) {
			return false;
		}

		$plugin_data = get_plugin_data( MASTERIYO_PLUGIN_FILE );

		$result           = $remote->update;
		$result->name     = $plugin_data['Name'];
		$result->slug     = $this->plugin_slug;
		$result->sections = (array) $result->sections;

		return $result;
	}

	/**
	 * Override the WordPress request to check if an update is available.
	 *
	 * @see https://make.wordpress.org/core/2020/07/30/recommended-usage-of-the-updates-api-to-support-the-auto-updates-ui-for-plugins-and-themes-in-wordpress-5-5/
	 *
	 * @since 1.0.0
	 *
	 * @param object $transient
	 * @return object
	 */
	public function update( $transient ) {
		if ( empty( $transient->checked ) ) {
			return $transient;
		}

		$res = (object) array(
			'id'            => $this->plugin_id,
			'slug'          => $this->plugin_slug,
			'plugin'        => $this->plugin_id,
			'new_version'   => $this->version,
			'url'           => '',
			'package'       => '',
			'icons'         => array(),
			'banners'       => array(),
			'banners_rtl'   => array(),
			'tested'        => '',
			'requires_php'  => '',
			'compatibility' => new \stdClass(),
		);

		$remote = $this->request();

		if (
			$remote && $remote->success && ! empty( $remote->update )
			&& version_compare( $this->version, $remote->update->version, '<' )
		) {
			$res->new_version = $remote->update->version;
			$res->package     = $remote->update->download_link;

			$transient->response[ $res->plugin ] = $res;
		} else {
			$transient->no_update[ $res->plugin ] = $res;
		}

		return $transient;
	}

	/**
	 * When the update is complete, purge the cache.
	 *
	 * @since 1.0.0
	 *
	 * @see https://developer.wordpress.org/reference/hooks/upgrader_process_complete/
	 *
	 * @param WP_Upgrader $upgrader
	 * @param array $options
	 * @return void
	 */
	public function purge( $upgrader, $options ) {
		if (
			$this->cache_allowed
			&& 'update' === $options['action']
			&& 'plugin' === $options['type']
			&& ! empty( $options['plugins'] )
		) {
			foreach ( $options['plugins'] as $plugin ) {
				if ( $plugin === $this->plugin_id ) {
					delete_transient( $this->cache_key );
				}
			}
		}
	}
}
