<?php
/**
 * Clanbite wp-admin: unified React settings + CPT submenu placement.
 *
 * @package clanbite
 */

namespace Kernowdev\Clanbite\Admin;
defined( 'ABSPATH' ) || exit;

use Kernowdev\Clanbite\Extensions\Abstract_Settings;
use Kernowdev\Clanbite\Extensions\Loader;
use Kernowdev\Clanbite\Extensions\Skeleton;

require_once __DIR__ . '/class-general-settings.php';
require_once __DIR__ . '/class-groups-settings.php';
require_once __DIR__ . '/class-admin-rest.php';

/**
 * Registers the top-level menu, enqueues the React shell, and wires REST.
 */
class Settings {

	protected static ?Settings $instance = null;

	protected General_Settings $general_settings;

	protected Groups_Settings $groups_settings;

	/**
	 * @var array<string, Abstract_Settings>
	 */
	protected array $settings_registry = array();

	protected Admin_Rest $rest;

	public static function instance(): Settings {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	protected function __construct() {
		$this->general_settings = new General_Settings();
		$this->groups_settings  = new Groups_Settings();

		$this->settings_registry[ $this->general_settings->get_option_key() ] = $this->general_settings;
		$this->settings_registry[ $this->groups_settings->get_option_key() ]  = $this->groups_settings;

		$loader = Loader::instance();
		foreach ( $loader->get_extensions() as $slug => $extension ) {
			if ( ! $extension instanceof Skeleton ) {
				continue;
			}
			if ( ! $loader->is_extension_installed( $slug ) ) {
				continue;
			}
			$admin = $extension->get_settings_admin();
			if ( $admin instanceof Abstract_Settings ) {
				$this->settings_registry[ $admin->get_option_key() ] = $admin;
			}
		}

		/**
		 * Filter: `clanbite_admin_rest_settings_registry`.
		 *
		 * @param array<string, Abstract_Settings> $registry Option key => handler.
		 * @param Loader                           $loader    Extension loader.
		 */
		$filtered = apply_filters( 'clanbite_admin_rest_settings_registry', $this->settings_registry, $loader );
		if ( is_array( $filtered ) ) {
			$this->settings_registry = array();
			foreach ( $filtered as $key => $handler ) {
				if ( ! is_string( $key ) || '' === $key ) {
					continue;
				}
				if ( ! $handler instanceof Abstract_Settings ) {
					continue;
				}
				$this->settings_registry[ $key ] = $handler;
			}
		}

		$this->rest = new Admin_Rest( $this->settings_registry );

		add_action( 'rest_api_init', array( $this->rest, 'register_routes' ) );
		// Priority 5: register before CPT submenus (usually priority 10) so the
		// duplicate "clanbite" submenu is first — otherwise the parent menu
		// links to the first real child (e.g. Teams) instead of this screen.
		add_action( 'admin_menu', array( $this, 'register_admin_pages' ), 5 );
		add_action( 'admin_enqueue_scripts', array( $this, 'maybe_enqueue_admin_app' ) );
		add_action( 'admin_init', array( $this, 'redirect_legacy_extension_submenu' ) );
	}

	/**
	 * Old bookmark to `clanbite-extensions` lands on the unified screen.
	 */
	public function redirect_legacy_extension_submenu(): void {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only legacy `?page=` redirect; value is compared to a constant after `sanitize_key()`.
		if ( ! is_admin() || ! isset( $_GET['page'] ) ) {
			return;
		}
		if ( 'clanbite-extensions' !== sanitize_key( wp_unslash( $_GET['page'] ) ) ) {
			return;
		}
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		// phpcs:enable WordPress.Security.NonceVerification.Recommended
		wp_safe_redirect( admin_url( 'admin.php?page=clanbite' ) );
		exit;
	}

	public function register_admin_pages(): void {
		add_menu_page(
			__( 'Clanbite', 'clanbite' ),
			__( 'Clanbite', 'clanbite' ),
			'manage_options',
			'clanbite',
			array( $this, 'render_main_page' ),
			$this->get_admin_menu_icon_data_uri(),
			56
		);

		// Required: same slug as parent so this is the first submenu entry; WP uses
		// that for the top-level menu href and avoids jumping straight to CPTs.
		add_submenu_page(
			'clanbite',
			__( 'Clanbite settings', 'clanbite' ),
			__( 'Settings', 'clanbite' ),
			'manage_options',
			'clanbite',
			array( $this, 'render_main_page' )
		);
	}

	/**
	 * Enqueue the unified settings app on the main Clanbite screen only.
	 */
	public function maybe_enqueue_admin_app( string $hook_suffix ): void {
		// Top-level only: toplevel_page_clanbite.
		$settings_hooks = array( 'toplevel_page_clanbite' );
		if ( ! in_array( $hook_suffix, $settings_hooks, true ) ) {
			return;
		}

		$handle = 'clanbite-admin-app';
		$rel    = \clanbite()->path . 'assets/dist/clanbite-admin.js';
		$url    = \clanbite()->url . 'assets/dist/clanbite-admin.js';

		if ( ! file_exists( $rel ) ) {
			add_action(
				'admin_notices',
				static function (): void {
					printf(
						'<div class="notice notice-warning"><p>%s</p></div>',
						esc_html__( 'Clanbite admin UI assets are missing. Run `npm run build:admin` from the plugin directory.', 'clanbite' )
					);
				}
			);
			return;
		}

		$asset_file = dirname( $rel ) . '/clanbite-admin.asset.php';
		$asset      = file_exists( $asset_file ) ? include $asset_file : array(
			'dependencies' => array(),
			'version'      => (string) filemtime( $rel ),
		);
		$deps       = is_array( $asset['dependencies'] ?? null ) ? $asset['dependencies'] : array();
		$ver        = isset( $asset['version'] ) ? (string) $asset['version'] : (string) filemtime( $rel );

		// Register media scripts first, then load the app after `media-editor` so `window.wp.media` exists.
		if ( function_exists( 'wp_enqueue_media' ) ) {
			wp_enqueue_media();
		}
		if ( ! in_array( 'media-editor', $deps, true ) ) {
			$deps[] = 'media-editor';
		}

		wp_enqueue_script(
			$handle,
			$url,
			$deps,
			$ver,
			true
		);

		wp_enqueue_style( 'wp-components' );

		// CSS is emitted by wp-scripts as style-{entry}.css automatically.
		wp_enqueue_style( 'clanbite-admin', \clanbite()->url . 'assets/dist/style-clanbite-admin.css', array( 'wp-components' ), $ver );

		wp_localize_script(
			$handle,
			'clanbiteAdmin',
			array(
				'restUrl'        => esc_url_raw( rest_url( 'clanbite/v1/' ) ),
				'nonce'          => wp_create_nonce( 'wp_rest' ),
				'logoUrl'        => \clanbite()->url . 'assets/img/logos/clanbite-logo.svg',
				'iconPacks'      => Admin_Rest::get_unified_icon_packs(),
				'iconPickerI18n' => Admin_Rest::get_default_icon_picker_i18n(),
			)
		);
	}

	public function render_main_page(): void {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		echo '<div class="wrap"><div id="clanbite-admin-root"></div></div>';
	}

	/**
	 * Resolve the top-level wp-admin menu icon in the format recommended by core docs.
	 *
	 * WordPress supports SVG icons in {@see add_menu_page()} when passed as a
	 * `data:image/svg+xml;base64,...` URI.
	 *
	 * @return string
	 */
	protected function get_admin_menu_icon_data_uri(): string {
		$icon_path = \clanbite()->path . 'assets/img/logos/clanbite-icon.svg';
		if ( ! is_readable( $icon_path ) ) {
			return 'dashicons-admin-generic';
		}

		$svg = file_get_contents( $icon_path );
		if ( false === $svg || '' === trim( $svg ) ) {
			return 'dashicons-admin-generic';
		}

		return 'data:image/svg+xml;base64,' . base64_encode( $svg );
	}
}
