<?php
/**
 * Update module for plugins.
 *
 * @package northmill-online-shared
 */

declare(strict_types=1);

namespace Northmill_Online_Shared\Modules\Updaters;

use Northmill_Online_Shared\Modules\Info\Plugin_Info_Module;
use Northmill_Online_Shared\Modules\Loggers\Log_Level_Enum;
use stdClass;
use WP_Upgrader;

/**
 * Plugin updater class.
 */
class Plugin_Updater_Module extends Abstract_Updater_Module {

	/**
	 * {@inheritDoc}
	 */
	final public function setup(): void {
		parent::setup();

		if ( is_admin() ) {
			add_filter( 'plugins_api', array( $this, 'get_plugin_info' ), 20, 3 );
			add_filter( 'site_transient_update_plugins', array( $this, 'filter_site_transient_update_plugins' ) );
			add_filter( 'upgrader_process_complete', array( $this, 'after_plugin_update' ), 10, 2 );
		}
	}

	/**
	 * Gets info to display in the plugin information popup when a plugin update is available.
	 *
	 * @param object|false|array $res    Response object or array. Default false.
	 * @param string             $action The type of information being requested from the Plugin Install API.
	 * @param object             $args   Plugin API arguments.
	 *
	 * @return false|object|array Plugin info.
	 */
	public function get_plugin_info( object|false|array $res, string $action, object $args ): object|false|array {
		if (
			'plugin_information' !== $action ||
			$this->shared->package_info->get_slug() !== $args->slug
		) {
			return $res;
		}

		$remote_error = self::get_transient( 'remote_error' );

		if ( $remote_error ) {
			return $res;
		}

		$remote = self::get_remote_plugin_info();

		if ( $remote ) {
			$remote = json_decode( $remote['body'] );

			$res                 = new stdClass();
			$res->name           = $remote->name;
			$res->slug           = $this->shared->package_info->get_slug();
			$res->version        = $remote->version;
			$res->tested         = $remote->tested;
			$res->requires       = $remote->requires;
			$res->author         = $this->shared->package_info->package_data->get_author_name();
			$res->author_profile = $this->shared->package_info->package_data->get_author_uri();
			$res->download_link  = $remote->download_url;
			$res->trunk          = $remote->download_url;
			$res->last_updated   = $remote->last_updated;
			$res->sections       = array(
				'changelog' => $remote->sections->changelog,
			);

			if ( ! empty( $remote->sections->screenshots ) ) {
				$res->sections['screenshots'] = $remote->sections->screenshots;
			}

			$res->banners = array(
				'low'  => $this->shared->package_info->get_banner( type: 'low' ),
				'high' => $this->shared->package_info->get_banner(),
			);
		}

		return $res;
	}

	/**
	 * Filters the list of plugins with updates available.
	 *
	 * @param mixed $transient Transient data.
	 *
	 * @return mixed
	 */
	public function filter_site_transient_update_plugins( mixed $transient ): mixed {

		if ( empty( $transient->checked ) ) {
			return $transient;
		}

		if ( self::get_transient( 'remote_error' ) ) {
			return $transient;
		}

		/**
		 * Plugin info.
		 *
		 * @var Plugin_Info_Module $plugin_info
		 */
		$plugin_info = $this->shared->info;

		$remote = self::get_remote_plugin_info();

		if ( $remote ) {
			$remote = json_decode( $remote['body'] );
			if ( $remote && JSON_ERROR_NONE === json_last_error() ) {
				$current_plugin_version = $this->shared->package_info->package_data->get_version();
				$remote_plugin_version  = $remote->version;
				$remote_wp_required     = $remote->requires;
				$current_wp_version     = get_bloginfo( 'version' );

				if (
					version_compare( $current_plugin_version, $remote_plugin_version, '<' ) &&
					version_compare( $remote_wp_required, $current_wp_version, '<=' )
				) {
					$res                                 = new stdClass();
					$res->slug                           = $this->shared->package_info->get_slug();
					$res->plugin                         = $plugin_info->basename;
					$res->new_version                    = $remote->version;
					$res->tested                         = $remote->tested;
					$res->package                        = $remote->download_url;
					$res->compatibility                  = new stdClass();
					$transient->response[ $res->plugin ] = $res;
				}
			}
		}

		return $transient;
	}

	/**
	 * Get plugin info from Northmill server.
	 *
	 * @return array|false
	 */
	private function get_remote_plugin_info(): false|array {
		$remote = self::get_transient( 'remote_data' );
		if ( ! $remote ) {
			$remote = wp_remote_get(
				$this->shared->package_info->get_info_json_url(),
				array(
					'timeout' => 10,
					'headers' => array(
						'Accept' => 'application/json',
					),
				)
			);

			if (
				! is_wp_error( $remote ) &&
				isset( $remote['response']['code'] ) &&
				200 === $remote['response']['code'] &&
				! empty( $remote['body'] )
			) {
				self::set_transient( 'remote_data', $remote, HOUR_IN_SECONDS * 6 ); // 6-hour cache.
				self::delete_transient( 'remote_error' );
			} else {
				$this->shared->logger->log(
					'plugin-update',
					'No customizations for item',
					array(
						'plugin_info_json_url' => $this->shared->package_info->get_info_json_url(),
						'remote'               => $remote,
					),
					Log_Level_Enum::ERROR
				);
				$remote = false;
				self::set_transient( 'remote_error', true, HOUR_IN_SECONDS );
			}
		}

		return $remote;
	}

	/**
	 * After update callback.
	 *
	 * @param WP_Upgrader $upgrader_object Upgrader object.
	 * @param array       $options Options.
	 *
	 * @noinspection PhpUnusedParameterInspection WP action/filter function.
	 */
	public function after_plugin_update( WP_Upgrader $upgrader_object, array $options ): void {
		if ( 'update' === $options['action'] && 'plugin' === $options['type'] ) {
			self::delete_transient( 'remote_data' );
		}
	}
}
