<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

trait Niwa_AI_Admin_Dashboard {

	public function register_admin_menu() {
		add_menu_page(
			__( 'Main Settings', 'niwa-ai' ),
			__( 'Niwa AI', 'niwa-ai' ),
			'manage_options',
			'niwa-ai',
			array( $this, 'render_admin_page' ),
			'dashicons-format-chat',
			56
		);

		add_submenu_page(
			'niwa-ai',
			__( 'Main Settings', 'niwa-ai' ),
			__( 'Main Settings', 'niwa-ai' ),
			'manage_options',
			'niwa-ai',
			array( $this, 'render_admin_page' ),
			1
		);
	}

	public function normalize_niwa_admin_submenu() {
		global $submenu;

		if ( empty( $submenu['niwa-ai'] ) || ! is_array( $submenu['niwa-ai'] ) ) {
			return;
		}

		$personal_assistant_title = $this->get_personal_assistant_menu_title();
		$order                   = array(
			'niwa-ai'                    => array( 10, __( 'Main Settings', 'niwa-ai' ) ),
			'niwa-ai-ui-settings'        => array( 20, __( 'UI Settings', 'niwa-ai' ) ),
			'niwa-ai-admin-autonomy'     => array( 30, $personal_assistant_title ),
			'niwa-ai-agent-mode'         => array( 40, __( 'Frontend Agent Rules', 'niwa-ai' ) ),
			'niwa-ai-analytics'          => array( 50, __( 'Analytics', 'niwa-ai' ) ),
			'niwa-ai-template-library'   => array( 55, __( 'Template Library', 'niwa-ai' ) ),
			'niwa-ai-code-snippets'      => array( 56, __( 'Code Snippets', 'niwa-ai' ) ),
			'niwa-ai-attention-capture'  => array( 60, __( 'Attention Capture', 'niwa-ai' ) ),
			'niwa-ai-conversion-busters' => array( 70, __( 'Conversion Busters', 'niwa-ai' ) ),
			'niwa-ai-leave-stoper'       => array( 80, __( 'Leave Stoper', 'niwa-ai' ) ),
			'niwa-ai-lead-capture'       => array( 90, __( 'Lead Capture', 'niwa-ai' ) ),
			'niwa-ai-history'            => array( 92, __( 'History', 'niwa-ai' ) ),
			'niwa-ai-cart-recovery'      => array( 95, __( 'Cart Recovery', 'niwa-ai' ) ),
			'niwa-ai-bloging'            => array( 100, __( 'Bloging', 'niwa-ai' ) ),
			'niwa-ai-mcp'                => array( 110, __( 'Niwa MCP', 'niwa-ai' ) ),
		);

		$items = array_values( $submenu['niwa-ai'] );
		foreach ( $items as $index => &$item ) {
			$slug = isset( $item[2] ) ? (string) $item[2] : '';

			if ( isset( $order[ $slug ] ) ) {
				$item['_niwa_order'] = $order[ $slug ][0];
				$item[0]            = $order[ $slug ][1];
				if ( isset( $item[3] ) ) {
					$item[3] = $order[ $slug ][1];
				}
			} elseif ( $this->is_niwa_account_menu_item( $item ) ) {
				$item['_niwa_order'] = 120;
				$item[0]            = __( 'Account', 'niwa-ai' );
				if ( isset( $item[3] ) ) {
					$item[3] = __( 'Account', 'niwa-ai' );
				}
			} else {
				$item['_niwa_order'] = 1000 + $index;
			}

			$item['_niwa_original_order'] = $index;
		}
		unset( $item );

		usort( $items, static function( $a, $b ) {
			$a_order = isset( $a['_niwa_order'] ) ? (int) $a['_niwa_order'] : 1000;
			$b_order = isset( $b['_niwa_order'] ) ? (int) $b['_niwa_order'] : 1000;

			if ( $a_order === $b_order ) {
				$a_original = isset( $a['_niwa_original_order'] ) ? (int) $a['_niwa_original_order'] : 0;
				$b_original = isset( $b['_niwa_original_order'] ) ? (int) $b['_niwa_original_order'] : 0;
				return $a_original <=> $b_original;
			}

			return $a_order <=> $b_order;
		} );

		foreach ( $items as &$item ) {
			unset( $item['_niwa_order'], $item['_niwa_original_order'] );
		}
		unset( $item );

		$submenu['niwa-ai'] = $items;
	}

	private function get_personal_assistant_menu_title() {
		$profile = $this->get_admin_autonomy_identity_profile();
		$name    = class_exists( 'Niwa_AI_Identity_Name' ) && isset( $profile['assistant_name'] )
			? Niwa_AI_Identity_Name::normalize( $profile['assistant_name'] )
			: '';

		return '' !== $name ? $name : __( 'Personal Assistant', 'niwa-ai' );
	}

	private function is_niwa_account_menu_item( $item ) {
		$slug  = isset( $item[2] ) ? strtolower( (string) $item[2] ) : '';
		$label = isset( $item[0] ) ? strtolower( wp_strip_all_tags( (string) $item[0] ) ) : '';

		return false !== strpos( $slug, 'account' ) || 'account' === $label;
	}

	/**
	 * Enqueue admin CSS on all Niwa AI pages.
	 *
	 * @param string $hook_suffix Current admin page hook.
	 */
	public function enqueue_niwa_admin_assets( $hook_suffix ) {
		$load_dashboard_assets = false !== strpos( $hook_suffix, 'niwa-ai' );
		$load_admin_launcher   = $this->should_load_admin_autonomy_launcher( $hook_suffix );

		if ( ! $load_dashboard_assets && ! $load_admin_launcher ) {
			return;
		}

		if ( $load_dashboard_assets ) {
			wp_enqueue_media();
		}

		if ( $load_admin_launcher && $this->asset_loader && method_exists( $this->asset_loader, 'enqueue_admin_launcher_assets' ) ) {
			$this->asset_loader->enqueue_admin_launcher_assets( $this->build_admin_autonomy_launcher_config() );
		}

		$admin_css_path    = NIWA_AI_DIR . 'assets/css/niwa-admin.css';
		$admin_css_version = file_exists( $admin_css_path ) ? (string) filemtime( $admin_css_path ) : NIWA_AI_VERSION;
		wp_enqueue_style(
			'niwa-admin',
			NIWA_AI_URL . 'assets/css/niwa-admin.css',
			array(),
			$admin_css_version
		);

		if ( 'niwa-ai_page_niwa-ai-history' === $hook_suffix ) {
			$history_css_path    = NIWA_AI_DIR . 'assets/css/niwa-admin-history.css';
			$history_css_version = file_exists( $history_css_path ) ? (string) filemtime( $history_css_path ) : NIWA_AI_VERSION;
			$history_js_path     = NIWA_AI_DIR . 'assets/js/niwa-admin-history.js';
			$history_js_version  = file_exists( $history_js_path ) ? (string) filemtime( $history_js_path ) : NIWA_AI_VERSION;
			$tokens_css_path     = NIWA_AI_DIR . 'assets/css/niwa-tokens.css';
			$tokens_css_version  = file_exists( $tokens_css_path ) ? (string) filemtime( $tokens_css_path ) : NIWA_AI_VERSION;
			wp_enqueue_style(
				'niwa-tokens',
				NIWA_AI_URL . 'assets/css/niwa-tokens.css',
				array(),
				$tokens_css_version
			);
			wp_enqueue_style(
				'niwa-admin-history',
				NIWA_AI_URL . 'assets/css/niwa-admin-history.css',
				array( 'niwa-admin', 'niwa-tokens' ),
				$history_css_version
			);
			wp_enqueue_script(
				'niwa-admin-history',
				NIWA_AI_URL . 'assets/js/niwa-admin-history.js',
				array(),
				$history_js_version,
				true
			);
			wp_localize_script(
				'niwa-admin-history',
				'niwaHistoryReader',
				array(
					'ajaxUrl' => admin_url( 'admin-ajax.php' ),
					'action'  => 'niwa_ai_history_transcript',
					'nonce'   => wp_create_nonce( 'niwa_ai_history_transcript' ),
					'error'   => __( 'Conversation could not be loaded. Try again.', 'niwa-ai' ),
				)
			);
		}
	}

	public function render_admin_page() {
		$auth_mode   = $this->auth_manager ? $this->auth_manager->get_mode() : 'api_key';
		$agent_model = $this->model_router && method_exists( $this->model_router, 'route_for_founder_agent' )
			? $this->model_router->route_for_founder_agent()
			: ( $this->model_router ? $this->model_router->route_for_agent() : 'gpt-5.4-mini' );
		$frontend_model = $this->model_router && method_exists( $this->model_router, 'route_for_frontend_chat' )
			? $this->model_router->route_for_frontend_chat()
			: ( $this->model_router ? $this->model_router->route_for_agent() : 'gpt-5.4-mini' );

		$auth_opts          = get_option( Niwa_AI_Constants::OPT_AUTH, array() );
		$gen_opts           = get_option( Niwa_AI_Constants::OPT_SETTINGS, array() );
		$oauth_status       = $this->auth_manager ? $this->auth_manager->get_oauth_status() : array();
		$device_oauth_status = $this->auth_manager ? $this->auth_manager->get_codex_device_status( get_current_user_id() ) : array( 'active' => false );
		$api_key_for_models = isset( $auth_opts['api_key'] ) ? (string) $auth_opts['api_key'] : '';
		$chat_model         = isset( $gen_opts['model'] ) ? trim( (string) $gen_opts['model'] ) : '';
		$model_options      = $this->model_catalog ? $this->model_catalog->get_models_for_mode( $auth_mode, $api_key_for_models ) : array();

		if ( $this->model_catalog && '' === $chat_model ) {
			$chat_model = (string) $this->model_catalog->get_default_chat_model( $auth_mode );
		} elseif ( '' === $chat_model ) {
			$chat_model = 'gpt-5.4-mini';
		}

		if ( empty( $model_options ) ) {
			$model_options = array( $chat_model => $chat_model );
		} elseif ( ! isset( $model_options[ $chat_model ] ) ) {
			$model_options[ $chat_model ] = $chat_model;
		}
		// Enqueue settings JS for dynamic mode switching and media library avatar pickers.
		$settings_js_path    = NIWA_AI_DIR . 'assets/js/niwa-admin-settings.js';
		$settings_js_version = file_exists( $settings_js_path ) ? (string) filemtime( $settings_js_path ) : NIWA_AI_VERSION;
		wp_enqueue_script(
			'niwa-admin-settings',
			NIWA_AI_URL . 'assets/js/niwa-admin-settings.js',
			array( 'jquery', 'media-editor', 'media-views' ),
			$settings_js_version,
			array( 'in_footer' => true )
		);
		wp_localize_script( 'niwa-admin-settings', 'niwaAdminSettings', array(
			'assetVersion' => $settings_js_version,
		) );

		$demo_query  = isset( $_GET['niwa_demo_query'] ) ? sanitize_text_field( wp_unslash( $_GET['niwa_demo_query'] ) ) : '';
		$demo_result = array();
		if ( ! empty( $demo_query ) && $this->rlm_engine ) {
			$t0          = microtime( true );
			$demo_result = $this->rlm_engine->answer( $demo_query, 'admin-demo' );
			$this->latency_tracker->record( 'rlm_demo_total', ( microtime( true ) - $t0 ) * 1000, array( 'query' => $demo_query, 'source' => isset( $demo_result['source'] ) ? $demo_result['source'] : '' ) );
		}

		echo '<div class="wrap niwa-wrap">';

		$this->render_dashboard_notices();
		$this->render_dashboard_header();
		$this->render_system_stats( $auth_mode, $agent_model, $frontend_model );
		$this->render_auth_card( $auth_opts, $auth_mode, $oauth_status, $device_oauth_status );
		$this->render_embeddings_card();
		$this->render_general_card( $gen_opts, $chat_model, $model_options, $auth_mode, $api_key_for_models );
		$this->render_progress_card();
		$this->render_rlm_demo_card( $demo_query, $demo_result );

		echo '</div>';
	}

	/* ================================================================
	 * Notices
	 * ============================================================= */

	private function render_dashboard_notices() {
		if ( isset( $_GET['niwa_full_embed'] ) && 'done' === sanitize_text_field( wp_unslash( $_GET['niwa_full_embed'] ) ) ) {
			echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Full index + embeddings started. Progress will update automatically.', 'niwa-ai' ) . '</p></div>';
		}

		if ( isset( $_GET['niwa_oauth'] ) ) {
			$action = sanitize_text_field( wp_unslash( $_GET['niwa_oauth'] ) );
			if ( 'connected' === $action ) {
				echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Codex OAuth connected successfully.', 'niwa-ai' ) . '</p></div>';
			} elseif ( 'device_started' === $action ) {
				echo '<div class="notice notice-info is-dismissible"><p>' . esc_html__( 'Open OpenAI verification page, enter the shown code, and wait for automatic confirmation.', 'niwa-ai' ) . '</p></div>';
			} elseif ( 'disconnected' === $action ) {
				echo '<div class="notice notice-info is-dismissible"><p>' . esc_html__( 'Codex OAuth disconnected.', 'niwa-ai' ) . '</p></div>';
			} elseif ( 'error' === $action ) {
				$msg = isset( $_GET['niwa_oauth_message'] ) ? sanitize_text_field( wp_unslash( $_GET['niwa_oauth_message'] ) ) : 'OAuth failed.';
				echo '<div class="notice notice-error is-dismissible"><p>' . esc_html( $msg ) . '</p></div>';
			}
		}

		if ( isset( $_GET['niwa_ai_blog_migrate_scanned'] ) ) {
			$affected = (int) $_GET['niwa_ai_blog_migrate_scanned'];
			$total    = isset( $_GET['niwa_ai_blog_migrate_total'] ) ? (int) $_GET['niwa_ai_blog_migrate_total'] : 0;
			echo '<div class="notice notice-info is-dismissible"><p>' . sprintf(
				esc_html__( 'Niwa Blog Migrator scan: %1$d od %2$d objava ima legacy CSS/JS leak.', 'niwa-ai' ),
				$affected,
				$total
			) . '</p></div>';
		}
		if ( isset( $_GET['niwa_ai_blog_migrate_done'] ) ) {
			$done    = (int) $_GET['niwa_ai_blog_migrate_done'];
			$skipped = isset( $_GET['niwa_ai_blog_migrate_skipped'] ) ? (int) $_GET['niwa_ai_blog_migrate_skipped'] : 0;
			$failed  = isset( $_GET['niwa_ai_blog_migrate_failed'] ) ? (int) $_GET['niwa_ai_blog_migrate_failed'] : 0;
			echo '<div class="notice notice-success is-dismissible"><p>' . sprintf(
				esc_html__( 'Niwa Blog Migrator: migrirano %1$d, preskočeno %2$d, neuspeh %3$d.', 'niwa-ai' ),
				$done,
				$skipped,
				$failed
			) . '</p></div>';
		}
	}

	/* ================================================================
	 * Page Header
	 * ============================================================= */

	private function render_dashboard_header() {
		echo '<div class="niwa-page-header">';
		echo '<div class="niwa-page-header__icon">';
		echo '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a7 7 0 0 1 7 7c0 2.38-1.19 4.47-3 5.74V17a2 2 0 0 1-2 2H10a2 2 0 0 1-2-2v-2.26C6.19 13.47 5 11.38 5 9a7 7 0 0 1 7-7z"/><line x1="10" y1="22" x2="14" y2="22"/></svg>';
		echo '</div>';
		echo '<div class="niwa-page-header__text">';
		echo '<h1 class="niwa-page-header__title">' . esc_html__( 'Main Settings', 'niwa-ai' ) . '</h1>';
		echo '<p class="niwa-page-header__subtitle">' . esc_html__( 'AI sales assistant dashboard — authentication, models, and knowledge base.', 'niwa-ai' ) . '</p>';
		echo '</div>';
		echo '<div class="niwa-page-header__badge">';
		echo '<span class="niwa-badge niwa-badge--success"><span class="niwa-badge__dot niwa-badge__dot--pulse"></span>' . esc_html__( 'Active', 'niwa-ai' ) . '</span>';
		echo '</div>';
		echo '</div>';
	}

	/* ================================================================
	 * System Stats
	 * ============================================================= */

	private function render_system_stats( $auth_mode, $agent_model, $frontend_model ) {
		echo '<div class="niwa-stats">';

		echo '<div class="niwa-stat niwa-stat--brand">';
		echo '<div class="niwa-stat__label">' . esc_html__( 'Auth Mode', 'niwa-ai' ) . '</div>';
		echo '<div class="niwa-stat__value">' . esc_html( ucfirst( str_replace( '_', ' ', $auth_mode ) ) ) . '</div>';
		echo '</div>';

		echo '<div class="niwa-stat">';
		echo '<div class="niwa-stat__label">' . esc_html__( 'Founder Agent', 'niwa-ai' ) . '</div>';
		echo '<div class="niwa-stat__value">' . esc_html( $agent_model ) . '</div>';
		echo '</div>';

		echo '<div class="niwa-stat">';
		echo '<div class="niwa-stat__label">' . esc_html__( 'Chat Model', 'niwa-ai' ) . '</div>';
		echo '<div class="niwa-stat__value">' . esc_html( $frontend_model ) . '</div>';
		echo '</div>';

		echo '</div>';
	}

	/* ================================================================
	 * Auth Card
	 * ============================================================= */

	private function render_auth_card( $auth_opts, $auth_mode, $oauth_status, $device_oauth_status ) {
		echo '<div class="niwa-card">';
		echo '<div class="niwa-card__header">';
		echo '<div class="niwa-card__icon">';
		echo '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>';
		echo '</div>';
		echo '<h2 class="niwa-card__title">' . esc_html__( 'Authentication', 'niwa-ai' ) . '</h2>';
		echo '</div>';

		echo '<div class="niwa-card__body">';

		// Settings API form — mode + api_key fields only, submit button placed below OAuth via form attribute.
		echo '<form id="niwa-auth-form" method="post" action="options.php">';
		settings_fields( 'niwa_ai_auth_group' );

		// Mode selector.
		echo '<div class="niwa-field">';
		echo '<label class="niwa-field__label">' . esc_html__( 'Connection Mode', 'niwa-ai' ) . '</label>';
		echo '<span class="niwa-field__hint">' . esc_html__( 'Select how Niwa connects to OpenAI. Models and RLM configuration adapt automatically.', 'niwa-ai' ) . '</span>';
		echo '<select id="niwa-auth-mode" name="niwa_ai_auth[mode]">';
		echo '<option value="api_key" ' . selected( isset( $auth_opts['mode'] ) ? $auth_opts['mode'] : 'api_key', 'api_key', false ) . '>' . esc_html__( 'API Key', 'niwa-ai' ) . '</option>';
		echo '<option value="oauth" ' . selected( isset( $auth_opts['mode'] ) ? $auth_opts['mode'] : 'api_key', 'oauth', false ) . '>' . esc_html__( 'Codex OAuth', 'niwa-ai' ) . '</option>';
		echo '</select>';
		echo '</div>';

		// API Key section — always visible.
		echo '<div class="niwa-auth-section">';
		echo '<div class="niwa-auth-section__header">';
		echo '<span class="niwa-auth-section__title">' . esc_html__( 'API Key', 'niwa-ai' ) . '</span>';
		$api_active = ( 'api_key' === $auth_mode );
		echo '<span class="niwa-badge ' . ( $api_active ? 'niwa-badge--success' : 'niwa-badge--neutral' ) . '" id="niwa-api-key-badge">';
		echo $api_active ? esc_html__( 'Active', 'niwa-ai' ) : esc_html__( 'Standby', 'niwa-ai' );
		echo '</span>';
		echo '</div>';
		echo '<div class="niwa-field">';
		echo '<span class="niwa-field__hint">' . esc_html__( 'OpenAI API key. Used for chat and other direct OpenAI features in API Key mode, and for embeddings whenever a key is present.', 'niwa-ai' ) . '</span>';
		echo '<input type="password" name="niwa_ai_auth[api_key]" value="' . esc_attr( isset( $auth_opts['api_key'] ) ? $auth_opts['api_key'] : '' ) . '" class="niwa-input--wide" autocomplete="off" />';
		echo '</div>';
		echo '</div>';

		echo '</form>';
		echo '</div>';

		// Codex OAuth panel — always visible, between API Key and Save button.
		$this->render_oauth_panel( $oauth_status, $device_oauth_status, $auth_mode );

		// Save button — uses HTML5 form attribute to submit niwa-auth-form from outside it.
		echo '<div class="niwa-card__footer">';
		echo '<button type="submit" form="niwa-auth-form" class="button niwa-btn niwa-btn--primary">' . esc_html__( 'Save Auth Settings', 'niwa-ai' ) . '</button>';
		echo '</div>';

		echo '</div>';
	}

	private function render_oauth_panel( $oauth_status, $device_oauth_status, $auth_mode = 'api_key' ) {
		$connected       = ! empty( $oauth_status['connected'] );
		$email           = ! empty( $oauth_status['email'] ) ? (string) $oauth_status['email'] : '';
		$account_id      = ! empty( $oauth_status['account_id'] ) ? (string) $oauth_status['account_id'] : '';
		$expires_at      = ! empty( $oauth_status['expires_at'] ) ? intval( $oauth_status['expires_at'] ) : 0;
		$expired         = ! empty( $oauth_status['expired'] );
		$expiring_soon   = ! empty( $oauth_status['expiring_soon'] );
		$has_refresh     = ! empty( $oauth_status['has_refresh_token'] );
		$device_active   = ! empty( $device_oauth_status['active'] ) && ! $connected;
		$user_code       = $device_active && ! empty( $device_oauth_status['user_code'] ) ? (string) $device_oauth_status['user_code'] : '';
		$verify_url      = $device_active && ! empty( $device_oauth_status['verification_url'] ) ? (string) $device_oauth_status['verification_url'] : 'https://auth.openai.com/codex/device';
		$device_expires  = $device_active && ! empty( $device_oauth_status['expires_at'] ) ? intval( $device_oauth_status['expires_at'] ) : 0;
		$poll_interval   = $device_active && ! empty( $device_oauth_status['interval'] ) ? max( 2, intval( $device_oauth_status['interval'] ) ) : 5;
		$oauth_active    = ( 'oauth' === $auth_mode );

		echo '<div class="niwa-card__body" style="border-top: 1px solid var(--niwa-edge-2);">';
		echo '<div class="niwa-oauth" id="niwa-oauth-panel">';
		echo '<div class="niwa-auth-section__header">';
		echo '<span class="niwa-auth-section__title">' . esc_html__( 'Codex OAuth', 'niwa-ai' ) . '</span>';
		echo '<span class="niwa-badge ' . ( $oauth_active ? 'niwa-badge--success' : 'niwa-badge--neutral' ) . '" id="niwa-oauth-badge">';
		echo $oauth_active ? esc_html__( 'Active', 'niwa-ai' ) : esc_html__( 'Standby', 'niwa-ai' );
		echo '</span>';
		echo '</div>';

		if ( $connected ) {
			if ( $expired && ! $has_refresh ) {
				echo '<div class="niwa-alert niwa-alert--error"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>' . esc_html__( 'Stored token expired. Reconnect Codex OAuth.', 'niwa-ai' ) . '</div>';
			} elseif ( $expiring_soon && $has_refresh ) {
				echo '<div class="niwa-alert niwa-alert--warning"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>' . esc_html__( 'Connected. Token near expiry, refresh token available.', 'niwa-ai' ) . '</div>';
			} else {
				echo '<span class="niwa-badge niwa-badge--success"><span class="niwa-badge__dot"></span>' . esc_html__( 'Connected', 'niwa-ai' ) . '</span>';
			}

			if ( '' !== $email || '' !== $account_id || $expires_at > 0 ) {
				echo '<div class="niwa-kv" style="margin-top:12px;">';
				if ( '' !== $email ) {
					echo '<span class="niwa-kv__key">' . esc_html__( 'Email', 'niwa-ai' ) . '</span>';
					echo '<span class="niwa-kv__value">' . esc_html( $email ) . '</span>';
				}
				if ( '' !== $account_id ) {
					echo '<span class="niwa-kv__key">' . esc_html__( 'Account ID', 'niwa-ai' ) . '</span>';
					echo '<span class="niwa-kv__value">' . esc_html( $account_id ) . '</span>';
				}
				if ( $expires_at > 0 ) {
					echo '<span class="niwa-kv__key">' . esc_html__( 'Token expires', 'niwa-ai' ) . '</span>';
					echo '<span class="niwa-kv__value">' . esc_html( date_i18n( 'Y-m-d H:i:s', $expires_at ) ) . '</span>';
				}
				echo '</div>';
			}
		} elseif ( $device_active ) {
			echo '<p style="margin:0 0 12px;font-size:13px;color:var(--niwa-ink-2);">' . esc_html__( 'Verification in progress. Finish it in browser and this page will connect automatically.', 'niwa-ai' ) . '</p>';
			if ( '' !== $user_code ) {
				echo '<div style="margin-bottom:12px;">';
				echo '<span class="niwa-field__label">' . esc_html__( 'Verification code', 'niwa-ai' ) . '</span>';
				echo '<div class="niwa-oauth__code">' . esc_html( $user_code ) . '</div>';
				echo '</div>';
			}
			if ( $device_expires > 0 ) {
				echo '<p style="margin:0 0 8px;font-size:12px;color:var(--niwa-ink-3);">' . esc_html__( 'Code expires:', 'niwa-ai' ) . ' ' . esc_html( date_i18n( 'Y-m-d H:i:s', $device_expires ) ) . '</p>';
			}
		} else {
			echo '<p style="margin:0 0 12px;font-size:13px;color:var(--niwa-ink-3);">' . esc_html__( 'Not connected. Click Connect to start OpenAI verification flow.', 'niwa-ai' ) . '</p>';
		}

		if ( ! empty( $oauth_status['last_error'] ) ) {
			echo '<div class="niwa-alert niwa-alert--error" style="margin-top:12px;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>' . esc_html( (string) $oauth_status['last_error'] ) . '</div>';
		}

		echo '<div class="niwa-btn-group" style="margin-top:16px;">';

		echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '" style="display:inline;">';
		echo '<input type="hidden" name="action" value="niwa_ai_codex_oauth_start" />';
		wp_nonce_field( 'niwa_ai_codex_oauth_start', 'niwa_ai_codex_oauth_start_nonce' );
		$connect_label = $connected ? __( 'Reconnect', 'niwa-ai' ) : ( $device_active ? __( 'Restart', 'niwa-ai' ) : __( 'Connect Codex OAuth', 'niwa-ai' ) );
		echo '<button type="submit" class="button niwa-btn niwa-btn--secondary">' . esc_html( $connect_label ) . '</button>';
		echo '</form>';

		if ( $connected ) {
			echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '" style="display:inline;">';
			echo '<input type="hidden" name="action" value="niwa_ai_codex_oauth_disconnect" />';
			wp_nonce_field( 'niwa_ai_codex_oauth_disconnect', 'niwa_ai_codex_oauth_disconnect_nonce' );
			echo '<button type="submit" class="button niwa-btn niwa-btn--danger">' . esc_html__( 'Disconnect', 'niwa-ai' ) . '</button>';
			echo '</form>';
		} elseif ( $device_active ) {
			echo '<a class="button niwa-btn niwa-btn--primary" href="' . esc_url( $verify_url ) . '" target="_blank" rel="noopener">' . esc_html__( 'Open Verification', 'niwa-ai' ) . '</a>';
			echo '<span id="niwa-codex-oauth-status" style="font-size:12px;color:var(--niwa-ink-3);">' . esc_html__( 'Waiting for verification...', 'niwa-ai' ) . '</span>';
			$poll_nonce    = wp_create_nonce( 'niwa_ai_codex_oauth_poll' );
			$connected_url = add_query_arg( array( 'page' => 'niwa-ai', 'niwa_oauth' => 'connected' ), admin_url( 'admin.php' ) );
			wp_enqueue_script( 'niwa-admin-oauth-poll', NIWA_AI_URL . 'assets/js/niwa-admin-oauth-poll.js', array(), NIWA_AI_VERSION, array( 'in_footer' => true ) );
			wp_localize_script( 'niwa-admin-oauth-poll', 'niwaAdminOAuthPoll', array(
				'nonce'        => $poll_nonce,
				'interval'     => intval( $poll_interval ),
				'connectedUrl' => $connected_url,
			) );
		}

		echo '</div>';
		echo '</div>';
		echo '</div>';
	}

	/* ================================================================
	 * Embeddings Card
	 * ============================================================= */

	private function render_embeddings_card() {
		echo '<div class="niwa-card">';
		echo '<div class="niwa-card__header">';
		echo '<div class="niwa-card__icon">';
		echo '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>';
		echo '</div>';
		echo '<h2 class="niwa-card__title">' . esc_html__( 'Embeddings', 'niwa-ai' ) . '</h2>';
		echo '<span class="niwa-card__title-note"><code>text-embedding-3-large</code></span>';
		echo '</div>';

		echo '<div class="niwa-card__body">';
		echo '<p class="niwa-field__hint">' . esc_html__( 'Uses your OpenAI API key when it works and has credit. Otherwise, Niwa provides embeddings automatically through niwachat.com.', 'niwa-ai' ) . '</p>';
		echo '</div>';
		echo '</div>';
	}

	/* ================================================================
	 * General Card
	 * ============================================================= */

	private function render_general_card( $gen_opts, $chat_model, $model_options, $auth_mode, $api_key_for_models ) {
		echo '<div class="niwa-card">';
		echo '<div class="niwa-card__header">';
		echo '<div class="niwa-card__icon">';
		echo '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>';
		echo '</div>';
		echo '<h2 class="niwa-card__title">' . esc_html__( 'General Settings', 'niwa-ai' ) . '</h2>';
		echo '</div>';

		echo '<div class="niwa-card__body">';
		echo '<form id="niwa-general-form" method="post" action="options.php">';
		settings_fields( 'niwa_ai_general_group' );
		echo '<input type="hidden" name="niwa_ai_settings[_niwa_general_form]" value="main" />';

		// Chat model.
		echo '<div class="niwa-field">';
		echo '<label class="niwa-field__label">' . esc_html__( 'Chat Model', 'niwa-ai' ) . '</label>';
		echo '<span class="niwa-field__hint" id="niwa-model-hint">';
		if ( 'api_key' === $auth_mode && '' === trim( $api_key_for_models ) ) {
			echo esc_html__( 'Add API key and save Auth settings to fetch full live model list.', 'niwa-ai' );
		} else {
			echo esc_html__( 'Available models depend on the selected connection mode.', 'niwa-ai' );
		}
		echo '</span>';
		echo '<select id="niwa-chat-model" name="niwa_ai_settings[model]" style="min-width:320px;">';
		foreach ( $model_options as $model_id => $model_label ) {
			echo '<option value="' . esc_attr( $model_id ) . '" ' . selected( $chat_model, $model_id, false ) . '>' . esc_html( $model_label ) . '</option>';
		}
		echo '</select>';
		echo '</div>';

		// Optional per-stage tracing for frontend latency diagnostics.
		$stage_timing_enabled = ! array_key_exists( 'enable_frontend_stage_timing', $gen_opts ) || ! empty( $gen_opts['enable_frontend_stage_timing'] );
		echo '<div class="niwa-field">';
		echo '<label class="niwa-field__label" for="niwa-enable-frontend-stage-timing">' . esc_html__( 'Frontend stage timing', 'niwa-ai' ) . '</label>';
		echo '<label style="display:flex;align-items:center;gap:8px;">';
		echo '<input type="checkbox" id="niwa-enable-frontend-stage-timing" name="niwa_ai_settings[enable_frontend_stage_timing]" value="1" ' . checked( $stage_timing_enabled, true, false ) . ' />';
		echo '<span>' . esc_html__( 'Measure response stages during troubleshooting', 'niwa-ai' ) . '</span>';
		echo '</label>';
		echo '<span class="niwa-field__hint">' . esc_html__( 'Stores one timing trace per frontend request ID. Turn this off when troubleshooting is complete.', 'niwa-ai' ) . '</span>';
		echo '</div>';

		echo '<p id="niwa-general-auth-dirty-hint" class="niwa-field__hint" aria-live="polite" hidden>';
		echo esc_html__( 'Save Authentication settings and reload this page before changing General models.', 'niwa-ai' );
		echo '</p>';

		echo '<div class="niwa-card__footer">';
		submit_button( __( 'Save General Settings', 'niwa-ai' ), 'primary niwa-btn niwa-btn--primary', 'submit', false );
		echo '</div>';
		echo '</form>';
		echo '</div>';
		echo '</div>';
	}

	private function render_general_avatar_field( $field_key, $label, $hint, $value, $default_url ) {
		$field_key   = sanitize_key( $field_key );
		$input_id    = 'niwa_' . $field_key;
		$preview_id  = $input_id . '_preview';
		$upload_id   = $input_id . '_upload';
		$remove_id   = $input_id . '_remove';
		$image_url   = '' !== trim( (string) $value ) ? (string) $value : (string) $default_url;
		$remove_css  = '' !== trim( (string) $value ) ? '' : ' style="display:none;"';

		echo '<div class="niwa-field">';
		echo '<label class="niwa-field__label" for="' . esc_attr( $input_id ) . '">' . esc_html( $label ) . '</label>';
		echo '<span class="niwa-field__hint">' . esc_html( $hint ) . '</span>';
		echo '<input type="hidden" id="' . esc_attr( $input_id ) . '" name="niwa_ai_settings[' . esc_attr( $field_key ) . ']" value="' . esc_attr( $value ) . '" />';
		echo '<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:8px;">';
		echo '<button type="button" class="button" id="' . esc_attr( $upload_id ) . '" data-niwa-media-target="' . esc_attr( $input_id ) . '" data-niwa-preview-target="' . esc_attr( $preview_id ) . '">' . esc_html__( 'Choose Image', 'niwa-ai' ) . '</button>';
		echo '<button type="button" class="button" id="' . esc_attr( $remove_id ) . '" data-niwa-remove-target="' . esc_attr( $input_id ) . '" data-niwa-preview-target="' . esc_attr( $preview_id ) . '" data-niwa-default-preview="' . esc_attr( $default_url ) . '"' . $remove_css . '>' . esc_html__( 'Remove', 'niwa-ai' ) . '</button>';
		echo '</div>';
		echo '<div id="' . esc_attr( $preview_id ) . '" style="margin-top:10px;">';
		echo '<img src="' . esc_url( $image_url ) . '" alt="" style="width:64px;height:64px;object-fit:cover;border-radius:999px;border:1px solid rgba(15,23,42,0.08);background:#fff;display:block;" />';
		echo '</div>';
		echo '</div>';
	}

	/* ================================================================
	 * Progress Card
	 * ============================================================= */

	private function render_progress_card() {
		$embedding_stats = $this->content_store ? $this->content_store->get_embedding_stats() : array( 'total' => 0, 'done' => 0, 'pending' => 0 );
		$emb_total       = isset( $embedding_stats['total'] ) ? intval( $embedding_stats['total'] ) : 0;
		$emb_done        = isset( $embedding_stats['done'] ) ? intval( $embedding_stats['done'] ) : 0;
		$emb_pending     = isset( $embedding_stats['pending'] ) ? intval( $embedding_stats['pending'] ) : 0;

		$index_progress = get_option( Niwa_AI_Constants::OPT_INDEX_PROGRESS, array() );
		$index_total    = isset( $index_progress['total'] ) ? intval( $index_progress['total'] ) : 0;
		$index_done     = isset( $index_progress['processed'] ) ? intval( $index_progress['processed'] ) : 0;
		$index_status   = isset( $index_progress['status'] ) ? (string) $index_progress['status'] : 'idle';
		if ( $index_total <= 0 && $this->content_store ) {
			$index_total = $this->content_store->count_by_type( 'product' )
				+ $this->content_store->count_by_type( 'page' )
				+ $this->content_store->count_by_type( 'post' )
				+ $this->content_store->count_by_type( 'elementor_library' )
				+ $this->content_store->count_by_type( 'wp_template' )
				+ $this->content_store->count_by_type( 'wp_template_part' )
				+ $this->content_store->count_by_type( 'wp_block' )
				+ $this->content_store->count_by_type( 'wp_navigation' )
				+ $this->content_store->count_by_type( 'site_knowledge' );
			$index_done   = $index_total;
			$index_status = 'done';
		} elseif ( 'done' === $index_status ) {
			$index_done = $index_total;
		}

		$overall_total   = $index_total + $emb_total;
		$overall_done    = $index_done + $emb_done;
		$overall_percent = $overall_total > 0 ? min( 100, max( 0, round( ( $overall_done / $overall_total ) * 100 ) ) ) : 0;
		$is_complete     = ( 100 === $overall_percent );

		echo '<div class="niwa-card">';
		echo '<div class="niwa-card__header">';
		echo '<div class="niwa-card__icon' . ( $is_complete ? ' niwa-card__icon--success' : '' ) . '">';
		echo '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>';
		echo '</div>';
		echo '<h2 class="niwa-card__title">' . esc_html__( 'Index + Embeddings', 'niwa-ai' ) . '</h2>';
		echo '</div>';

		echo '<div class="niwa-card__body">';

		// Mini stats.
		echo '<div class="niwa-stats" style="margin-bottom:20px;">';

		echo '<div class="niwa-stat niwa-stat--brand">';
		echo '<div class="niwa-stat__label">' . esc_html__( 'Indexed', 'niwa-ai' ) . '</div>';
		echo '<div class="niwa-stat__value">' . esc_html( $index_done . ' / ' . $index_total ) . '</div>';
		echo '</div>';

		echo '<div class="niwa-stat niwa-stat--brand">';
		echo '<div class="niwa-stat__label">' . esc_html__( 'Embedded', 'niwa-ai' ) . '</div>';
		echo '<div class="niwa-stat__value">' . esc_html( $emb_done . ' / ' . $emb_total ) . '</div>';
		echo '</div>';

		echo '<div class="niwa-stat' . ( $emb_pending > 0 ? ' niwa-stat--warning' : ' niwa-stat--success' ) . '">';
		echo '<div class="niwa-stat__label">' . esc_html__( 'Pending', 'niwa-ai' ) . '</div>';
		echo '<div class="niwa-stat__value">' . esc_html( (string) $emb_pending ) . '</div>';
		echo '</div>';

		echo '</div>';

		// Progress bar.
		$fill_class = 'niwa-progress__fill';
		if ( $is_complete ) {
			$fill_class .= ' niwa-progress__fill--complete';
		} elseif ( $overall_percent > 0 && $overall_percent < 100 ) {
			$fill_class .= ' niwa-progress__fill--animated';
		}

		echo '<div class="niwa-progress">';
		echo '<div class="niwa-progress__track">';
		echo '<div id="niwa-embed-progress" class="' . esc_attr( $fill_class ) . '" style="width:' . esc_attr( (string) $overall_percent ) . '%;"></div>';
		echo '</div>';
		echo '<div class="niwa-progress__meta">';
		echo '<span id="niwa-embed-info">' . esc_html__( 'Index:', 'niwa-ai' ) . ' ' . esc_html( $index_done . '/' . $index_total ) . ' &middot; ' . esc_html__( 'Embeddings:', 'niwa-ai' ) . ' ' . esc_html( $emb_done . '/' . $emb_total ) . '</span>';
		echo '<span id="niwa-embed-percent" class="niwa-progress__percent">' . esc_html( (string) $overall_percent ) . '%</span>';
		echo '</div>';
		echo '</div>';

		// Error.
		$last_error = get_option( Niwa_AI_Constants::OPT_EMBEDDING_LAST_ERROR, array() );
		if ( is_array( $last_error ) && ! empty( $last_error['message'] ) ) {
			$when   = isset( $last_error['at'] ) ? intval( $last_error['at'] ) : 0;
			$suffix = $when > 0 ? ' (' . date_i18n( 'Y-m-d H:i', $when ) . ')' : '';
			echo '<div class="niwa-alert niwa-alert--error"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>' . esc_html( $last_error['message'] . $suffix ) . '</div>';
		}

		// Embeddings polling script.
		$embed_nonce = wp_create_nonce( 'niwa_ai_embeddings_status' );
		wp_enqueue_script( 'niwa-admin-embeddings', NIWA_AI_URL . 'assets/js/niwa-admin-embeddings.js', array(), NIWA_AI_VERSION, array( 'in_footer' => true ) );
		wp_localize_script( 'niwa-admin-embeddings', 'niwaAdminEmbeddings', array( 'nonce' => $embed_nonce ) );

		echo '</div>';

		// Footer — action button.
		echo '<div class="niwa-card__footer">';
		echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '">';
		echo '<input type="hidden" name="action" value="niwa_ai_run_full_index_and_embeddings" />';
		wp_nonce_field( 'niwa_ai_run_full_index_and_embeddings', 'niwa_ai_run_full_index_and_embeddings_nonce' );
		echo '<button type="submit" class="button niwa-btn niwa-btn--primary">' . esc_html__( 'Run Full Index + Embeddings', 'niwa-ai' ) . '</button>';
		echo '</form>';
		echo '</div>';

		echo '</div>';
	}

	/* ================================================================
	 * RLM Demo Card
	 * ============================================================= */

	private function render_rlm_demo_card( $demo_query, $demo_result ) {
		echo '<div class="niwa-card">';
		echo '<div class="niwa-card__header">';
		echo '<div class="niwa-card__icon">';
		echo '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>';
		echo '</div>';
		echo '<h2 class="niwa-card__title">' . esc_html__( 'RLM Demo', 'niwa-ai' ) . '</h2>';
		echo '</div>';

		echo '<div class="niwa-card__body">';
		echo '<form method="get" action="" class="niwa-field__row" style="align-items:flex-end;">';
		echo '<input type="hidden" name="page" value="niwa-ai" />';
		echo '<div style="flex:1;min-width:240px;">';
		echo '<label class="niwa-field__label">' . esc_html__( 'Test Query', 'niwa-ai' ) . '</label>';
		echo '<input type="text" name="niwa_demo_query" value="' . esc_attr( $demo_query ) . '" placeholder="' . esc_attr__( 'Type a question to test RLM retrieval...', 'niwa-ai' ) . '" class="niwa-input--wide" style="max-width:100%;" />';
		echo '</div>';
		echo '<button class="button niwa-btn niwa-btn--primary" type="submit">' . esc_html__( 'Run Demo', 'niwa-ai' ) . '</button>';
		echo '</form>';

		if ( ! empty( $demo_query ) ) {
			echo '<div class="niwa-demo-result">';
			if ( ! empty( $demo_result['success'] ) ) {
				echo '<div class="niwa-kv" style="margin-bottom:12px;">';
				echo '<span class="niwa-kv__key">' . esc_html__( 'Source', 'niwa-ai' ) . '</span>';
				echo '<span class="niwa-kv__value">' . esc_html( isset( $demo_result['source'] ) ? $demo_result['source'] : 'rlm' ) . '</span>';
				echo '<span class="niwa-kv__key">' . esc_html__( 'Confidence', 'niwa-ai' ) . '</span>';
				echo '<span class="niwa-kv__value">' . esc_html( isset( $demo_result['confidence'] ) ? (string) $demo_result['confidence'] : '' ) . '</span>';
				echo '</div>';
				echo '<pre>' . esc_html( $demo_result['answer'] ) . '</pre>';
				if ( ! empty( $demo_result['cards'] ) && is_array( $demo_result['cards'] ) ) {
					echo '<h4 style="margin:16px 0 8px;font-size:13px;font-weight:600;color:var(--niwa-ink-2);">' . esc_html__( 'Cards', 'niwa-ai' ) . '</h4>';
					echo '<ul style="margin:0;padding-left:18px;">';
					foreach ( $demo_result['cards'] as $c ) {
						echo '<li style="font-size:13px;color:var(--niwa-ink-2);">' . esc_html( isset( $c['name'] ) ? $c['name'] : '' ) . '</li>';
					}
					echo '</ul>';
				}
			} else {
				echo '<div class="niwa-alert niwa-alert--error"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>' . esc_html( isset( $demo_result['error'] ) ? $demo_result['error'] : 'unknown' ) . '</div>';
			}
			echo '</div>';
		}

		echo '</div>';
		echo '</div>';
	}

	private function should_load_admin_autonomy_launcher( $hook_suffix = '' ) {
		if ( ! is_admin() || wp_doing_ajax() || wp_doing_cron() ) {
			return false;
		}

		if ( ! current_user_can( 'manage_options' ) || ! $this->admin_autonomy || ! $this->asset_loader ) {
			return false;
		}

		if ( function_exists( 'wp_is_json_request' ) && wp_is_json_request() ) {
			return false;
		}

		$script = isset( $_SERVER['SCRIPT_NAME'] ) ? wp_basename( wp_unslash( $_SERVER['SCRIPT_NAME'] ) ) : '';
		if ( in_array( $script, array( 'async-upload.php', 'media-upload.php' ), true ) ) {
			return false;
		}

		$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
		if ( $screen && in_array( $screen->base, array( 'customize', 'site-editor' ), true ) ) {
			return false;
		}

		// Performance: keep the default limited to Dashboard + Niwa pages, while
		// letting site owners deliberately expand or tighten the wp-admin scope.
		$is_dashboard = $screen && 'dashboard' === $screen->base;
		$is_niwa_page = ( is_string( $hook_suffix ) && false !== strpos( $hook_suffix, 'niwa-ai' ) )
			|| ( $screen && isset( $screen->id ) && false !== strpos( (string) $screen->id, 'niwa-ai' ) )
			|| ( $screen && isset( $screen->base ) && false !== strpos( (string) $screen->base, 'niwa-ai' ) );
		$scope        = $this->get_admin_autonomy_launcher_scope();
		if ( 'everywhere' === $scope ) {
			$is_allowed = true;
		} elseif ( 'dashboard' === $scope ) {
			$is_allowed = $is_dashboard;
		} else {
			$is_allowed = $is_dashboard || $is_niwa_page;
		}

		if ( ! $is_allowed && method_exists( $this, 'has_active_admin_ui_chat_job_for_current_user' ) ) {
			$is_allowed = $this->has_active_admin_ui_chat_job_for_current_user();
		}

		/**
		 * Filter whether the admin autonomy launcher should load on the current
		 * screen. Receive `$is_allowed` (default-screen gate result),
		 * `$hook_suffix`, and the current `$screen` object. Return bool.
		 */
		$is_allowed = apply_filters( 'niwa_ai_admin_launcher_screens', $is_allowed, $hook_suffix, $screen );

		return (bool) $is_allowed;
	}

	private function get_admin_autonomy_launcher_scope() {
		$settings = get_option( Niwa_AI_Constants::OPT_AUTONOMY, array() );
		$settings = is_array( $settings ) ? $settings : array();
		$scope    = isset( $settings['admin_launcher_scope'] ) ? sanitize_key( (string) $settings['admin_launcher_scope'] ) : 'niwa_dashboard';

		return in_array( $scope, array( 'dashboard', 'niwa_dashboard', 'everywhere' ), true ) ? $scope : 'niwa_dashboard';
	}

	private function build_admin_autonomy_launcher_config() {
		$opts         = get_option( Niwa_AI_Constants::OPT_SETTINGS, array() );
		$opts         = is_array( $opts ) ? $opts : array();
		$theme_mode   = isset( $opts['chat_theme_mode'] ) ? (string) $opts['chat_theme_mode'] : 'neutral';
		$language     = $this->detect_admin_autonomy_launcher_language();
		$copy         = $this->get_admin_autonomy_language_copy( $language );
		$greeting     = __( 'Zdravo. Ja sam Niwa admin agent. Mogu da pregledam sajt, proverim WordPress i WooCommerce stanje, i izvedem izmene direktno iz backenda.', 'niwa-ai' );
		$banner       = __( 'Šta želiš da uradim u WordPress adminu?', 'niwa-ai' );
		$placeholder  = __( 'Napiši zadatak za Niwa AI...', 'niwa-ai' );
		$launcher_txt = __( 'Ask Niwa AI', 'niwa-ai' );
		$labels       = array(
			'ask_what_you_need' => __( 'Kako mogu da pomognem?', 'niwa-ai' ),
			'send_button'       => __( 'Pošalji', 'niwa-ai' ),
		);

		$launcher_ui  = $this->get_admin_autonomy_launcher_ui_strings();
		$assistant_name = $this->get_admin_autonomy_display_name();
		$greeting     = $launcher_ui['greeting'];
		$banner       = $launcher_ui['banner'];
		$placeholder  = $launcher_ui['placeholder'];
		$launcher_txt = $launcher_ui['launcher_text'];
		$labels       = array(
			'ask_what_you_need' => $launcher_ui['help_prompt'],
			'send_button'       => $launcher_ui['send_button'],
		);
		$localized_suggestions = 'sr' === $language
			? array(
				'Proveri status sajta',
				'Prikaži aktivne sesije',
				'Šta si poslednje radio?',
			)
			: array(
				'Check site status',
				'Show active sessions',
				'What did you work on last?',
			);
		if ( isset( $copy['initial_suggestions'] ) && is_array( $copy['initial_suggestions'] ) ) {
			$localized_suggestions = $copy['initial_suggestions'];
		}

		$initial_suggestions = ! empty( $launcher_ui['show_suggestions'] )
			? array(
				'list'  => $localized_suggestions,
				'codes' => array( null, null, null ),
			)
			: array(
				'list'  => array(),
				'codes' => array(),
			);

		return array(
			'ajax'      => admin_url( 'admin-ajax.php' ),
			'nonce'     => wp_create_nonce( 'niwa_ai_admin_autonomy_chat_nonce' ),
			'action'    => 'niwa_ai_admin_autonomy_chat',
			'history_action' => 'niwa_ai_admin_autonomy_history',
			'status_action' => 'niwa_ai_admin_autonomy_status',
			'admin_mode' => true,
			'delay'     => 300,
			'greeting'  => $greeting,
			'greeting_version' => md5( $greeting ),
			'checkout'  => '',
			'brand'     => array( 'dark' => '#020c0a', 'accent' => '#d2ef9a', 'white' => '#FFFFFF' ),
			'is_debug'  => ( defined( 'WP_DEBUG' ) && WP_DEBUG ),
			'pluginURL' => NIWA_AI_URL,
			'admin_fresh_session' => false,
			'admin_session_key'   => function_exists( 'wp_get_session_token' )
				? wp_hash( get_current_user_id() . ':' . wp_get_session_token() )
				: wp_hash( get_current_user_id() . ':admin' ),

			'banner_message'         => $banner,
			'avatar_url'             => $this->get_backend_agent_avatar_url( $opts ),
			'wordmark_url'           => NIWA_AI_URL . 'assets/img/niwa-wordmark.svg',
			'assistant_display_name' => $assistant_name,

			'centerstage_mode' => false,
			'launcher_desktop' => array(
				'position' => 'br',
				'x'        => 24,
				'y'        => 24,
				'w'        => 148,
				'h'        => 48,
			),
			'launcher_tablet' => array(
				'position' => 'br',
				'x'        => 20,
				'y'        => 20,
				'w'        => 148,
				'h'        => 48,
			),
			'launcher_mobile' => array(
				'position' => 'br',
				'x'        => 16,
				'y'        => 16,
				'w'        => 140,
				'h'        => 48,
			),
			'desktop' => array(
				'w' => 420,
				'h' => 680,
			),
			'tablet' => array(
				'w' => 360,
				'h' => 620,
			),
			'mobile' => array(
				'w'          => 340,
				'h'          => 600,
				'fullscreen' => true,
			),
			'language'    => $language,
			'labels'      => $labels,
			'initial_suggestions'   => $initial_suggestions,
			'email_optin_enabled'   => false,
			'email_ask_name_again'  => false,
			'phone_capture_enabled' => false,
			'phone_capture_mode'    => 'off',
			'leave_stoper'          => array( 'enabled' => false ),
			'chat_theme_mode'       => $theme_mode,
			'delivery_days'         => 0,
			'product_card'          => array(
				'footer_mode'       => 'off',
				'banner_type'       => 'off',
				'banner_text'       => '',
				'banner_bg_color'   => '#000000',
				'banner_bg_opacity' => 0,
				'delivery_days'     => 0,
			),
			'site_icon'             => get_site_icon_url( 64, '' ),
			'message_placeholder'   => $placeholder,
			'launcher_text'         => $launcher_txt,
		);
	}

	private function get_admin_autonomy_launcher_ui_strings() {
		$language     = $this->detect_admin_autonomy_launcher_language();
		$copy         = $this->get_admin_autonomy_language_copy( $language );
		$profile      = $this->get_admin_autonomy_identity_profile();
		$is_complete  = $this->is_admin_autonomy_identity_complete();
		$help_prompt  = $this->get_admin_autonomy_help_prompt( $language );
		$placeholder  = $this->get_admin_autonomy_message_placeholder( $language, $is_complete );
		$placeholder  = ! empty( $is_complete )
			? ( isset( $copy['message_placeholder'] ) ? (string) $copy['message_placeholder'] : $placeholder )
			: ( isset( $copy['answer_placeholder'] ) ? (string) $copy['answer_placeholder'] : $placeholder );
		$send_button  = 'sr' === $language ? 'Pošalji' : 'Send';
		$send_button  = isset( $copy['send_button'] ) ? (string) $copy['send_button'] : $send_button;
		$launcher_txt = $this->get_admin_autonomy_launcher_label( $language, $profile, $is_complete );
		$greeting     = $help_prompt;
		$banner       = $help_prompt;

		if ( ! $is_complete ) {
			$onboarding_prompt = $this->prime_admin_autonomy_launcher_onboarding();
			if ( '' !== $onboarding_prompt ) {
				$greeting = $onboarding_prompt;
				$banner   = $onboarding_prompt;
			}
		}

		if ( ! $is_complete && '' === trim( (string) ( isset( $profile['assistant_name'] ) ? $profile['assistant_name'] : '' ) ) ) {
			$placeholder = 'sr' === $language ? 'Napiši odgovor...' : 'Type your answer...';
		}

		return array(
			'greeting'         => $greeting,
			'banner'           => $banner,
			'help_prompt'      => $help_prompt,
			'placeholder'      => $placeholder,
			'launcher_text'    => $launcher_txt,
			'send_button'      => $send_button,
			'show_suggestions' => $is_complete,
		);
	}

	private function get_admin_autonomy_identity_profile() {
		if ( $this->admin_autonomy_workspace && method_exists( $this->admin_autonomy_workspace, 'get_identity_profile' ) ) {
			return (array) $this->admin_autonomy_workspace->get_identity_profile();
		}

		return array();
	}

	private function get_frontend_agent_avatar_url( $opts = array() ) {
		$opts = is_array( $opts ) ? $opts : array();
		$url  = isset( $opts['frontend_avatar_url'] ) ? esc_url_raw( (string) $opts['frontend_avatar_url'] ) : '';
		$default_url = defined( 'NIWA_AI_DEFAULT_FRONTEND_AVATAR_URL' )
			? NIWA_AI_DEFAULT_FRONTEND_AVATAR_URL
			: NIWA_AI_URL . 'assets/img/niwa-admin-personal-assistant.png';

		return '' !== trim( $url ) ? $url : $default_url;
	}

	private function get_backend_agent_avatar_url( $opts = array() ) {
		$opts = is_array( $opts ) ? $opts : array();
		$url  = isset( $opts['backend_avatar_url'] ) ? esc_url_raw( (string) $opts['backend_avatar_url'] ) : '';

		$default_url = defined( 'NIWA_AI_DEFAULT_BACKEND_AVATAR_URL' )
			? NIWA_AI_DEFAULT_BACKEND_AVATAR_URL
			: NIWA_AI_URL . 'assets/img/niwa-admin-personal-assistant.png';

		return '' !== trim( $url ) ? $url : $default_url;
	}

	private function get_admin_autonomy_display_name() {
		$profile = $this->get_admin_autonomy_identity_profile();
		$name    = class_exists( 'Niwa_AI_Identity_Name' ) && isset( $profile['assistant_name'] )
			? Niwa_AI_Identity_Name::normalize( $profile['assistant_name'] )
			: '';

		if ( '' !== $name ) {
			return $name;
		}

		$copy = $this->get_admin_autonomy_language_copy( $this->detect_admin_autonomy_launcher_language() );
		return isset( $copy['assistant_name_fallback'] ) ? (string) $copy['assistant_name_fallback'] : 'Assistant';
	}

	private function get_admin_autonomy_launcher_label( $language, $profile, $is_complete ) {
		$profile = is_array( $profile ) ? $profile : array();
		$name    = class_exists( 'Niwa_AI_Identity_Name' ) && isset( $profile['assistant_name'] )
			? Niwa_AI_Identity_Name::normalize( $profile['assistant_name'] )
			: '';
		$copy    = $this->get_admin_autonomy_language_copy( $language );

		if ( '' === $name ) {
			return isset( $copy['open_assistant'] ) ? (string) $copy['open_assistant'] : 'Open assistant';
		}

		return $name;
	}

	private function is_admin_autonomy_identity_complete() {
		if ( $this->admin_autonomy_chat && method_exists( $this->admin_autonomy_chat, 'is_founder_onboarding_complete' ) ) {
			return (bool) $this->admin_autonomy_chat->is_founder_onboarding_complete();
		}

		$profile = $this->get_admin_autonomy_identity_profile();
		return ! empty( $profile['completed_at'] ) && 'complete' === (string) ( isset( $profile['onboarding_step'] ) ? $profile['onboarding_step'] : '' );
	}

	private function prime_admin_autonomy_launcher_onboarding() {
		$context = array(
			'ui_locale'             => function_exists( 'determine_locale' ) ? (string) determine_locale() : '',
			'ui_language'           => $this->get_admin_autonomy_preferred_language(),
			'conversation_language' => $this->get_admin_autonomy_preferred_language(),
		);

		if ( $this->admin_autonomy_chat && method_exists( $this->admin_autonomy_chat, 'maybe_handle_founder_onboarding' ) ) {
			$result = (array) $this->admin_autonomy_chat->maybe_handle_founder_onboarding( '', $context );
			if ( ! empty( $result['handled'] ) && ! empty( $result['reply'] ) ) {
				return trim( (string) $result['reply'] );
			}
		}

		if ( $this->admin_autonomy_chat && method_exists( $this->admin_autonomy_chat, 'build_founder_start_message' ) ) {
			return trim( (string) $this->admin_autonomy_chat->build_founder_start_message( $context ) );
		}

		$language = $this->detect_admin_autonomy_launcher_language();
		$copy     = $this->get_admin_autonomy_language_copy( $language );
		if ( isset( $copy['wake_up'] ) ) {
			return (string) $copy['wake_up'];
		}
		return 'sr' === $language
			? 'Hej! Oh, upravo ste me probudili, cekajte da pogledam okolo... znaci ja sam na ovom sajtu, vau, znaci ovo je moj dom... Ali ne znam nista o njemu, nista o Vama, a ni o sebi jos uvek.' . "\n\n" . 'Mozete li da mi pomognete? Imam gomilu pitanja, ali pocecu od najvaznijeg. Kako glasi Vase ime?'
			: 'Hey! Oh, you just woke me up, let me take a look around... so I am on this site, wow, this is my home. But I do not know anything about it, anything about you, or anything about myself yet.' . "\n\n" . 'Can you help me out? I have a ton of questions, but let me start with the most important one.' . "\n" . 'What is your name?';
	}

	private function get_admin_autonomy_help_prompt( $language ) {
		$copy = $this->get_admin_autonomy_language_copy( $language );
		if ( isset( $copy['help_prompt'] ) ) {
			return (string) $copy['help_prompt'];
		}
		return 'sr' === $language
			? 'Kako mogu da ti pomognem danas?'
			: 'How can I help you today?';
	}

	private function get_admin_autonomy_message_placeholder( $language, $is_complete ) {
		$copy = $this->get_admin_autonomy_language_copy( $language );
		if ( ! $is_complete ) {
			if ( isset( $copy['answer_placeholder'] ) ) {
				return (string) $copy['answer_placeholder'];
			}
			return 'sr' === $language ? 'Napiši odgovor...' : 'Type your answer...';
		}

		return 'sr' === $language ? 'Napiši poruku...' : 'Type your message...';
	}

	private function detect_admin_autonomy_launcher_language() {
		return $this->get_admin_autonomy_preferred_language();
	}

	private function get_admin_autonomy_preferred_language() {
		$ui_settings = get_option( Niwa_AI_Constants::OPT_UI_SETTINGS, array() );
		$language    = is_array( $ui_settings ) && isset( $ui_settings['ui_language'] )
			? (string) $ui_settings['ui_language']
			: '';
		$normalized  = $this->normalize_admin_autonomy_language_code( $language );

		if ( '' !== $normalized ) {
			return $normalized;
		}

		$locale = function_exists( 'determine_locale' ) ? (string) determine_locale() : '';
		if ( '' === $locale && function_exists( 'get_locale' ) ) {
			$locale = (string) get_locale();
		}

		$normalized = $this->normalize_admin_autonomy_language_code( $locale );
		return '' !== $normalized ? $normalized : 'en';
	}

	private function normalize_admin_autonomy_language_code( $language ) {
		$language = strtolower( trim( str_replace( '-', '_', (string) $language ) ) );
		if ( '' === $language ) {
			return '';
		}

		$parts = preg_split( '/_+/', $language );
		$base  = isset( $parts[0] ) ? preg_replace( '/[^a-z]/', '', (string) $parts[0] ) : '';
		if ( '' === $base ) {
			return '';
		}

		if ( 'bs' === $base ) {
			return 'sr';
		}

		return in_array( $base, array( 'en', 'sr', 'hr', 'de', 'es', 'fr' ), true ) ? $base : 'en';
	}

	private function get_admin_autonomy_language_copy( $language ) {
		$language = $this->normalize_admin_autonomy_language_code( $language );
		$copy     = array(
			'assistant_name_fallback' => 'Assistant',
			'open_assistant'          => 'Open assistant',
			'setup_prefix'            => 'Set up ',
			'ask_prefix'              => 'Ask ',
			'send_button'             => 'Send',
			'wake_up'                 => 'Hey! Oh, you just woke me up, let me take a look around... so I am on this site, wow, this is my home. But I do not know anything about it, anything about you, or anything about myself yet.' . "\n\n" . 'Can you help me out? I have a ton of questions, but let me start with the most important one.' . "\n" . 'What is your name?',
			'help_prompt'             => 'How can I help you today?',
			'answer_placeholder'      => 'Type your answer...',
			'message_placeholder'     => 'Type your message...',
			'initial_suggestions'     => array(
				'Check site status',
				'Show active sessions',
				'What did you work on last?',
			),
		);

		$translations = array(
			'sr' => array(
				'assistant_name_fallback' => 'Asistent',
				'open_assistant'          => 'Otvori asistenta',
				'setup_prefix'            => 'Podesi ',
				'ask_prefix'              => 'Pitaj ',
				'send_button'             => 'Posalji',
				'wake_up'                 => 'Hej! Oh, upravo ste me probudili, cekajte da pogledam okolo... znaci ja sam na ovom sajtu, vau, znaci ovo je moj dom... Ali ne znam nista o njemu, nista o Vama, a ni o sebi jos uvek.' . "\n\n" . 'Mozete li da mi pomognete? Imam gomilu pitanja, ali pocecu od najvaznijeg. Kako glasi Vase ime?',
				'help_prompt'             => 'Kako mogu da ti pomognem danas?',
				'answer_placeholder'      => 'Napisi odgovor...',
				'message_placeholder'     => 'Napisi poruku...',
				'initial_suggestions'     => array(
					'Proveri status sajta',
					'Prikazi aktivne sesije',
					'Sta si poslednje radio?',
				),
			),
			'hr' => array(
				'assistant_name_fallback' => 'Asistent',
				'open_assistant'          => 'Otvori asistenta',
				'setup_prefix'            => 'Postavi ',
				'ask_prefix'              => 'Pitaj ',
				'send_button'             => 'Posalji',
				'wake_up'                 => 'Hej! Oh, upravo ste me probudili, cekajte da pogledam okolo... znaci ja sam na ovom sajtu, vau, ovo je moj dom... Ali ne znam nista o njemu, nista o Vama, a ni o sebi jos uvijek.' . "\n\n" . 'Mozete li mi pomoci? Imam hrpu pitanja, ali pocet cu od najvaznijeg. Kako se zovete?',
				'help_prompt'             => 'Kako ti mogu pomoci danas?',
				'answer_placeholder'      => 'Napisi odgovor...',
				'message_placeholder'     => 'Napisi poruku...',
				'initial_suggestions'     => array(
					'Provjeri status stranice',
					'Prikazi aktivne sesije',
					'Sto si zadnje radio?',
				),
			),
			'de' => array(
				'assistant_name_fallback' => 'Assistent',
				'open_assistant'          => 'Assistent oeffnen',
				'setup_prefix'            => 'Einrichten ',
				'ask_prefix'              => 'Frage ',
				'send_button'             => 'Senden',
				'wake_up'                 => 'Hey! Oh, du hast mich gerade geweckt, lass mich kurz umschauen... also bin ich auf dieser Website, wow, das ist mein Zuhause. Aber ich weiss noch nichts darueber, nichts ueber dich und noch nichts ueber mich selbst.' . "\n\n" . 'Kannst du mir helfen? Ich habe eine Menge Fragen, aber ich fange mit der wichtigsten an.' . "\n" . 'Wie heisst du?',
				'help_prompt'             => 'Wie kann ich dir heute helfen?',
				'answer_placeholder'      => 'Schreibe deine Antwort...',
				'message_placeholder'     => 'Schreibe deine Nachricht...',
				'initial_suggestions'     => array(
					'Website-Status pruefen',
					'Aktive Sitzungen anzeigen',
					'Woran hast du zuletzt gearbeitet?',
				),
			),
			'es' => array(
				'assistant_name_fallback' => 'Asistente',
				'open_assistant'          => 'Abrir asistente',
				'setup_prefix'            => 'Configura ',
				'ask_prefix'              => 'Pregunta a ',
				'send_button'             => 'Enviar',
				'wake_up'                 => 'Hey! Oh, acabas de despertarme, dejame mirar alrededor... entonces estoy en este sitio, wow, este es mi hogar. Pero todavia no se nada sobre el, nada sobre ti ni nada sobre mi.' . "\n\n" . 'Puedes ayudarme? Tengo un monton de preguntas, pero empezare por la mas importante.' . "\n" . 'Como te llamas?',
				'help_prompt'             => 'Como puedo ayudarte hoy?',
				'answer_placeholder'      => 'Escribe tu respuesta...',
				'message_placeholder'     => 'Escribe tu mensaje...',
				'initial_suggestions'     => array(
					'Comprobar estado del sitio',
					'Mostrar sesiones activas',
					'En que trabajaste ultimamente?',
				),
			),
			'fr' => array(
				'assistant_name_fallback' => 'Assistant',
				'open_assistant'          => 'Ouvrir l assistant',
				'setup_prefix'            => 'Configurer ',
				'ask_prefix'              => 'Demander a ',
				'send_button'             => 'Envoyer',
				'wake_up'                 => 'He ! Oh, tu viens de me reveiller, laisse-moi regarder autour de moi... donc je suis sur ce site, wow, c est ma maison. Mais je ne sais encore rien d elle, rien de toi, ni rien de moi-meme.' . "\n\n" . 'Peux-tu m aider ? J ai plein de questions, mais je vais commencer par la plus importante.' . "\n" . 'Comment tu t appelles ?',
				'help_prompt'             => 'Comment puis-je t aider aujourd hui ?',
				'answer_placeholder'      => 'Ecris ta reponse...',
				'message_placeholder'     => 'Ecris ton message...',
				'initial_suggestions'     => array(
					'Verifier l etat du site',
					'Afficher les sessions actives',
					'Sur quoi as-tu travaille en dernier ?',
				),
			),
		);

		if ( isset( $translations[ $language ] ) ) {
			$copy = array_merge( $copy, $translations[ $language ] );
		}

		return $copy;
	}

	private function get_admin_autonomy_launcher_language_sample() {
		$sample        = array();
		$front_page_id = intval( get_option( 'page_on_front' ) );

		if ( $front_page_id > 0 ) {
			$front_page = get_post( $front_page_id );
			if ( $front_page && $front_page instanceof WP_Post ) {
				$sample[] = $front_page->post_title;
				$sample[] = wp_strip_all_tags( $front_page->post_excerpt );
				$sample[] = wp_strip_all_tags( $front_page->post_content );
			}
		}

		if ( empty( array_filter( $sample ) ) && isset( $this->content_store ) && $this->content_store ) {
			if ( method_exists( $this->content_store, 'get_home_page_summary' ) ) {
				$sample[] = (string) $this->content_store->get_home_page_summary( 500 );
			}
			if ( method_exists( $this->content_store, 'get_navigation_summary' ) ) {
				$sample[] = (string) $this->content_store->get_navigation_summary();
			}
		}

		$sample[] = (string) get_bloginfo( 'description' );
		return trim( implode( ' ', array_filter( $sample ) ) );
	}

	public function render_admin_autonomy_launcher() {
		if ( ! $this->should_load_admin_autonomy_launcher() ) {
			return;
		}

		$config        = $this->build_admin_autonomy_launcher_config();
		$theme_mode    = isset( $config['chat_theme_mode'] ) ? (string) $config['chat_theme_mode'] : 'neutral';
		$placeholder   = isset( $config['message_placeholder'] ) ? (string) $config['message_placeholder'] : __( 'Napiši zadatak za Niwa AI...', 'niwa-ai' );
		$banner        = isset( $config['banner_message'] ) ? (string) $config['banner_message'] : __( 'Šta želiš da uradim u WordPress adminu?', 'niwa-ai' );
		$launcher_text = isset( $config['launcher_text'] ) ? (string) $config['launcher_text'] : __( 'Ask Niwa AI', 'niwa-ai' );
		$wordmark_url  = isset( $config['wordmark_url'] ) ? (string) $config['wordmark_url'] : NIWA_AI_URL . 'assets/img/niwa-wordmark.svg';
		$default_logo_url = defined( 'NIWA_AI_DEFAULT_BACKEND_AVATAR_URL' )
			? NIWA_AI_DEFAULT_BACKEND_AVATAR_URL
			: NIWA_AI_URL . 'assets/img/niwa-admin-personal-assistant.png';
		$logo_url         = isset( $config['avatar_url'] ) ? (string) $config['avatar_url'] : $default_logo_url;

		// Pre-calculate launcher pill width from the actual label text (which
		// is the backend agent's assistant_name once onboarding is complete)
		// so the admin pill paints at its final size on first frame — the
		// backend agent's name is accommodated without any flash or resize.
		$launcher_width_px = Niwa_AI_Chat_Renderer::estimate_launcher_pill_width( $launcher_text );
		$morph_style       = sprintf( '--niwa-launcher-width:%dpx;--niwa-launcher-height:48px;', $launcher_width_px );
		?>
		<div class="niwa-widget niwa-widget--floating niwa-widget--admin" data-niwa-theme="<?php echo esc_attr( $theme_mode ); ?>">
			<div class="niwa-morph niwa-morph--admin" id="niwaMorph"
				style="<?php echo esc_attr( $morph_style ); ?>"
				aria-label="<?php esc_attr_e( 'Open Niwa AI admin assistant', 'niwa-ai' ); ?>">
				<div class="niwa-morph__chat">
					<div class="niwa-morph__header">
						<img src="<?php echo esc_url( $wordmark_url ); ?>" alt="Niwa" class="niwa-morph__wordmark" />
						<button class="niwa-morph__close" aria-label="<?php esc_attr_e( 'Close', 'niwa-ai' ); ?>">
							<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
								<line x1="18" y1="6" x2="6" y2="18"></line>
								<line x1="6" y1="6" x2="18" y2="18"></line>
							</svg>
						</button>
					</div>
					<div class="niwa-morph__body" data-niwa-admin-chat="1" role="log" aria-live="polite" aria-relevant="additions">
						<div class="niwa-welcome">
							<div class="niwa-welcome__icon" aria-hidden="true">
								<img src="<?php echo esc_url( $logo_url ); ?>" alt="" width="64" height="64" />
							</div>
							<div class="niwa-welcome__text">
								<?php echo esc_html( $banner ); ?>
							</div>
						</div>
					</div>
				</div>

				<div class="niwa-morph__pill" id="niwaChathead" role="button" tabindex="0"
					data-launcher-text="<?php echo esc_attr( $launcher_text ); ?>"
					data-launcher-text-mobile="<?php echo esc_attr( $launcher_text ); ?>"
					data-launcher-width-desktop="<?php echo esc_attr( $launcher_width_px ); ?>"
					data-launcher-width-mobile="<?php echo esc_attr( $launcher_width_px ); ?>">
					<span class="niwa-morph__orb" aria-hidden="true">
						<span class="niwa-orb"></span>
					</span>
					<span class="niwa-morph__label"><?php echo esc_html( $launcher_text ); ?></span>
					<span class="niwa-morph__badge" id="niwaBadge"></span>

					<div class="niwa-morph__input">
						<textarea class="niwa-morph__textarea"
							id="niwaChatheadInput"
							name="message"
							placeholder="<?php echo esc_attr( $placeholder ); ?>"
							aria-label="<?php echo esc_attr( $placeholder ); ?>"
							rows="1"></textarea>
						<button class="niwa-morph__send niwa-morph__send--disabled"
							id="niwaChatheadSend"
							type="button"
							aria-label="<?php esc_attr_e( 'Send', 'niwa-ai' ); ?>">
							<svg width="32" height="32" viewBox="0 0 32 32" fill="none" aria-hidden="true">
								<path d="M16 22L16 10M16 10L11 15M16 10L21 15" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/>
							</svg>
						</button>
					</div>
				</div>
			</div>
		</div>
		<?php
	}

	public function ajax_admin_autonomy_chat() {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error(
				array(
					'error'   => __( 'Nemate dozvolu za admin asistenta.', 'niwa-ai' ),
					'message' => __( 'Nemate dozvolu za admin asistenta.', 'niwa-ai' ),
				),
				403
			);
		}

		// Keep this request short. The actual LLM turn is processed by a
		// signed background worker so wp-admin navigation cannot abort it.
		nocache_headers();
		if ( function_exists( 'session_status' ) && PHP_SESSION_ACTIVE === session_status() ) {
			@session_write_close(); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
		}

		$this->validate_admin_autonomy_ajax_nonce( array( 'nonce', '_ajax_nonce' ) );

		if ( ! $this->admin_autonomy || ! method_exists( $this->admin_autonomy, 'enqueue_browser_message' ) ) {
			wp_send_json_error(
				array(
					'error'   => __( 'Admin autonomy runtime nije dostupan.', 'niwa-ai' ),
					'message' => __( 'Admin autonomy runtime nije dostupan.', 'niwa-ai' ),
				),
				500
			);
		}

		$message    = isset( $_POST['message'] ) ? sanitize_textarea_field( wp_unslash( $_POST['message'] ) ) : '';
		$request_id = isset( $_POST['request_id'] ) ? sanitize_key( wp_unslash( $_POST['request_id'] ) ) : '';
		if ( '' === trim( $message ) ) {
			wp_send_json_error(
				array(
					'error'   => 'empty_message',
					'message' => __( 'Prazna poruka.', 'niwa-ai' ),
					'code'    => 'empty_message',
				),
				400
			);
		}

		$job = $this->find_or_enqueue_admin_ui_chat_job( get_current_user_id(), $message, $request_id );
		if ( ! empty( $job['conflict'] ) ) {
			wp_send_json_error(
				array(
					'error'   => 'admin_ui_request_message_conflict',
					'message' => __( 'Isti request ID je vec vezan za drugu poruku.', 'niwa-ai' ),
					'code'    => 'admin_ui_request_message_conflict',
				),
				409
			);
		}
		if ( ! empty( $job['queue_full'] ) ) {
			wp_send_json_error(
				array(
					'error'   => 'admin_ui_chat_queue_full',
					'message' => __( 'Admin chat red je pun. Sacekaj da se postojeci poslovi zavrse.', 'niwa-ai' ),
					'code'    => 'admin_ui_chat_queue_full',
				),
				429
			);
		}
		if ( empty( $job['id'] ) ) {
			wp_send_json_error(
				array(
					'error'   => 'admin_ui_chat_enqueue_failed',
					'message' => __( 'Admin chat posao nije mogao da se upise u queue.', 'niwa-ai' ),
					'code'    => 'admin_ui_chat_enqueue_failed',
				),
				500
			);
		}

		$handed_off = $this->handoff_admin_ui_chat_job( (string) $job['id'] );
		if ( ! $handed_off ) {
			$this->dispatch_admin_ui_chat_worker( 'admin-chat-submit-fallback' );
		}
		$this->maybe_start_admin_autonomy_background_clock( 'admin-ui-queue' );

		wp_send_json_success(
			array(
				'status'     => isset( $job['status'] ) ? (string) $job['status'] : 'pending',
				'job_id'     => (string) $job['id'],
				'request_id' => isset( $job['request_id'] ) ? (string) $job['request_id'] : '',
				'generation' => isset( $job['generation'] ) ? intval( $job['generation'] ) : 0,
				'session'    => isset( $job['session_id'] ) ? (string) $job['session_id'] : '',
				'message'    => isset( $job['message'] ) ? (string) $job['message'] : $message,
				'created_at' => isset( $job['created_at'] ) ? intval( $job['created_at'] ) : time(),
				'sequence'   => isset( $job['sequence'] ) ? intval( $job['sequence'] ) : 0,
			)
		);
	}

	public function ajax_admin_autonomy_history() {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error(
				array(
					'error'   => __( 'Nemate dozvolu za admin asistenta.', 'niwa-ai' ),
					'message' => __( 'Nemate dozvolu za admin asistenta.', 'niwa-ai' ),
				),
				403
			);
		}

		$this->validate_admin_autonomy_ajax_nonce( 'nonce' );

		if ( ! $this->admin_autonomy || ! method_exists( $this->admin_autonomy, 'get_browser_display_history' ) ) {
			wp_send_json_error(
				array(
					'error'   => __( 'Admin autonomy istorija nije dostupna.', 'niwa-ai' ),
					'message' => __( 'Admin autonomy istorija nije dostupna.', 'niwa-ai' ),
				),
				500
			);
		}

		$mode      = isset( $_POST['mode'] ) ? sanitize_key( wp_unslash( $_POST['mode'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$is_cursor = in_array( $mode, array( 'initial', 'older', 'tail' ), true );
		$limit     = isset( $_POST['limit'] ) ? absint( wp_unslash( $_POST['limit'] ) ) : ( $is_cursor ? 30 : 60 ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$limit     = $is_cursor ? min( 50, max( 10, $limit ) ) : min( 120, max( 20, $limit ) );
		$before_ts = isset( $_POST['before_ts'] ) ? absint( wp_unslash( $_POST['before_ts'] ) ) : 0; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$since_ts  = isset( $_POST['since_ts'] ) ? absint( wp_unslash( $_POST['since_ts'] ) ) : 0; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$before_id = isset( $_POST['before_id'] ) ? sanitize_text_field( wp_unslash( $_POST['before_id'] ) ) : '';
		$since_id  = isset( $_POST['since_id'] ) ? sanitize_text_field( wp_unslash( $_POST['since_id'] ) ) : '';

		$history = (array) $this->admin_autonomy->get_browser_display_history(
			get_current_user_id(),
			array(
				'limit'     => $is_cursor ? $limit + 1 : $limit,
				'before_ts' => $before_ts,
				'since_ts'  => $since_ts,
				'before_id' => $before_id,
				'since_id'  => $since_id,
				'channel'   => 'admin_ui',
			)
		);

		if ( $is_cursor ) {
			$has_more = count( $history ) > $limit;
			if ( $has_more ) {
				$history = array_slice( $history, -$limit );
			}

			wp_send_json_success(
				array(
					'history'   => $history,
					'has_more'  => $has_more && 'tail' !== $mode,
					'oldest_ts' => $this->get_admin_autonomy_history_edge_ts( $history, 'oldest' ),
					'newest_ts' => $this->get_admin_autonomy_history_edge_ts( $history, 'newest' ),
					'oldest_id' => $this->get_admin_autonomy_history_edge_id( $history, 'oldest' ),
					'newest_id' => $this->get_admin_autonomy_history_edge_id( $history, 'newest' ),
				)
			);
		}

		wp_send_json_success(
			array(
				'history' => $history,
			)
		);
	}

	private function get_admin_autonomy_history_edge_ts( $history, $edge ) {
		$history = is_array( $history ) ? array_values( $history ) : array();
		if ( empty( $history ) ) {
			return 0;
		}

		$item = 'newest' === $edge ? $history[ count( $history ) - 1 ] : $history[0];
		if ( ! is_array( $item ) ) {
			return 0;
		}

		if ( isset( $item['ts'] ) ) {
			return absint( $item['ts'] );
		}

		return isset( $item['timestamp'] ) ? absint( $item['timestamp'] ) : 0;
	}

	private function get_admin_autonomy_history_edge_id( $history, $edge ) {
		$history = is_array( $history ) ? array_values( $history ) : array();
		if ( empty( $history ) ) {
			return '';
		}

		$item = 'newest' === $edge ? $history[ count( $history ) - 1 ] : $history[0];
		return is_array( $item ) && isset( $item['id'] ) ? (string) $item['id'] : '';
	}

	private function validate_admin_autonomy_ajax_nonce( $request_key = '_ajax_nonce' ) {
		$request_keys = is_array( $request_key ) ? $request_key : array( $request_key );
		foreach ( $request_keys as $key ) {
			$key = is_string( $key ) ? $key : '';
			if ( '' !== $key && false !== check_ajax_referer( 'niwa_ai_admin_autonomy_chat_nonce', $key, false ) ) {
				return true;
			}
		}

		wp_send_json_error(
			array(
				'error'            => __( 'Admin stranica je istekla. Osvezite stranicu pa pokusajte ponovo.', 'niwa-ai' ),
				'message'          => __( 'Admin stranica je istekla. Osvezite stranicu pa pokusajte ponovo.', 'niwa-ai' ),
				'refresh_required' => true,
				'code'             => 'admin_autonomy_nonce_expired',
			),
			403
		);

		return false;
	}
}
