<?php
/**
 * Abstract class that implements a page.
 *
 * @package    Nelio_Content
 * @subpackage Nelio_Content/admin/pages
 * @author     David Aguilera <david.aguilera@neliosoftware.com>
 * @since      2.0.0
 */

defined( 'ABSPATH' ) || exit;

/**
 * A class that represents a page.
 */
abstract class Nelio_Content_Abstract_Page {

	/**
	 * Parent slug.
	 *
	 * @var string
	 */
	protected $parent_slug;

	/**
	 * Page slug.
	 *
	 * @var string
	 */
	protected $slug;

	/**
	 * Page title.
	 *
	 * @var string
	 */
	protected $title;

	/**
	 * Required capability to view page.
	 *
	 * @var string|bool
	 */
	protected $capability;

	/**
	 * Page rendering mode.
	 *
	 * @var 'extends-existing-page'|'regular-page'
	 */
	protected $mode;

	/**
	 * Creates an instance of this class.
	 *
	 * @param string                                 $parent_slug Parent slug.
	 * @param string                                 $slug        Page slug.
	 * @param string                                 $title       Page title.
	 * @param string|bool                            $capability  Required capability to view this page.
	 * @param 'extends-existing-page'|'regular-page' $mode        Optional. Rendering mode. Default: `regular-page`.
	 */
	public function __construct( $parent_slug, $slug, $title, $capability, $mode = 'regular-page' ) {

		$this->parent_slug = $parent_slug;
		$this->slug        = $slug;
		$this->title       = $title;
		$this->capability  = $capability;
		$this->mode        = $mode;
	}

	/**
	 * Hooks into WordPress.
	 *
	 * @return void
	 */
	public function init() {

		/**
		 * Short-circuits the addition of the given page.
		 *
		 * @param boolean $short-circuit Whether to skip the addition of this page or not. Default: `false`.
		 *
		 * @since 3.6.0
		 */
		$skip = apply_filters( "nelio_content_omit_{$this->slug}_page", false );
		if ( $skip ) {
			return;
		}

		add_action( 'admin_menu', array( $this, 'add_page' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'maybe_enqueue_assets' ) );

		add_action(
			'current_screen',
			function () {
				if ( $this->is_current_screen_this_page() ) {
					$this->add_page_specific_hooks();
				}
			}
		);
	}

	/**
	 * Adds the page.
	 *
	 * @return void
	 */
	public function add_page() {

		$capability = $this->capability;
		if ( is_bool( $capability ) ) {
			$capability = $capability ? 'read' : 'invalid-capability';
		}

		add_submenu_page(
			$this->parent_slug,
			$this->title,
			$this->title,
			$capability,
			$this->slug,
			$this->get_render_function()
		);
	}

	/**
	 * Displays the page.
	 *
	 * @return void
	 */
	public function display() {

		printf(
			'<div class="%s wrap">',
			esc_attr( $this->slug )
		);

		printf(
			'<div class="notice notice-error notice-alt hide-if-js"><p>%s</p></div>',
			esc_html_x( 'This page requires JavaScript. Please enable JavaScript in your browser settings.', 'user', 'nelio-content' )
		);

		printf(
			'<div id="%s" class="hide-if-no-js"></div>',
			esc_attr( "{$this->slug}-page" )
		);

		echo '</div>';
	}

	/**
	 * Callback to enqueue assets if the current page is this page.
	 *
	 * @return void
	 */
	public function maybe_enqueue_assets() {

		if ( ! $this->is_current_screen_this_page() ) {
			return;
		}

		$this->enqueue_assets();
	}

	/**
	 * Helper function that actually enqueues page assets.
	 *
	 * @return void
	 */
	abstract protected function enqueue_assets();

	/**
	 * Returns the appropriate render function based on the page’s mode.
	 *
	 * @return callable|''
	 */
	private function get_render_function() {

		switch ( $this->mode ) {

			case 'extends-existing-page':
				return '';

			case 'regular-page':
			default:
				return array( $this, 'display' );

		}
	}

	/**
	 * Removes the page from the menu.
	 *
	 * @param string $parent_slug Parent slug.
	 * @param string $slug   Page slug.
	 *
	 * @return void
	 */
	protected function remove_page_from_menu( $parent_slug, $slug ) {

		/** @var array<string, list<list<mixed>>> */
		global $submenu;
		if ( ! isset( $submenu[ $parent_slug ] ) ) {
			return;
		}

		// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
		$submenu[ $parent_slug ] = array_filter(
			$submenu[ $parent_slug ],
			function ( $item ) use ( $slug ) {
				return $item[2] !== $slug;
			}
		);
	}

	/**
	 * Whether the current screen is this page or not.
	 *
	 * @return bool
	 */
	protected function is_current_screen_this_page() {

		$screen = get_current_screen();
		if ( empty( $screen ) ) {
			return false;
		}

		$haystack = $screen->id;
		$needle   = str_replace( 'edit.php?post_type=', 'edit-', $this->slug );

		return strlen( $needle ) <= strlen( $haystack ) && 0 === substr_compare( $haystack, $needle, -strlen( $needle ) );
	}

	/**
	 * Adds additional hooks into WordPress.
	 *
	 * @return void
	 */
	protected function add_page_specific_hooks() {
		// Nothing to be done.
	}
}
