<?php

namespace WarrantyCart\Admin;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Admin Menu Class
 */
class Menu {

	/**
	 * Constructor
	 */
	public function __construct() {
		add_action( 'admin_menu', array( $this, 'register' ) );
		add_action( 'admin_head', array( $this, 'print_menu_icon_style' ) );

		add_filter( 'admin_footer_text', array( $this, 'remove_footer_text' ) );
		// Priority 11: core hooks its version string on at 10, and the later
		// callback is the one that wins.
		add_filter( 'update_footer', array( $this, 'remove_footer_text' ), 11 );

		/*
		 * Late enough that everyone has registered, early enough that nothing
		 * has printed. `in_admin_header` fires after every plugin's `admin_init`
		 * and before the notice hooks run, so removing callbacks here catches
		 * plugins that add theirs at any point during the request.
		 */
		add_action( 'in_admin_header', array( $this, 'suppress_admin_notices' ), PHP_INT_MAX );
	}

	/**
	 * Keep other plugins' notices off this plugin's screens.
	 *
	 * The warranty admin is a single-page app that owns the whole content area
	 * and lays itself out against the viewport. WordPress prints `admin_notices`
	 * above that, and by convention every plugin on the site uses them — review
	 * requests, upgrade nags, "connect your account" banners. On a normal
	 * options page they push the form down harmlessly. Here they land inside a
	 * fixed shell, above a sidebar and topbar that have already committed to
	 * their offsets, and the app is pushed off the bottom of the screen.
	 *
	 * Scoped to this plugin's own screens, so nothing is hidden anywhere a user
	 * would otherwise have seen it. A notice that matters — WooCommerce asking
	 * for a database update, a security warning — is still shown on every other
	 * screen in the admin, including the one it is about.
	 *
	 * This plugin does not use `admin_notices` itself; the app reports its own
	 * state inline, where the thing it refers to is.
	 *
	 * @return void
	 */
	public function suppress_admin_notices(): void {
		if ( ! $this->is_plugin_screen() ) {
			return;
		}

		remove_all_actions( 'admin_notices' );
		remove_all_actions( 'all_admin_notices' );
		remove_all_actions( 'user_admin_notices' );
		remove_all_actions( 'network_admin_notices' );

		/**
		 * Fires after other plugins' notices are cleared from a warranty screen.
		 *
		 * Re-add anything that genuinely has to appear here.
		 */
		do_action( 'warranty_cart_admin_notices_suppressed' );
	}

	/**
	 * Paint the menu icon as a CSS mask.
	 *
	 * WordPress tints menu icons to match the active admin colour scheme, and
	 * changes that tint on hover and for the current item. An <img> cannot
	 * follow any of that. A mask can: the glyph supplies the shape and
	 * `background-color: currentColor` takes the colour core has already
	 * computed for the item.
	 *
	 * Printed rather than enqueued because it is four declarations that only
	 * apply to one selector; a stylesheet request for this would cost more than
	 * it saves.
	 *
	 * @return void
	 */
	public function print_menu_icon_style(): void {
		$icon = WARRANTY_CART_URL . 'assets/images/admin-menu-icon.svg';

		/*
		 * The metrics match a dashicon's exactly, so the mark sits on the same
		 * baseline as every other item in the menu.
		 *
		 * Core styles `.wp-menu-image::before` as an inline-block glyph at
		 * `font: normal 20px/1 dashicons` with `padding: 8px 0`, inside a
		 * 36x34 parent that centres it horizontally. An earlier version of this
		 * rule used `display: block` with `margin: 0 auto`, which takes the
		 * element out of that flow and drops it below its neighbours.
		 *
		 * So: keep `inline-block`, keep core's padding, and let the parent's
		 * `text-align: center` do the horizontal centring. `content-box` is
		 * explicit because a `box-sizing: border-box` reset from another plugin
		 * would otherwise eat the padding and shift the icon up by 8px.
		 */
		printf(
			'<style id="warranty-cart-menu-icon">
				#adminmenu .toplevel_page_warranty-cart .wp-menu-image::before {
					content: "";
					display: inline-block;
					box-sizing: content-box;
					width: 20px;
					height: 20px;
					padding: 8px 0;
					background-color: currentColor;
					-webkit-mask: url(%1$s) no-repeat center / 20px 20px;
					mask: url(%1$s) no-repeat center / 20px 20px;
				}
			</style>',
			esc_url( $icon )
		);
	}

	/**
	 * Whether the current screen belongs to this plugin.
	 *
	 * @return bool
	 */
	private function is_plugin_screen(): bool {
		if ( ! function_exists( 'get_current_screen' ) ) {
			return false;
		}

		$screen = get_current_screen();

		return $screen && false !== strpos( $screen->id, 'warranty-cart' );
	}

	/**
	 * Clear WordPress's admin footer on this plugin's screens.
	 *
	 * The admin is a single-page app that owns the full height of the content
	 * area, and the footer — "Thank you for creating with WordPress" and the
	 * version number — renders inside it, below a layout that has already
	 * accounted for the viewport. The result is a stray line of text under an
	 * app that looks finished without it.
	 *
	 * Scoped to this plugin's own screens; every other admin page keeps its
	 * footer, because removing it site-wide would be taking something that is
	 * not ours to take.
	 *
	 * @param string $text The existing footer text.
	 * @return string      Empty on our screens, unchanged everywhere else.
	 */
	public function remove_footer_text( string $text = '' ): string {
		return $this->is_plugin_screen() ? '' : $text;
	}

	/**
	 * Register admin menu
	 */
	public function register(): void {
		global $submenu;

		$slug          = 'warranty-cart';
		$capability    = apply_filters( 'warranty_cart_menu_capability', 'manage_warranty_cart' );
		$menu_position = apply_filters( 'warranty_cart_menu_position', 56 );

		/*
		 * 'none', not a dashicon.
		 *
		 * The plugin has its own mark and was borrowing dashicons-shield, which
		 * is a different shield from the one on the listing icon, the banners
		 * and the app's own sidebar — four surfaces, three of them agreeing.
		 * 'none' leaves the slot empty for print_menu_icon_style() to fill with
		 * a CSS mask of assets/images/admin-menu-icon.svg, which is how the
		 * mark can still recolour itself per admin colour scheme and per state.
		 * A plain <img> could not: it would stay one colour on every scheme and
		 * ignore hover and current entirely.
		 */
		$menu_icon = 'none';

		/**
		 * Filter the admin menu icon
		 *
		 * @param string $menu_icon Menu icon.
		 */
		$menu_icon = apply_filters( 'warranty_cart_admin_menu_icon', $menu_icon );

		// Main menu page
		$this->add_menu(
			__( 'Warranty Cart', 'warranty-cart' ),
			__( 'Warranty Cart', 'warranty-cart' ),
			$capability,
			$slug,
			array( $this, 'render_dashboard_page' ),
			$menu_icon,
			$menu_position
		);

		/*
		 * No submenu of our own.
		 *
		 * There were six items — Dashboard, Warranties, Orders, Reports, Tools,
		 * Settings — each a hash link into the single-page app. They were a
		 * duplicate of the app's own sidebar, and a stale one: the sidebar has
		 * eleven destinations in three groups, so Records, Claims, Checkout
		 * Fields, Getting Started and Our Plugins were reachable only from
		 * inside. Two navigations disagreeing about what the plugin contains is
		 * worse than one.
		 *
		 * They were also inert as WordPress menu items. A fragment is not sent
		 * to the server, so every one of them pointed at the same URL as far as
		 * WordPress was concerned: it highlighted the first regardless of where
		 * the app actually was, and a middle-click or a bookmark landed on the
		 * dashboard whatever had been clicked.
		 *
		 * The filter stays, applied to an empty list. Removing it would break a
		 * site that uses it to add an item of its own, which is the use the
		 * documentation gives it — and that use still works.
		 */
		if ( current_user_can( $capability ) ) {
			/**
			 * Filter admin menu submenu items
			 *
			 * @param array  $submenu_items Submenu items. Empty by default: the
			 *                              app carries its own navigation.
			 * @param string $capability    Menu capability.
			 * @param string $slug          Menu slug.
			 */
			$items = apply_filters( 'warranty_cart_admin_menu_submenu', array(), $capability, $slug );

			if ( ! empty( $items ) && is_array( $items ) ) {
				// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
				$submenu[ $slug ] = $items;
			}
		}

		/**
		 * Action for adding custom menu items
		 *
		 * @param string $capability    Menu capability.
		 * @param int    $menu_position Menu position.
		 */
		do_action( 'warranty_cart_admin_menu', $capability, $menu_position );
	}

	/**
	 * Add menu page helper
	 *
	 * @param string   $page_title Page title.
	 * @param string   $menu_title Menu title.
	 * @param string   $capability Capability required to access the menu.
	 * @param string   $menu_slug  Menu slug.
	 * @param callable $callback   Callback function to render the page.
	 * @param string   $icon       Menu icon.
	 * @param int      $position   Menu position.
	 */
	private function add_menu( string $page_title, string $menu_title, string $capability, string $menu_slug, callable $callback, string $icon, int $position ): void {
		add_menu_page(
			$page_title,
			$menu_title,
			$capability,
			$menu_slug,
			$callback,
			$icon,
			$position
		);
	}

	/**
	 * Render dashboard page
	 */
	public function render_dashboard_page(): void {
		echo '<div id="warranty-cart-dashboard"></div>';
	}
}
