<?php
/**
 * Remove Admin Bar Items Module
 *
 * @package RemoveAdminBarItems
 * @since 1.0.0
 */

namespace RemoveAdminBarItems;

use WP_Admin_Bar;
use WP_PowerSuite\Core\AbstractModule;

class Module extends AbstractModule {

	private const MODULE_ID = 'remove-admin-bar-items';
	private const MODULE_VERSION = '1.3.1';
	private const DISCOVERED_TRANSIENT = 'wpps_rabi_discovered_items';

	/**
	 * Core WordPress admin bar nodes shown in settings (id => label).
	 *
	 * @return array<string, string>
	 */
	public static function getKnownBarItems(): array {
		return [
			'wp-logo'     => __( 'WordPress Logo', 'wp-powersuite' ),
			'site-name'   => __( 'Site Name', 'wp-powersuite' ),
			'updates'     => __( 'Updates', 'wp-powersuite' ),
			'comments'    => __( 'Comments', 'wp-powersuite' ),
			'new-content' => __( 'New Content', 'wp-powersuite' ),
			'customize'   => __( 'Customize', 'wp-powersuite' ),
		];
	}

	/**
	 * Structural / special nodes never offered as hide targets.
	 *
	 * @return list<string>
	 */
	private static function getExcludedNodeIds(): array {
		return [
			'menu-toggle',
			'top-secondary',
			'my-account', // Handled by the dedicated "Remove Howdy" option.
		];
	}

	private ?array $settings_cache = null;

	public function getId(): string {
		return self::MODULE_ID;
	}

	public function getName(): string {
		return __( 'Remove Admin Bar Items', 'wp-powersuite' );
	}

	public function getVersion(): string {
		return self::MODULE_VERSION;
	}

	public function getDescription(): string {
		return __( 'Trim the WordPress toolbar by hiding menu items you do not need.', 'wp-powersuite' );
	}

	public function getContext(): string {
		return 'both';
	}

	/**
	 * Discovered third-party top-level admin bar items from recent toolbar loads.
	 *
	 * @return array<int, array{id: string, title: string}>
	 */
	public static function getDiscoveredBarItems(): array {
		$discovered = get_transient( self::DISCOVERED_TRANSIENT );
		if ( ! is_array( $discovered ) ) {
			return [];
		}

		$known_ids = array_keys( self::getKnownBarItems() );
		$excluded  = self::getExcludedNodeIds();

		$extra = [];
		foreach ( $discovered as $item ) {
			if ( ! is_array( $item ) || empty( $item['id'] ) ) {
				continue;
			}
			$id = sanitize_key( (string) $item['id'] );
			if ( $id === '' || in_array( $id, $known_ids, true ) || in_array( $id, $excluded, true ) ) {
				continue;
			}
			$extra[] = [
				'id'    => $id,
				'title' => isset( $item['title'] ) ? (string) $item['title'] : $id,
			];
		}

		return $extra;
	}

	protected function registerHooks(): void {
		// Discover after plugins register nodes, before we remove any.
		add_action( 'admin_bar_menu', [ $this, 'cacheDiscoveredBarItems' ], PHP_INT_MAX - 1 );

		$settings = $this->getSettings();

		if ( $settings['items_to_remove'] !== [] ) {
			add_action( 'admin_bar_menu', [ $this, 'preventCoreAdminBarItems' ], 5 );
			add_action( 'admin_bar_menu', [ $this, 'removeAdminBarItems' ], PHP_INT_MAX );
			add_action( 'wp_before_admin_bar_render', [ $this, 'removeAdminBarItemsBeforeRender' ] );
		}

		if ( $settings['remove_howdy'] ) {
			add_action( 'admin_bar_menu', [ $this, 'removeHowdy' ], PHP_INT_MAX - 100 );
		}

		if ( $settings['remove_help_tab'] ) {
			add_filter( 'contextual_help', [ $this, 'filterHelpTabs' ], 999, 3 );
			add_action( 'admin_head', [ $this, 'removeHelpTab' ] );
			add_action( 'admin_head', [ $this, 'hideHelpTabLink' ] );
		}

		if ( $settings['remove_screen_options'] ) {
			add_action( 'admin_head', [ $this, 'hideScreenOptions' ] );
		}
	}

	/**
	 * Cache top-level admin bar nodes for the settings UI.
	 */
	public function cacheDiscoveredBarItems( WP_Admin_Bar $wp_admin_bar ): void {
		$nodes = $wp_admin_bar->get_nodes();
		if ( ! is_array( $nodes ) || $nodes === [] ) {
			return;
		}

		$excluded = self::getExcludedNodeIds();
		$items    = [];

		foreach ( $nodes as $node ) {
			if ( ! is_object( $node ) || empty( $node->id ) ) {
				continue;
			}

			// Skip groups and nested children — top-level items only.
			if ( ! empty( $node->group ) ) {
				continue;
			}
			$parent = $node->parent ?? '';
			if ( $parent !== '' && $parent !== false && $parent !== null ) {
				continue;
			}

			$id = sanitize_key( (string) $node->id );
			if ( $id === '' || isset( $items[ $id ] ) || in_array( $id, $excluded, true ) ) {
				continue;
			}

			$title = isset( $node->title ) ? wp_strip_all_tags( (string) $node->title ) : $id;
			$title = trim( (string) preg_replace( '/\s+/', ' ', $title ) );

			$items[ $id ] = [
				'id'    => $id,
				'title' => $title !== '' ? $title : $id,
			];
		}

		if ( $items === [] ) {
			return;
		}

		// Merge with previous cache so intermittent plugin items stay listed.
		$previous = get_transient( self::DISCOVERED_TRANSIENT );
		if ( is_array( $previous ) ) {
			foreach ( $previous as $item ) {
				if ( ! is_array( $item ) || empty( $item['id'] ) ) {
					continue;
				}
				$id = sanitize_key( (string) $item['id'] );
				if ( $id === '' || isset( $items[ $id ] ) || in_array( $id, $excluded, true ) ) {
					continue;
				}
				$items[ $id ] = [
					'id'    => $id,
					'title' => isset( $item['title'] ) ? (string) $item['title'] : $id,
				];
			}
		}

		set_transient( self::DISCOVERED_TRANSIENT, array_values( $items ), DAY_IN_SECONDS );
	}

	/**
	 * Prevent core admin bar callbacks from registering selected items.
	 */
	public function preventCoreAdminBarItems( WP_Admin_Bar $wp_admin_bar ): void {
		unset( $wp_admin_bar );

		foreach ( $this->getItemsToRemove() as $item_id ) {
			match ( $item_id ) {
				'wp-logo'     => remove_action( 'admin_bar_menu', 'wp_admin_bar_wp_menu', 10 ),
				'site-name'   => remove_action( 'admin_bar_menu', 'wp_admin_bar_site_menu', 30 ),
				'customize'   => remove_action( 'admin_bar_menu', 'wp_admin_bar_customize_menu', 40 ),
				'updates'     => remove_action( 'admin_bar_menu', 'wp_admin_bar_updates_menu', 50 ),
				'comments'    => remove_action( 'admin_bar_menu', 'wp_admin_bar_comments_menu', 60 ),
				'new-content' => remove_action( 'admin_bar_menu', 'wp_admin_bar_new_content_menu', 70 ),
				default       => null,
			};
		}
	}

	public function removeAdminBarItems( WP_Admin_Bar $wp_admin_bar ): void {
		$this->removeBarNodes( $wp_admin_bar );
	}

	public function removeAdminBarItemsBeforeRender(): void {
		global $wp_admin_bar;

		if ( $wp_admin_bar instanceof WP_Admin_Bar ) {
			$this->removeBarNodes( $wp_admin_bar );
		}
	}

	private function removeBarNodes( WP_Admin_Bar $wp_admin_bar ): void {
		foreach ( $this->getItemsToRemove() as $item_id ) {
			$wp_admin_bar->remove_node( $item_id );

			if ( $item_id === 'site-name' ) {
				$wp_admin_bar->remove_node( 'view-site' );
			}

			if ( $item_id === 'customize' ) {
				foreach ( [ 'customize-background', 'customize-header', 'widgets', 'menus' ] as $child_id ) {
					$wp_admin_bar->remove_node( $child_id );
				}
			}
		}
	}

	public function removeHowdy( WP_Admin_Bar $wp_admin_bar ): void {
		remove_action( 'admin_bar_menu', 'wp_admin_bar_my_account_item', 7 );
		remove_action( 'admin_bar_menu', 'wp_admin_bar_my_account_item', 9991 );

		$user_id = get_current_user_id();
		if ( ! $user_id ) {
			return;
		}

		$current_user = wp_get_current_user();
		if ( ! $current_user->exists() ) {
			return;
		}

		$avatar       = get_avatar( $user_id, 26 );
		$display_name = $current_user->display_name;
		$class        = $avatar ? 'with-avatar' : 'no-avatar';

		$wp_admin_bar->add_menu(
			[
				'id'     => 'my-account',
				'parent' => 'top-secondary',
				'title'  => $display_name . $avatar,
				'href'   => get_edit_profile_url( $user_id ),
				'meta'   => [
					'class' => $class,
				],
			]
		);
	}

	/**
	 * @param mixed $old_help Previous help content.
	 * @param mixed $screen_id Screen ID.
	 * @param mixed $screen Current screen object.
	 * @return mixed
	 */
	public function filterHelpTabs( $old_help, $screen_id, $screen ) {
		if ( is_object( $screen ) && method_exists( $screen, 'remove_help_tabs' ) ) {
			$screen->remove_help_tabs();
		}

		return $old_help;
	}

	public function removeHelpTab(): void {
		$screen = get_current_screen();
		if ( $screen ) {
			$screen->remove_help_tabs();
		}
	}

	public function hideHelpTabLink(): void {
		echo '<style>#contextual-help-link-wrap { display: none !important; }</style>';
	}

	public function hideScreenOptions(): void {
		echo '<style>#screen-options-link-wrap { display: none !important; }</style>';
	}

	/**
	 * @return list<string>
	 */
	private function getItemsToRemove(): array {
		return $this->getSettings()['items_to_remove'];
	}

	/**
	 * @param array<string, mixed> $raw Raw stored settings.
	 * @return array{
	 *     items_to_remove: list<string>,
	 *     remove_howdy: bool,
	 *     remove_help_tab: bool,
	 *     remove_screen_options: bool
	 * }
	 */
	public static function normalizeSettings( array $raw ): array {
		$defaults = [
			'items_to_remove'       => [],
			'remove_howdy'          => false,
			'remove_help_tab'       => false,
			'remove_screen_options' => false,
		];

		$settings  = array_merge( $defaults, $raw );
		$items_raw = $settings['items_to_remove'] ?? [];
		$items     = [];
		$excluded  = self::getExcludedNodeIds();

		if ( is_array( $items_raw ) ) {
			foreach ( $items_raw as $item_id ) {
				if ( ! is_string( $item_id ) ) {
					continue;
				}
				$id = sanitize_key( $item_id );
				if ( $id === '' || in_array( $id, $excluded, true ) ) {
					continue;
				}
				$items[] = $id;
			}
			$items = array_values( array_unique( $items ) );
		}

		return [
			'items_to_remove'       => $items,
			'remove_howdy'          => ! empty( $settings['remove_howdy'] ),
			'remove_help_tab'       => ! empty( $settings['remove_help_tab'] ),
			'remove_screen_options' => ! empty( $settings['remove_screen_options'] ),
		];
	}

	/**
	 * @return array{
	 *     items_to_remove: list<string>,
	 *     remove_howdy: bool,
	 *     remove_help_tab: bool,
	 *     remove_screen_options: bool
	 * }
	 */
	private function getSettings(): array {
		if ( $this->settings_cache !== null ) {
			return $this->settings_cache;
		}

		$settings             = $this->moduleSettings()->all( [] );
		$this->settings_cache = self::normalizeSettings( is_array( $settings ) ? $settings : [] );

		return $this->settings_cache;
	}
}
