<?php
/**
 * Mersal Suite — shared admin shell for the Mersal plugin family.
 *
 * This class is shipped *inside every Mersal plugin* so each one keeps working
 * standalone. A tiny version-aware loader (suite-loader.php) makes sure only the
 * newest copy is ever loaded, and every active Mersal plugin registers itself
 * into the shared registry. The Suite then renders ONE unified admin experience
 * (the dark-green sidebar + topbar + suite switcher + hub) and dispatches each
 * route to the plugin that owns it.
 *
 * @package Mersal_Suite
 */

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

if ( class_exists( 'Mersal_Suite' ) ) {
	return;
}

require_once __DIR__ . '/suite-shell.php';
require_once __DIR__ . '/suite-system.php';
require_once __DIR__ . '/class-mersal-suite-catalog.php';
require_once __DIR__ . '/class-mersal-suite-account.php';
require_once __DIR__ . '/class-mersal-suite-remote.php';
require_once __DIR__ . '/class-mersal-suite-wizard.php';
require_once __DIR__ . '/class-mersal-suite-fields.php';

/**
 * The shared Suite controller.
 */
class Mersal_Suite {

	use Mersal_Suite_Shell;
	use Mersal_Suite_System;

	const VERSION   = '1.5.0';
	const MENU_SLUG = 'mersal-suite';
	const PREFS_KEY = 'mersal_suite_prefs';
	const OPT_KEY   = 'mersal_suite_settings';

	/** @var Mersal_Suite|null */
	private static $instance = null;

	/** @var array<string,array> Registered plugin descriptors keyed by id. */
	private $plugins = array();

	/** @var bool Whether the admin hooks have been wired. */
	private $booted = false;

	/**
	 * Singleton accessor.
	 *
	 * @return Mersal_Suite
	 */
	public static function instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Register a Mersal plugin into the Suite.
	 *
	 * @param array $args {
	 *     @type string   $id            Short id, e.g. 'otp', 'nt', 'cm'.
	 *     @type array    $name          ['ar'=>..,'en'=>..] display name.
	 *     @type array    $sub           ['ar'=>..,'en'=>..] tagline.
	 *     @type string   $icon          Material Symbols icon name.
	 *     @type string   $accent        Hex accent colour.
	 *     @type string   $version       Plugin version string.
	 *     @type string   $brand_icon    URL of the square brand mark (optional).
	 *     @type int      $order         Sort order (lower = earlier).
	 *     @type array    $nav           Sidebar nav groups (see navForPlugin format).
	 *     @type array    $routes        Map of route => callable( Mersal_Suite $ctx ).
	 *     @type string   $default_route Route opened when the plugin is picked.
	 * }
	 */
	public static function register( array $args ) {
		if ( empty( $args['id'] ) ) {
			return;
		}
		$suite = self::instance();
		$defaults = array(
			'id'            => '',
			'name'          => array( 'ar' => '', 'en' => '' ),
			'sub'           => array( 'ar' => '', 'en' => '' ),
			'icon'          => 'extension',
			'accent'        => '#0f7a4a',
			'version'       => '1.0.0',
			'brand_icon'    => '',
			'order'         => 50,
			'nav'           => array(),
			'routes'        => array(),
			'default_route' => $args['id'] . '.dash',
		);
		$suite->plugins[ $args['id'] ] = wp_parse_args( $args, $defaults );
		$suite->boot();
	}

	/**
	 * Wire the admin hooks (idempotent — runs once across all plugins).
	 */
	public function boot() {
		if ( $this->booted || ! is_admin() ) {
			return;
		}
		$this->booted = true;
		Mersal_Suite_Account::init();
		if ( class_exists( 'Mersal_Suite_Remote' ) ) {
			Mersal_Suite_Remote::init();
		}
		add_action( 'admin_menu', array( $this, 'register_menu' ), 9 );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
		add_action( 'admin_body_class', array( $this, 'body_class' ) );
		add_action( 'wp_ajax_mersal_suite_pref', array( $this, 'ajax_pref' ) );
		add_action( 'wp_ajax_mersal_suite_toggle', array( $this, 'ajax_toggle' ) );
		add_action( 'wp_ajax_mersal_suite_save', array( $this, 'ajax_save' ) );
	}

	/* ------------------------------------------------------------------ */
	/* Helpers                                                            */
	/* ------------------------------------------------------------------ */

	/**
	 * Sorted list of registered plugins.
	 *
	 * @return array
	 */
	public function plugins() {
		$p = $this->plugins;
		uasort(
			$p,
			static function ( $a, $b ) {
				return (int) $a['order'] <=> (int) $b['order'];
			}
		);
		return $p;
	}

	/**
	 * Current language ('ar' | 'en').
	 *
	 * @return string
	 */
	public function lang() {
		$prefs = $this->prefs();
		return 'en' === $prefs['lang'] ? 'en' : 'ar';
	}

	/**
	 * Whether dark mode is on.
	 *
	 * @return bool
	 */
	public function is_dark() {
		$prefs = $this->prefs();
		return ! empty( $prefs['dark'] );
	}

	/**
	 * Read the shared UI prefs (per-user).
	 *
	 * @return array{lang:string,dark:bool}
	 */
	public function prefs() {
		$prefs = get_user_meta( get_current_user_id(), self::PREFS_KEY, true );
		if ( ! is_array( $prefs ) ) {
			$prefs = array();
		}
		return wp_parse_args(
			$prefs,
			array(
				'lang' => 'ar',
				'dark' => false,
			)
		);
	}

	/**
	 * Pick the localized value from an ['ar'=>..,'en'=>..] array (or a scalar).
	 *
	 * @param mixed $val Value.
	 * @return string
	 */
	public function L( $val ) {
		if ( is_array( $val ) ) {
			$lang = $this->lang();
			return isset( $val[ $lang ] ) ? (string) $val[ $lang ] : (string) ( $val['ar'] ?? reset( $val ) );
		}
		return (string) $val;
	}

	/**
	 * Current route id (e.g. 'hub', 'otp.channels').
	 *
	 * @return string
	 */
	public function route() {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$r = isset( $_GET['route'] ) ? sanitize_text_field( wp_unslash( $_GET['route'] ) ) : '';
		$r = preg_replace( '/[^a-z0-9._-]/', '', strtolower( $r ) );
		if ( '' === $r ) {
			$r = 'hub';
		}
		return $r;
	}

	/**
	 * Plugin id that owns the current route ('suite' for hub/system).
	 *
	 * @param string|null $route Route override.
	 * @return string
	 */
	public function current_plugin( $route = null ) {
		$route = null === $route ? $this->route() : $route;
		foreach ( array_keys( $this->plugins ) as $id ) {
			if ( 0 === strpos( $route, $id . '.' ) ) {
				return $id;
			}
		}
		return 'suite';
	}

	/**
	 * Build a URL to a Suite route.
	 *
	 * @param string $route Route id.
	 * @return string
	 */
	public function url( $route = 'hub' ) {
		return admin_url( 'admin.php?page=' . self::MENU_SLUG . '&route=' . rawurlencode( $route ) );
	}

	/**
	 * Are we on the Suite admin screen?
	 *
	 * @return bool
	 */
	private function is_our_screen() {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		return is_admin() && isset( $_GET['page'] ) && self::MENU_SLUG === $_GET['page'];
	}

	/**
	 * Read a shipped Suite asset from disk.
	 *
	 * @param string $rel Relative path under includes/suite/.
	 * @return string
	 */
	private function asset( $rel ) {
		$path = __DIR__ . '/' . ltrim( $rel, '/' );
		return file_exists( $path ) ? (string) file_get_contents( $path ) : '';
	}

	/* ------------------------------------------------------------------ */
	/* WP plumbing                                                        */
	/* ------------------------------------------------------------------ */

	/**
	 * Register the Mersal Plugins admin menu + submenus.
	 */
	public function register_menu() {
		$ar    = 'ar' === $this->lang();
		$title = $ar ? 'إضافات مرسال' : 'Mersal Plugins';
		add_menu_page(
			$title,
			$title,
			'manage_options',
			self::MENU_SLUG,
			array( $this, 'render' ),
			$this->menu_icon(),
			58
		);

		$subs = array(
			array( 'hub', $ar ? 'مرسال سويت' : 'Mersal Suite' ),
			array( 'otp', $ar ? 'مرسال OTP' : 'Mersal OTP' ),
			array( 'nt', $ar ? 'مرسال للإشعارات' : 'Mersal Notifications' ),
			array( 'cm', $ar ? 'مرسال للحملات' : 'Mersal Campaigns' ),
			array( 'updates', $ar ? 'التحديثات' : 'Updates' ),
		);
		foreach ( $subs as $sub ) {
			add_submenu_page(
				self::MENU_SLUG,
				$sub[1],
				$sub[1],
				'manage_options',
				self::MENU_SLUG,
				array( $this, 'render' )
			);
		}
		add_action( 'admin_menu', array( $this, 'patch_submenu_links' ), 1000 );
	}

	/**
	 * Point each submenu item at its Suite route (or activate/download).
	 */
	public function patch_submenu_links() {
		global $submenu;
		if ( ! isset( $submenu[ self::MENU_SLUG ] ) || ! is_array( $submenu[ self::MENU_SLUG ] ) ) {
			return;
		}
		$targets = array(
			'hub',
			'otp',
			'nt',
			'cm',
			'updates',
		);
		foreach ( $submenu[ self::MENU_SLUG ] as $i => &$row ) {
			if ( ! isset( $targets[ $i ] ) ) {
				continue;
			}
			$key = $targets[ $i ];
			if ( 'hub' === $key ) {
				$row[2] = 'admin.php?page=' . self::MENU_SLUG . '&route=hub';
			} elseif ( 'updates' === $key ) {
				$row[2] = 'admin.php?page=' . self::MENU_SLUG . '&route=updates';
			} elseif ( class_exists( 'Mersal_Suite_Catalog' ) ) {
				$row[2] = Mersal_Suite_Catalog::resolve_url( $key );
				if ( Mersal_Suite_Catalog::is_active( $key ) ) {
					$def  = Mersal_Suite_Catalog::meta( $key, $this );
					$leaf = $def ? $def['default_route'] : $key . '.dash';
					$row[2] = 'admin.php?page=' . self::MENU_SLUG . '&route=' . rawurlencode( $leaf );
				}
			}
		}
		unset( $row );
	}

	/**
	 * Admin menu icon (shipped brand mark).
	 *
	 * @return string
	 */
	private function menu_icon() {
		$png         = __DIR__ . '/assets/mersal-plugins-icon.png';
		$svg         = __DIR__ . '/assets/mersal-plugins-icon.svg';
		$plugin_file = defined( 'MNOTIF_FILE' ) ? MNOTIF_FILE : dirname( dirname( __DIR__ ) ) . '/mersal-notifications.php';
		if ( file_exists( $png ) ) {
			return plugins_url( 'includes/suite/assets/mersal-plugins-icon.png', $plugin_file );
		}
		if ( file_exists( $svg ) ) {
			// Big (traced) SVGs go by URL — inlining them as base64 bloats
			// every admin page; small marks stay inline to save a request.
			if ( filesize( $svg ) > 4096 ) {
				return plugins_url( 'includes/suite/assets/mersal-plugins-icon.svg', $plugin_file );
			}
			$raw = (string) file_get_contents( $svg );
			return 'data:image/svg+xml;base64,' . base64_encode( $raw );
		}
		$fallback = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#a7aaad"><path d="M12 2 3 7v10l9 5 9-5V7l-9-5zm0 2.3 6.5 3.6L12 11.5 5.5 7.9 12 4.3zM5 9.3l6 3.3v6.8l-6-3.3V9.3zm14 0v6.8l-6 3.3v-6.8l6-3.3z"/></svg>';
		return 'data:image/svg+xml;base64,' . base64_encode( $fallback );
	}

	/**
	 * Body class so the CSS can take over the canvas.
	 *
	 * @param string $classes Existing classes.
	 * @return string
	 */
	public function body_class( $classes ) {
		if ( $this->is_our_screen() ) {
			$classes .= ' mersal-suite-screen';
		}
		return $classes;
	}

	/**
	 * Enqueue the web fonts on our screen (CSS/JS print inline in render()).
	 *
	 * @param string $hook Current admin page hook.
	 */
	public function enqueue( $hook ) {
		if ( ! $this->is_our_screen() ) {
			return;
		}
		wp_enqueue_style(
			'mersal-suite-fonts',
			'https://fonts.googleapis.com/css2?family=Cairo:wght@400;500;600;700;800;900&family=Inter:wght@400;500;600;700&family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@24,400,0,0&display=block',
			array(),
			null
		);
	}

	/* ------------------------------------------------------------------ */
	/* AJAX                                                               */
	/* ------------------------------------------------------------------ */

	/**
	 * Persist a UI preference (lang / dark).
	 */
	public function ajax_pref() {
		check_ajax_referer( 'mersal_suite_pref', 'nonce' );
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
		$prefs = $this->prefs();
		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified above.
		if ( isset( $_POST['lang'] ) ) {
			$prefs['lang'] = 'en' === $_POST['lang'] ? 'en' : 'ar';
		}
		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified above.
		if ( isset( $_POST['dark'] ) ) {
			$prefs['dark'] = '1' === (string) $_POST['dark'] || 'true' === (string) $_POST['dark'];
		}
		update_user_meta( get_current_user_id(), self::PREFS_KEY, $prefs );
		wp_send_json_success( $prefs );
	}

	/**
	 * Toggle a boolean option (used by integration / general-toggle switches the
	 * SDK owns; plugins handle their own toggles via their own endpoints).
	 */
	public function ajax_toggle() {
		check_ajax_referer( 'mersal_suite_save', 'nonce' );
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified above.
		$key = isset( $_POST['key'] ) ? sanitize_key( wp_unslash( $_POST['key'] ) ) : '';
		if ( '' === $key ) {
			wp_send_json_error( array( 'message' => 'bad key' ), 400 );
		}
		$opts = $this->settings();
		if ( 'autoUpdate' === $key && class_exists( 'Mersal_Suite_Remote' ) ) {
			// The real state lives in the per-plugin updater options.
			$opts[ $key ] = ! Mersal_Suite_Remote::auto_update_on();
		} else {
			$opts[ $key ] = empty( $opts[ $key ] );
		}
		update_option( self::OPT_KEY, $opts );
		/** Let plugins react to a Suite-owned toggle. */
		do_action( 'mersal_suite_toggled', $key, $opts[ $key ] );
		wp_send_json_success( array( 'key' => $key, 'on' => $opts[ $key ] ) );
	}

	/**
	 * Persist Suite-level general settings (sender identity etc.).
	 */
	public function ajax_save() {
		check_ajax_referer( 'mersal_suite_save', 'nonce' );
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified above.
		$raw  = isset( $_POST['fields'] ) && is_array( $_POST['fields'] ) ? wp_unslash( $_POST['fields'] ) : array();
		$opts = $this->settings();
		foreach ( $raw as $k => $v ) {
			$opts[ sanitize_key( $k ) ] = sanitize_text_field( $v );
		}
		update_option( self::OPT_KEY, $opts );
		do_action( 'mersal_suite_settings_saved', $opts );
		wp_send_json_success( $opts );
	}

	/**
	 * Read Suite-level settings.
	 *
	 * @return array
	 */
	public function settings() {
		$o = get_option( self::OPT_KEY, array() );
		return is_array( $o ) ? $o : array();
	}

	/**
	 * Read a Suite toggle/option with a default.
	 *
	 * @param string $key Option key.
	 * @param mixed  $def Default.
	 * @return mixed
	 */
	public function opt( $key, $def = false ) {
		$o = $this->settings();
		return array_key_exists( $key, $o ) ? $o[ $key ] : $def;
	}

	/* ------------------------------------------------------------------ */
	/* Render                                                             */
	/* ------------------------------------------------------------------ */

	/**
	 * Render the whole unified admin app.
	 */
	public function render() {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'You are not allowed to access this page.', 'mersal-notifications' ) );
		}
		$ar    = 'ar' === $this->lang();
		$dir   = $ar ? 'rtl' : 'ltr';
		$dark  = $this->is_dark() ? 'true' : 'false';
		$route = $this->route();
		$cur   = $this->current_plugin( $route );

		// Inline the design tokens (bulletproof against asset blockers).
		echo "\n<style id=\"mersal-suite-css\">\n" . $this->asset( 'assets/suite.css' ) . "\n</style>\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

		$cfg = array(
			'ajax'         => admin_url( 'admin-ajax.php' ),
			'prefNonce'    => wp_create_nonce( 'mersal_suite_pref' ),
			'saveNonce'    => wp_create_nonce( 'mersal_suite_save' ),
			'installNonce' => wp_create_nonce( 'mersal_suite_install' ),
			'lang'         => $this->lang(),
			'dark'         => $this->is_dark(),
		);
		echo '<script>window.MERSAL_SUITE=' . wp_json_encode( $cfg ) . ';</script>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

		echo '<div class="mersal-suite-app" data-dark="' . esc_attr( $dark ) . '" dir="' . esc_attr( $dir ) . '">';
		$this->sidebar( $route, $cur );
		echo '<div style="display:flex; flex-direction:column; min-width:0;">';
		$this->topbar( $route, $cur );
		echo '<main style="flex:1; padding:24px 28px 56px; min-width:0;"><div style="max-width:1180px; margin:0 auto;">';
		$this->announcement_banner();
		$this->dispatch( $route, $cur );
		echo '</div></main>';
		echo '</div>'; // main col
		echo '</div>'; // app

		echo "\n<script id=\"mersal-suite-js\">\n" . $this->asset( 'assets/suite.js' ) . "\n</script>\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Dispatch a route to its renderer.
	 *
	 * @param string $route Route id.
	 * @param string $cur   Current plugin id.
	 */
	private function dispatch( $route, $cur ) {
		$system = array( 'updates', 'integrations', 'settings', 'billing' );
		if ( 'hub' === $route ) {
			$this->page_hub();
			return;
		}
		if ( in_array( $route, $system, true ) ) {
			$this->system_page( $route );
			return;
		}
		// Plugin-owned route.
		if ( isset( $this->plugins[ $cur ]['routes'][ $route ] ) && is_callable( $this->plugins[ $cur ]['routes'][ $route ] ) ) {
			call_user_func( $this->plugins[ $cur ]['routes'][ $route ], $this );
			return;
		}
		$this->page_placeholder( $route );
	}
}
