<?php

declare(strict_types=1);

namespace SiteFort\Admin;

use SiteFort\Includes\Capabilities;
use SiteFort\Modules\Firewall\FirewallConfigService;
use SiteFort\Modules\Hardening\Hardening;

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

/**
 * Hosts the wp-admin menu shell and React admin application.
 */
class Admin {

	/**
	 * Plugin slug used for handles and page IDs.
	 */
	private string $plugin_name;

	/**
	 * Current plugin version.
	 */
	private string $version;

	/**
	 * Parsed build metadata for the admin app.
	 *
	 * @var array<string, mixed>|null
	 */
	private ?array $admin_build_meta = null;

	/**
	 * @param string $plugin_name Plugin slug.
	 * @param string $version     Plugin version.
	 */
	public function __construct( string $plugin_name, string $version ) {
		$this->plugin_name = $plugin_name;
		$this->version     = $version;
	}

	/**
	 * Load the admin build metadata when available.
	 *
	 * @return array<string, mixed>
	 */
	private function get_admin_build_meta(): array {
		if ( $this->admin_build_meta !== null ) {
			return $this->admin_build_meta;
		}

		$build_meta_path = SITEFORT_PLUGIN_DIR . 'assets/admin-app/build-meta.json';
		if ( ! file_exists( $build_meta_path ) ) {
			$this->admin_build_meta = [];
			return $this->admin_build_meta;
		}

		$build_meta_raw = file_get_contents( $build_meta_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- trusted local build artifact
		if ( ! is_string( $build_meta_raw ) || $build_meta_raw === '' ) {
			$this->admin_build_meta = [];
			return $this->admin_build_meta;
		}

		$decoded = json_decode( $build_meta_raw, true );
		$this->admin_build_meta = is_array( $decoded ) ? $decoded : [];

		return $this->admin_build_meta;
	}

	/**
	 * Resolve the raw admin page slug from the request.
	 *
	 * @return string
	 */
	private function get_raw_admin_page_slug(): string {
		$page = filter_input(
			INPUT_GET,
			'page',
			FILTER_CALLBACK,
			[
				'options' => 'sanitize_key',
				'flags'   => FILTER_REQUIRE_SCALAR,
			]
		);

		if ( ! is_string( $page ) || $page === '' ) {
			return '';
		}

		return sanitize_key( $page );
	}

	/**
	 * Resolve the current SiteFort admin page slug.
	 *
	 * @return string
	 */
	private function get_current_admin_page_slug(): string {
		return $this->get_raw_admin_page_slug();
	}

	/**
	 * Determine whether the current request is for a SiteFort admin screen.
	 *
	 * @return bool
	 */
	private function is_sitefort_admin_page(): bool {
		$current_page = $this->get_current_admin_page_slug();

		return $current_page === $this->plugin_name || str_starts_with( $current_page, $this->plugin_name . '-' );
	}

	/**
	 * Enqueue assets that are needed across wp-admin while the SiteFort menu exists.
	 *
	 * @param string $hook_suffix Current admin page hook.
	 */
	public function enqueue_global_admin_assets( string $hook_suffix ): void {
		unset( $hook_suffix );

		$style_handle      = $this->plugin_name . '-admin-global';
		$global_style_path = SITEFORT_PLUGIN_DIR . 'assets/admin-global.css';
		wp_enqueue_style(
			$style_handle,
			SITEFORT_PLUGIN_URL . 'assets/admin-global.css',
			[],
			file_exists( $global_style_path ) ? (string) filemtime( $global_style_path ) : $this->version
		);
		wp_add_inline_style(
			$style_handle,
			sprintf(
				':root{--sitefort-admin-menu-dark-icon:url("%s");}',
				esc_url( SITEFORT_PLUGIN_URL . 'assets/images/icon.svg' )
			)
		);
		wp_enqueue_script(
			$this->plugin_name . '-admin-global',
			SITEFORT_PLUGIN_URL . 'assets/admin-global.js',
			[],
			$this->version,
			true
		);
	}

	/**
	 * Resolve the current admin page to a page entry key.
	 *
	 * @param array<string, mixed> $build_meta The parsed build metadata.
	 * @param string               $current_page The current SiteFort page slug.
	 * @return string
	 */
	private function resolve_admin_entry_key( array $build_meta, string $current_page ): string {
		$default_entry = isset( $build_meta['defaultEntry'] ) && is_string( $build_meta['defaultEntry'] )
			? $build_meta['defaultEntry']
			: 'dashboard';
		$network_entry = isset( $build_meta['networkEntry'] ) && is_string( $build_meta['networkEntry'] )
			? $build_meta['networkEntry']
			: $default_entry;
		$page_map      = isset( $build_meta['pageMap'] ) && is_array( $build_meta['pageMap'] )
			? $build_meta['pageMap']
			: [];

		if ( is_network_admin() ) {
			return $network_entry;
		}

		if ( isset( $page_map[ $current_page ] ) && is_string( $page_map[ $current_page ] ) ) {
			return $page_map[ $current_page ];
		}

		return $default_entry;
	}

	/**
	 * Resolve the current admin app entry bundle and CSS asset.
	 *
	 * @return array{script: string|null, style: string|null, version: string}
	 */
	private function get_admin_app_assets(): array {
		$build_meta   = $this->get_admin_build_meta();
		$current_page = $this->get_current_admin_page_slug();
		$entry_key    = $this->resolve_admin_entry_key( $build_meta, $current_page );
		$entries      = isset( $build_meta['entries'] ) && is_array( $build_meta['entries'] ) ? $build_meta['entries'] : [];
		$script_file  = isset( $entries[ $entry_key ] ) && is_string( $entries[ $entry_key ] ) ? $entries[ $entry_key ] : null;
		$style_file   = isset( $build_meta['style'] ) && is_string( $build_meta['style'] ) ? $build_meta['style'] : null;
		$build_ver    = isset( $build_meta['buildVersion'] ) && is_scalar( $build_meta['buildVersion'] )
			? (string) $build_meta['buildVersion']
			: $this->version;
		$build_meta_path = SITEFORT_PLUGIN_DIR . 'assets/admin-app/build-meta.json';
		$build_stamp     = file_exists( $build_meta_path ) ? (int) filemtime( $build_meta_path ) : 0;
		$asset_version   = $build_stamp > 0 ? $build_ver . '.' . $build_stamp : $build_ver;

		return [
			'script'  => $script_file,
			'style'   => $style_file,
			'version' => $asset_version,
		];
	}

	/**
	 * Register admin app styles.
	 *
	 * @param string $hook_suffix Current admin page hook.
	 */
	public function enqueue_styles( string $hook_suffix ): void {
		if ( strpos( $hook_suffix, $this->plugin_name ) === false ) {
			return;
		}

		$admin_assets = $this->get_admin_app_assets();
		$style_file   = $admin_assets['style'];
		$build_ver    = $admin_assets['version'];

		if ( is_string( $style_file ) && $style_file !== '' ) {
			$style_path = SITEFORT_PLUGIN_DIR . 'assets/admin-app/' . ltrim( $style_file, '/' );
			if ( file_exists( $style_path ) ) {
				wp_enqueue_style(
					$this->plugin_name . '-admin-styles',
					SITEFORT_PLUGIN_URL . 'assets/admin-app/' . ltrim( $style_file, '/' ),
					array(),
					$build_ver
				);
			}
		}

		$admin_page_style_path = SITEFORT_PLUGIN_DIR . 'assets/admin-page.css';
		wp_enqueue_style(
			$this->plugin_name . '-admin-page',
			SITEFORT_PLUGIN_URL . 'assets/admin-page.css',
			[],
			file_exists( $admin_page_style_path ) ? (string) filemtime( $admin_page_style_path ) : $this->version
		);
	}

	/**
	 * Register admin app scripts.
	 *
	 * @param string $hook_suffix Current admin page hook.
	 */
	public function enqueue_scripts( string $hook_suffix ): void {
		if ( strpos( $hook_suffix, $this->plugin_name ) === false ) {
			return;
		}

		$admin_page_script_path = SITEFORT_PLUGIN_DIR . 'assets/admin-page.js';
		wp_enqueue_script(
			$this->plugin_name . '-admin-page',
			SITEFORT_PLUGIN_URL . 'assets/admin-page.js',
			[],
			file_exists( $admin_page_script_path ) ? (string) filemtime( $admin_page_script_path ) : $this->version,
			false
		);

		$admin_assets = $this->get_admin_app_assets();
		$script_file  = $admin_assets['script'];

		if ( ! is_string( $script_file ) || $script_file === '' ) {
			return;
		}

		$script_path = SITEFORT_PLUGIN_DIR . 'assets/admin-app/' . ltrim( $script_file, '/' );
		$script_url  = SITEFORT_PLUGIN_URL . 'assets/admin-app/' . ltrim( $script_file, '/' );
		$build_ver   = $admin_assets['version'];

		if ( ! file_exists( $script_path ) ) {
			return;
		}

		// Ensure WP dependencies are available before the module loads.
		wp_enqueue_script( 'wp-i18n' );
		wp_enqueue_script( 'wp-api-fetch' );

		// Register the page entry module with a build version.  The emitted
		// module graph uses the same version in all relative imports, so the
		// browser resolves one coherent build while WordPress retains cache
		// control over the entry URL.
		wp_register_script(
			$this->plugin_name . '-admin-script',
			$script_url,
			array( 'wp-i18n', 'wp-api-fetch' ),
			$build_ver,
			true
		);
		wp_enqueue_script( $this->plugin_name . '-admin-script' );

		$current_page  = $this->get_current_admin_page_slug();
		$localize_data = [
			'apiUrl'          => rest_url( SITEFORT_REST_NAMESPACE . '/' ),
			'apiRoot'         => rest_url(),
			'nonce'           => wp_create_nonce( 'wp_rest' ),
			'secure_nonce'    => wp_create_nonce( 'sitefort_nonce' ),
			'pluginName'      => $this->plugin_name,
			'version'         => $this->version,
			'assetsUrl'       => SITEFORT_PLUGIN_URL . 'assets/admin-app/',
			'adminUrl'        => admin_url(),
			'ajaxUrl'         => admin_url( 'admin-ajax.php' ),
			'networkAdminUrl' => is_network_admin() ? network_admin_url() : admin_url(),
			'isNetworkAdmin'  => is_network_admin(),
			'currentPage'     => $current_page,
			'branding'        => $this->get_branding_data(),
		];

		// Call get_all() once; license and key-health checks need the same cloud snapshot.
		$cloud_all = \SiteFort\Modules\Cloud\Service\CloudSettings::get_all();
		$key_error = $cloud_all['_key_error'] ?? null;
		if ( ! $key_error ) {
			try {
				\SiteFort\Includes\MasterKey::get();
			} catch ( \RuntimeException $e ) {
				$key_error = \SiteFort\Includes\MasterKey::get_last_error();
			}
		}

		if ( ! $key_error ) {
			$key_error = \SiteFort\Includes\SecretManager::detect_store_key_issue();
		}

		if ( $key_error ) {
			$key_path = is_array( $key_error ) && ! empty( $key_error['path'] )
				? (string) $key_error['path']
				: \SiteFort\Includes\MasterKey::get_key_file_path();
			$key_dir                       = dirname( $key_path );
			$writable_probe                = is_dir( $key_dir ) ? $key_dir : dirname( $key_dir );
			$key_error['key_source']       = \SiteFort\Includes\MasterKey::get_source();
			$key_error['storage_writable'] = (
				\SiteFort\Includes\MasterKey::get_source() !== 'constant'
				&& $writable_probe !== ''
				&& wp_is_writable( $writable_probe )
			);
			$localize_data['keyError']     = $key_error;
		}

		// Console restore uses site_uuid first, with license_key as the recovery path.
		$raw_cloud    = \SiteFort\Modules\Cloud\Service\CloudSettings::get_raw();
		$has_identity = ( $raw_cloud['site_uuid'] ?? '' ) !== ''
			|| ( $raw_cloud['license_key'] ?? '' ) !== ''
			|| \SiteFort\Includes\SettingsStore::get_field( \SiteFort\Includes\SettingsStore::COL_STATE, 'last_license_key', '' ) !== '';
		$localize_data['keyBackupAvailable'] = $has_identity
			&& \SiteFort\Includes\SettingsStore::get_field( \SiteFort\Includes\SettingsStore::COL_STATE, 'key_backup_available', '' ) === '1';

		$fw_settings             = FirewallConfigService::load_effective_settings();
		$harden_settings         = \SiteFort\Includes\SettingsStore::get( \SiteFort\Includes\SettingsStore::COL_HARDENING );
		$firewall_enabled        = ! empty( $fw_settings['enabled'] );
		$firewall_origin_mode    = FirewallConfigService::get_origin_enforcement_mode( $fw_settings );
		$recommended_hardening   = Hardening::get_recommended_hardening_keys();
		$hardening_count         = is_array( $harden_settings )
			? Hardening::count_effective_hardening_controls( $harden_settings, $recommended_hardening )
			: 0;
		$cloud_has_creds         = ! empty( $cloud_all['site_token'] ) && ! empty( $cloud_all['site_secret'] );
		$effective_license_status = (string) ( $cloud_all['license_status'] ?? '' );
		if ( $effective_license_status === 'active' && ! $cloud_has_creds ) {
			$effective_license_status = '';
		}

		$localize_data['setupStatus'] = [
			'connected'          => $cloud_has_creds && $effective_license_status !== '',
			'plan'               => (string) ( $cloud_all['license_plan'] ?? 'free' ),
			'firewall'           => $firewall_origin_mode === FirewallConfigService::MODE_ENFORCE,
			'firewallEnabled'    => $firewall_enabled,
			'firewallOriginMode' => $firewall_origin_mode,
			'hardening'          => $hardening_count >= Hardening::get_recommended_hardening_threshold(),
			'hardeningCount'     => $hardening_count,
			'loginProtection'    => ! empty( $harden_settings['limit_login_attempts_enabled'] ),
			'scanned'            => (int) \SiteFort\Includes\SettingsStore::get_field( \SiteFort\Includes\SettingsStore::COL_STATE, 'scans_completed', 0 ) > 0,
			'dismissed'          => \SiteFort\Includes\SettingsStore::get_field( \SiteFort\Includes\SettingsStore::COL_STATE, 'setup_dismissed', '' ) === '1',
		];
		$localize_data['licenseStatus'] = $effective_license_status;
		$localize_data['licensePlan']   = (string) ( $cloud_all['license_plan'] ?? 'free' );
		$localize_data['siteMigration'] = [
			'status'         => sanitize_text_field( (string) ( $cloud_all['migration_status'] ?? 'none' ) ),
			'oldUrl'         => esc_url_raw( (string) ( $cloud_all['linked_record_url'] ?? '' ) ),
			'pendingUrl'     => esc_url_raw( (string) ( $cloud_all['migration_pending_url'] ?? '' ) ),
			'replaceAllowed' => (bool) ( $cloud_all['migration_replace_allowed'] ?? true ),
			'replaceMessage' => sanitize_text_field( (string) ( $cloud_all['migration_replace_message'] ?? '' ) ),
		];

		// Plugin screens suppress WordPress admin notices, so the plan suspension notice is
		// drawn by the app instead. Same decision and copy, different presentation.
		$plan_notice_active = class_exists( '\\SiteFort\\Modules\\Cloud\\Service\\PlanSuspensionNotice' )
			&& \SiteFort\Modules\Cloud\Service\PlanSuspensionNotice::is_active();
		$plan_notice = [ 'visible' => $plan_notice_active ];
		if ( $plan_notice_active ) {
			$copy = \SiteFort\Modules\Cloud\Service\PlanSuspensionNotice::get_copy();
			$plan_notice['title']  = sanitize_text_field( $copy['title'] );
			$plan_notice['body']   = sanitize_text_field( $copy['body'] );
			$plan_notice['action'] = sanitize_text_field( $copy['action'] );
			$plan_notice['url']    = esc_url_raw( $copy['url'] );
		}
		$localize_data['planNotice'] = $plan_notice;

		$localize_data['consoleGate'] = [
			'pending' => class_exists( '\\SiteFort\\Modules\\Cloud\\Service\\ConsoleGateNotice' )
				&& \SiteFort\Modules\Cloud\Service\ConsoleGateNotice::pending_notice_is_active(),
		];

		wp_localize_script( $this->plugin_name . '-admin-script', 'sitefortData', $localize_data );
		wp_set_script_translations( $this->plugin_name . '-admin-script', 'sitefort', SITEFORT_PLUGIN_DIR . 'languages' );
	}

	/**
	 * Add a body class for SiteFort admin pages.
	 *
	 * @param string $classes Existing admin body classes.
	 * @return string
	 */
	public function add_admin_body_class( string $classes ): string {
		if ( ! $this->is_sitefort_admin_page() ) {
			return $classes;
		}

		return trim( $classes . ' sitefort-admin-screen' );
	}

	/**
	 * Replace the enqueued script tag with a module script.
	 *
	 * The admin build appends the same build version to internal imports, so
	 * the entry URL can retain WordPress versioning safely.
	 *
	 * @param string $tag    Script tag.
	 * @param string $handle Script handle.
	 * @param string $src    Script URL.
	 * @return string Modified script tag.
	 */
	public function add_type_attribute( string $tag, string $handle, string $src ): string {
		if ( $handle !== $this->plugin_name . '-admin-script' ) {
			return $tag;
		}

		unset( $src );

		if ( strpos( $tag, ' type=' ) !== false ) {
			return $tag;
		}

		return str_replace( '<' . 'script ', '<' . 'script type="module" ', $tag );
	}

	/**
	 * Add the admin menu and submenus.
	 */
	public function add_admin_menu(): void {
		$cloud_all       = \SiteFort\Modules\Cloud\Service\CloudSettings::get_all();
		$cloud_has_creds = ! empty( $cloud_all['site_token'] ) && ! empty( $cloud_all['site_secret'] );
		$license_status  = (string) ( $cloud_all['license_status'] ?? '' );
		if ( $license_status === 'active' && ! $cloud_has_creds ) {
			$license_status = '';
		}

		$setup_dismissed = \SiteFort\Includes\SettingsStore::get_field(
			\SiteFort\Includes\SettingsStore::COL_STATE,
			'setup_dismissed',
			''
		) === '1';
		$wizard_done     = $setup_dismissed || ( $cloud_has_creds && $license_status !== '' );

		$icon_path         = SITEFORT_PLUGIN_DIR . 'assets/images/icon-light.svg';
		$icon_contents     = file_exists( $icon_path ) ? file_get_contents( $icon_path ) : false; // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- local trusted path
		$icon              = ( $icon_contents !== false && $icon_contents !== '' )
			? 'data:image/svg+xml;base64,' . base64_encode( $icon_contents ) // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
			: 'dashicons-shield-alt';
		$plugin_capability = Capabilities::admin_capability();

		add_menu_page(
			esc_html__( 'SiteFort Dashboard', 'sitefort' ),
			esc_html__( 'SiteFort', 'sitefort' ),
			$plugin_capability,
			$this->plugin_name,
			array( $this, 'display_react_admin_page' ),
			$icon,
			81
		);

		$dashboard_label = $wizard_done
			? esc_html__( 'Dashboard', 'sitefort' )
			: esc_html__( 'Setup', 'sitefort' );

		add_submenu_page(
			$this->plugin_name,
			esc_html__( 'SiteFort Dashboard', 'sitefort' ),
			$dashboard_label,
			$plugin_capability,
			$this->plugin_name,
			array( $this, 'display_react_admin_page' )
		);

		add_submenu_page(
			$this->plugin_name,
			esc_html__( 'Scanner', 'sitefort' ),
			esc_html__( 'Scanner', 'sitefort' ),
			$plugin_capability,
			$this->plugin_name . '-scanner',
			array( $this, 'display_react_admin_page' )
		);

		add_submenu_page(
			$this->plugin_name,
			esc_html__( 'Hardening', 'sitefort' ),
			esc_html__( 'Hardening', 'sitefort' ),
			$plugin_capability,
			$this->plugin_name . '-hardening',
			array( $this, 'display_react_admin_page' )
		);

		add_submenu_page(
			$this->plugin_name,
			esc_html__( 'Login Security', 'sitefort' ),
			esc_html__( 'Login Security', 'sitefort' ),
			$plugin_capability,
			$this->plugin_name . '-login-security',
			array( $this, 'display_react_admin_page' )
		);

		add_submenu_page(
			$this->plugin_name,
			esc_html__( 'Firewall', 'sitefort' ),
			esc_html__( 'Firewall', 'sitefort' ),
			$plugin_capability,
			$this->plugin_name . '-firewall',
			array( $this, 'display_react_admin_page' )
		);

		add_submenu_page(
			$this->plugin_name,
			esc_html__( 'Vulnerabilities', 'sitefort' ),
			esc_html__( 'Vulnerabilities', 'sitefort' ),
			$plugin_capability,
			$this->plugin_name . '-vulnerability',
			array( $this, 'display_react_admin_page' )
		);

		add_submenu_page(
			$this->plugin_name,
			esc_html__( 'Settings', 'sitefort' ),
			esc_html__( 'Settings', 'sitefort' ),
			$plugin_capability,
			$this->plugin_name . '-settings',
			array( $this, 'display_react_admin_page' )
		);

		add_submenu_page(
			$this->plugin_name,
			esc_html__( 'Security Tools', 'sitefort' ),
			esc_html__( 'Tools', 'sitefort' ),
			$plugin_capability,
			$this->plugin_name . '-tools',
			array( $this, 'display_react_admin_page' )
		);

		add_submenu_page(
			$this->plugin_name,
			esc_html__( 'Audit Log', 'sitefort' ),
			esc_html__( 'Audit Log', 'sitefort' ),
			$plugin_capability,
			$this->plugin_name . '-audit-log',
			array( $this, 'display_react_admin_page' )
		);

		$license_ok = $cloud_has_creds && in_array( $license_status, array( 'active', 'valid' ), true );
		if ( ! $license_ok ) {
			add_submenu_page(
				$this->plugin_name,
				esc_html__( 'License & Plan', 'sitefort' ),
				esc_html__( 'License & Plan', 'sitefort' ),
				$plugin_capability,
				$this->plugin_name . '-settings&tab=license',
				array( $this, 'display_react_admin_page' )
			);
		}

		$license_plan = (string) ( $cloud_all['license_plan'] ?? 'free' );
		$is_free_plan = ( $license_plan === 'free' );
		if ( $wizard_done && $is_free_plan && current_user_can( $plugin_capability ) ) {
			global $submenu;

			$submenu[ $this->plugin_name ][] = array(
				'<span class="sitefort-upgrade-nav-link"><span class="dashicons dashicons-star-filled sitefort-upgrade-icon"></span>' . esc_html__( 'Upgrade to Pro', 'sitefort' ) . '<span class="dashicons dashicons-external sitefort-upgrade-ext"></span></span>',
				$plugin_capability,
				'https://securewp.net/pricing/',
				esc_html__( 'Upgrade to Pro', 'sitefort' ),
			);
		}
	}

	/**
	 * Display the admin page container for the React application.
	 */
	public function display_react_admin_page(): void {
		$branding = class_exists( '\\SiteFort\\Modules\\WhiteLabel\\WhiteLabel' )
			? \SiteFort\Modules\WhiteLabel\WhiteLabel::get_active_branding()
			: [];
		$name = ! empty( $branding['plugin_name'] ) ? sanitize_text_field( (string) $branding['plugin_name'] ) : 'SiteFort';
		$loading_label = sprintf(
			/* translators: %s: plugin name shown while the admin app is loading. */
			__( 'Loading %s...', 'sitefort' ),
			$name
		);

		echo '<div class="wrap"><div id="sitefort-admin-app">' .
			'<div class="sitefort-admin-loading">' .
				'<div class="sitefort-admin-loading-inner">' .
					'<svg class="sitefort-admin-spinner" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true">' .
						'<circle opacity="0.25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>' .
						'<path opacity="0.75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>' .
					'</svg>' .
					'<div class="sitefort-admin-loading-label">' . esc_html( $loading_label ) . '</div>' .
				'</div>' .
			'</div>' .
		'</div></div>';
	}

	/**
	 * Resolve setup notice data, or null when the notice should not render.
	 *
	 * @return array{plugin_name:string,setup_url:string,nonce:string,ajax_url:string}|null
	 */
	private function get_setup_notice_context(): ?array {
		if ( ! Capabilities::can_manage_plugin() ) {
			return null;
		}

		if ( $this->is_sitefort_admin_page() ) {
			return null;
		}

		$cloud_all       = \SiteFort\Modules\Cloud\Service\CloudSettings::get_all();
		$cloud_has_creds = ! empty( $cloud_all['site_token'] ) && ! empty( $cloud_all['site_secret'] );
		$license_status  = (string) ( $cloud_all['license_status'] ?? '' );
		if ( $license_status === 'active' && ! $cloud_has_creds ) {
			$license_status = '';
		}

		$setup_dismissed = \SiteFort\Includes\SettingsStore::get_field(
			\SiteFort\Includes\SettingsStore::COL_STATE,
			'setup_dismissed',
			''
		) === '1';
		$wizard_done     = $setup_dismissed || ( $cloud_has_creds && $license_status !== '' );
		if ( $wizard_done ) {
			return null;
		}

		$user_id = get_current_user_id();
		if ( get_user_meta( $user_id, 'sitefort_setup_notice_dismissed', true ) === '1' ) {
			return null;
		}

		$branding = class_exists( '\\SiteFort\\Modules\\WhiteLabel\\WhiteLabel' )
			? \SiteFort\Modules\WhiteLabel\WhiteLabel::get_active_branding()
			: [];

		return [
			'plugin_name' => ! empty( $branding['plugin_name'] ) ? sanitize_text_field( (string) $branding['plugin_name'] ) : 'SiteFort',
			'setup_url'   => admin_url( 'admin.php?page=' . $this->plugin_name ),
			'nonce'       => wp_create_nonce( 'sitefort_dismiss_notice' ),
			'ajax_url'    => admin_url( 'admin-ajax.php' ),
		];
	}

	/**
	 * Render the setup notice banner on all admin pages when setup is incomplete.
	 *
	 * Shown to plugin administrators on non-SiteFort pages until the site is
	 * connected and licensed, or the user dismisses it.
	 */
	public function render_setup_notice(): void {
		$notice_context = $this->get_setup_notice_context();
		if ( $notice_context === null ) {
			return;
		}

		$plugin_name = $notice_context['plugin_name'];
		$setup_url   = $notice_context['setup_url'];
		$nonce       = $notice_context['nonce'];
		$ajax_url    = $notice_context['ajax_url'];
		?>
		<div id="sitefort-setup-notice"
			class="sitefort-setup-notice"
			data-nonce="<?php echo esc_attr( $nonce ); ?>"
			data-ajax-url="<?php echo esc_url( $ajax_url ); ?>">

		<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
			class="sitefort-setup-notice-icon" aria-hidden="true">
			<path fill-rule="evenodd" d="M12 1.5a5.25 5.25 0 0 0-5.25 5.25v3a3 3 0 0 0-3 3v6.75a3 3 0 0 0 3 3h10.5a3 3 0 0 0 3-3v-6.75a3 3 0 0 0-3-3v-3c0-2.9-2.35-5.25-5.25-5.25Zm3.75 8.25v-3a3.75 3.75 0 1 0-7.5 0v3h7.5Z" clip-rule="evenodd" />
		</svg>

		<div class="sitefort-setup-notice-body">
			<span class="sitefort-setup-notice-title"><?php echo esc_html( $plugin_name ); ?> <?php esc_html_e( 'is not yet configured', 'sitefort' ); ?></span>
			<span class="sitefort-setup-notice-copy"><?php esc_html_e( 'Complete the setup to activate firewall protection, security hardening, and malware scanning.', 'sitefort' ); ?></span>
		</div>

		<a href="<?php echo esc_url( $setup_url ); ?>"
			class="sitefort-setup-notice-action">
			<?php esc_html_e( 'Complete Setup', 'sitefort' ); ?>
		</a>

		<button id="sitefort-dismiss-notice"
			aria-label="<?php esc_attr_e( 'Dismiss', 'sitefort' ); ?>"
			class="sitefort-setup-notice-dismiss"
			data-sitefort-dismiss-notice>
			<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="sitefort-setup-notice-dismiss-icon" aria-hidden="true">
				<path d="M6.28 5.22a.75.75 0 0 0-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 1 0 1.06 1.06L10 11.06l3.72 3.72a.75.75 0 1 0 1.06-1.06L11.06 10l3.72-3.72a.75.75 0 0 0-1.06-1.06L10 8.94 6.28 5.22Z" />
			</svg>
		</button>
	</div>
	<?php
	}

	/**
	 * AJAX handler: dismiss the setup notice for the current user.
	 */
	public function dismiss_setup_notice_ajax(): void {
		if ( ! check_ajax_referer( 'sitefort_dismiss_notice', 'nonce', false ) ) {
			wp_die( -1 );
		}
		if ( ! Capabilities::can_manage_plugin() ) {
			wp_die( -1 );
		}
		update_user_meta( get_current_user_id(), 'sitefort_setup_notice_dismissed', '1' );
		wp_send_json_success();
	}

	/**
	 * Get sanitized branding data for the React frontend.
	 *
	 * @return array<string,mixed>
	 */
	private function get_branding_data(): array {
		$branding = class_exists( '\\SiteFort\\Modules\\WhiteLabel\\WhiteLabel' )
			? \SiteFort\Modules\WhiteLabel\WhiteLabel::get_active_branding()
			: [];
		if ( empty( $branding ) ) {
			return [];
		}

		return [
			'pluginName'      => sanitize_text_field( (string) ( $branding['plugin_name'] ?? '' ) ),
			'companyName'     => sanitize_text_field( (string) ( $branding['company_name'] ?? '' ) ),
			'logoUrl'         => esc_url( $branding['logo_url'] ?? '', [ 'https' ] ),
			'supportUrl'      => esc_url( $branding['support_url'] ?? '', [ 'https' ] ),
			'emailFromName'   => sanitize_text_field( (string) ( $branding['email_from_name'] ?? '' ) ),
			'adminFooterText' => sanitize_text_field( (string) ( $branding['admin_footer_text'] ?? '' ) ),
			'hideAttribution' => ! empty( $branding['hide_attribution'] ),
		];
	}

}
