<?php
/**
 * The Compatibility Checker parent.
 *
 * @package Automattic/WooCommerce/Grow/Tools
 */

namespace Automattic\WooCommerce\Grow\Tools\CompatChecker\v0_0_1\Checks;

use WC_Admin_Notices;

defined( 'ABSPATH' ) || exit;

/**
 * The CompatCheck class.
 */
abstract class CompatCheck {

	/**
	 * Array of admin notices.
	 *
	 * @var array
	 */
	protected $notices = [];

	/**
	 * Array of CompatCheck instances.
	 *
	 * @var array
	 */
	private static $instances = [];

	/**
	 * The plugin data.
	 *
	 * @var array
	 */
	protected $plugin_data = [];

	/**
	 * Run checks
	 *
	 * @return bool
	 */
	abstract protected function run_checks();

	/**
	 * Get the instance of the CompatCheck object.
	 *
	 * @param string $plugin_basename The basename of the plugin.
	 *
	 * @return CompatCheck
	 */
	public static function instance( $plugin_basename ) {
		$class = get_called_class();
		if ( ! isset( self::$instances[ $class ][ $plugin_basename ] ) ) {
			self::$instances[ $class ][ $plugin_basename ] = new $class();
		}

		return self::$instances[ $class ][ $plugin_basename ];
	}

	/**
	 * Adds an admin notice to be displayed.
	 *
	 * @param string $slug      The slug for the notice.
	 * @param string $css_class The CSS class for the notice.
	 * @param string $message   The notice message.
	 */
	protected function add_admin_notice( $slug, $css_class, $message ) {
		$screen = get_current_screen();
		$hidden = [ 'update', 'update-network', 'update-core', 'update-core-network', 'upgrade', 'upgrade-network', 'network' ];
		$show   = isset( $screen->id ) && ! in_array( $screen->id, $hidden, true );
		$slug   = isset( $this->plugin_data['File'] ) ? plugin_basename( $this->plugin_data['File'] ) . '-' . $slug : $slug;

		/**
		 * The Compat Check filter to show an admin notice.
		 *
		 * @since 0.0.1
		 *
		 * @param bool   $show Whether to show the admin notice.
		 * @param string $slug The slug for the notice.
		 */
		if ( ! apply_filters( 'wc_grow_compat_check_show_admin_notice', $show, $slug ) ) {
			return;
		}

		// If the notice is a warning and WooCommerce admin notice system is available. Then use it.
		if ( str_contains( $css_class, 'warning' ) && class_exists( WC_Admin_Notices::class ) ) {
			// Do not display the notice if it was dismissed.
			if ( ! get_user_meta( get_current_user_id(), 'dismissed_' . $slug . '_notice', true ) ) {
				WC_Admin_Notices::add_custom_notice( $slug, $message );
			}
		} else {
			$this->notices[ $slug ] = [
				'class'   => $css_class,
				'message' => $message,
			];
		}
	}

	/**
	 * Compares major version.
	 *
	 * @param string $left     First version number.
	 * @param string $right    Second version number.
	 * @param string $operator An optional operator. The possible operators are: <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne respectively.
	 *
	 * @return int|bool
	 */
	protected function compare_major_version( $left, $right, $operator = null ) {
		$pattern = '/^(\d+\.\d+).*/';
		$replace = '$1.0';

		$left  = preg_replace( $pattern, $replace, $left );
		$right = preg_replace( $pattern, $replace, $right );

		return version_compare( $left, $right, $operator );
	}

	/**
	 * Display admin notices generated by the checker.
	 */
	public function display_admin_notices() {
		$allowed_tags = [
			'a'      => [
				'class'  => [],
				'href'   => [],
				'target' => [],
			],
			'strong' => [],
		];

		foreach ( $this->notices as $key => $notice ) {
			$class   = $notice['class'];
			$message = $notice['message'];
			printf(
				'<div class="notice notice-%1$s"><p>%2$s</p></div>',
				esc_attr( $class ),
				wp_kses( $message, $allowed_tags )
			);
		}
	}

	/**
	 * Sets the plugin data.
	 *
	 * @param array $plugin_data The plugin data.
	 */
	protected function set_plugin_data( $plugin_data ) {
		$defaults          = [
			'Name'        => '',
			'Version'     => '',
			'RequiresWP'  => '',
			'RequiresPHP' => '',
			'RequiresWC'  => '',
			'TestedWP'    => '',
			'TestedWC'    => '',
		];
		$this->plugin_data = wp_parse_args( $plugin_data, $defaults );
	}

	/**
	 * Determines if the plugin is WooCommerce compatible.
	 *
	 * @param array $plugin_data The plugin data.
	 *
	 * @return bool
	 */
	public function is_compatible( $plugin_data ) {
		$this->set_plugin_data( $plugin_data );
		add_action( 'admin_notices', [ $this, 'display_admin_notices' ], 20 );
		return $this->run_checks();
	}

	/**
	 * Remove dismissable notices.
	 */
	public function remove_dismissable_notices() {
		if ( class_exists( WC_Admin_Notices::class ) ) {
			$plugin_basename = plugin_basename( $this->plugin_data['File'] );
			$all_notices     = WC_Admin_Notices::get_notices();
			foreach ( $all_notices as $notice_name ) {
				if ( 0 === strpos( $notice_name, $plugin_basename ) ) {
					WC_Admin_Notices::remove_notice( $notice_name );
				}
			}
		}
	}
}
