<?php

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

class Niwa_AI_Admin_Autonomy_Settings {
    private $store;
    private $telegram;
    private $whatsapp;
    private $email_service;
    private $model_catalog;
    private $performance_settings_ui;

    public function __construct( $store, $telegram, $whatsapp = null ) {
        $this->store    = $store;
        $this->telegram = $telegram;
        $this->whatsapp = $whatsapp;
    }

    public function set_email_service( $email_service ) {
        $this->email_service = $email_service;
    }

    public function set_model_catalog( $model_catalog ) {
        $this->model_catalog = $model_catalog;
    }

    public function set_performance_settings_ui( $performance_settings_ui ) {
        $this->performance_settings_ui = $performance_settings_ui;
    }

    public function register_hooks() {
        add_action( 'admin_menu', array( $this, 'add_submenu' ) );
        add_action( 'admin_init', array( $this, 'register_settings' ) );
        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
        add_action( 'admin_post_niwa_ai_admin_autonomy_disable_operative_heartbeat', array( $this, 'handle_disable_operative_heartbeat' ) );
        add_action( 'wp_ajax_niwa_ai_whatsapp_connect', array( $this, 'ajax_whatsapp_connect' ) );
        add_action( 'wp_ajax_niwa_ai_whatsapp_status', array( $this, 'ajax_whatsapp_status' ) );
        add_action( 'wp_ajax_niwa_ai_whatsapp_disconnect', array( $this, 'ajax_whatsapp_disconnect' ) );
    }

    public function enqueue_admin_assets( $hook_suffix ) {
        if ( 'niwa-ai_page_niwa-ai-admin-autonomy' !== $hook_suffix ) {
            return;
        }

        wp_enqueue_media();
        $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,
        ) );

        $whatsapp_access_js_path    = NIWA_AI_DIR . 'assets/js/niwa-admin-whatsapp-access.js';
        $whatsapp_access_js_version = file_exists( $whatsapp_access_js_path ) ? (string) filemtime( $whatsapp_access_js_path ) : NIWA_AI_VERSION;
        wp_enqueue_script(
            'niwa-admin-whatsapp-access',
            NIWA_AI_URL . 'assets/js/niwa-admin-whatsapp-access.js',
            array(),
            $whatsapp_access_js_version,
            array( 'in_footer' => true )
        );
    }

    public function add_submenu() {
        $title = $this->get_personal_assistant_title();

        add_submenu_page(
            'niwa-ai',
            $title,
            $title,
            'manage_options',
            'niwa-ai-admin-autonomy',
            array( $this, 'render_page' )
        );
    }

    private function get_personal_assistant_title() {
        $state = get_option( Niwa_AI_Constants::OPT_AUTONOMY_WORKSPACE_STATE, array() );
        $state = is_array( $state ) ? $state : array();
        $profile = isset( $state['identity_profile'] ) && is_array( $state['identity_profile'] )
            ? $state['identity_profile']
            : array();
        $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 get_operative_heartbeat_runtime_status( $role ) {
        $role = 'stock_manager' === sanitize_key( (string) $role ) ? 'stock_manager' : 'admin';

        $config_option = 'stock_manager' === $role
            ? Niwa_AI_Constants::OPT_AUTONOMY_STOCK_OPERATIVE_HB_CONFIG
            : Niwa_AI_Constants::OPT_AUTONOMY_OPERATIVE_HB_CONFIG;
        $state_option = 'stock_manager' === $role
            ? Niwa_AI_Constants::OPT_AUTONOMY_STOCK_OPERATIVE_HB_STATE
            : Niwa_AI_Constants::OPT_AUTONOMY_OPERATIVE_HB_STATE;
        $brief_file = 'stock_manager' === $role ? 'STOCK_OPERATIVE_HEARTBEAT.md' : 'OPERATIVE_HEARTBEAT.md';

        $config = get_option( $config_option, array() );
        $state  = get_option( $state_option, array() );
        $config = is_array( $config ) ? $config : array();
        $state  = is_array( $state ) ? $state : array();

        $brief = '';
        if ( class_exists( 'Niwa_AI_Admin_Autonomy_Workspace' ) ) {
            $workspace = new Niwa_AI_Admin_Autonomy_Workspace();
            $record    = $workspace->read_bootstrap_file( $brief_file );
            if ( ! empty( $record['success'] ) && ! empty( $record['content'] ) ) {
                $brief = trim( (string) $record['content'] );
            }
        }

        if ( false !== strpos( $brief, '<!-- No ' ) && false === strpos( $brief, '## Active Task' ) ) {
            $brief = '';
        }

        return array(
            'role'             => $role,
            'label'            => 'stock_manager' === $role ? __( 'Stock Operative Heartbeat', 'niwa-ai' ) : __( 'Admin Operative Heartbeat', 'niwa-ai' ),
            'brief_file'       => $brief_file,
            'enabled'          => ! empty( $config['enabled'] ),
            'interval_minutes' => max( 0.5, round( floatval( isset( $config['interval_minutes'] ) ? $config['interval_minutes'] : 60 ), 2 ) ),
            'delivery_channel' => isset( $config['delivery_channel'] ) ? sanitize_key( (string) $config['delivery_channel'] ) : 'telegram',
            'last_run_at'      => intval( isset( $state['last_run_at'] ) ? $state['last_run_at'] : 0 ),
            'last_attempt_at'  => intval( isset( $state['last_attempt_at'] ) ? $state['last_attempt_at'] : 0 ),
            'last_sent_at'     => intval( isset( $state['last_sent_at'] ) ? $state['last_sent_at'] : 0 ),
            'last_status'      => isset( $state['last_status'] ) ? (string) $state['last_status'] : '',
            'last_summary'     => isset( $state['last_summary'] ) ? (string) $state['last_summary'] : '',
            'run_count'        => intval( isset( $state['run_count'] ) ? $state['run_count'] : 0 ),
            'brief_summary'    => $this->truncate_runtime_text( $brief, 360 ),
        );
    }

    private function truncate_runtime_text( $text, $limit = 240 ) {
        $text  = trim( wp_strip_all_tags( (string) $text ) );
        $limit = max( 40, intval( $limit ) );
        if ( '' === $text || mb_strlen( $text ) <= $limit ) {
            return $text;
        }

        return rtrim( mb_substr( $text, 0, $limit - 3 ) ) . '...';
    }

    private function get_active_scheduled_jobs_runtime_status() {
        if ( ! class_exists( 'Niwa_AI_Admin_Autonomy_Scheduler' ) ) {
            return array();
        }

        $scheduler = new Niwa_AI_Admin_Autonomy_Scheduler();
        if ( ! method_exists( $scheduler, 'list_jobs' ) ) {
            return array();
        }

        return (array) $scheduler->list_jobs( '', 'active', 6 );
    }

    private function get_prompt_cache_runtime_status() {
        $state = get_option( Niwa_AI_Constants::OPT_AUTONOMY_PROMPT_CACHE_USAGE, array() );
        $state = is_array( $state ) ? $state : array();

        $last = isset( $state['last'] ) && is_array( $state['last'] ) ? $state['last'] : array();

        return array(
            'has_samples'              => ! empty( $last ),
            'last'                     => $last,
            'sample_count'             => max( 0, intval( isset( $state['sample_count'] ) ? $state['sample_count'] : 0 ) ),
            'aggregate_prompt_tokens'  => max( 0, intval( isset( $state['aggregate_prompt_tokens'] ) ? $state['aggregate_prompt_tokens'] : 0 ) ),
            'aggregate_cached_tokens'  => max( 0, intval( isset( $state['aggregate_cached_tokens'] ) ? $state['aggregate_cached_tokens'] : 0 ) ),
            'aggregate_cache_hit_rate' => isset( $state['aggregate_cache_hit_rate'] ) ? max( 0.0, min( 1.0, (float) $state['aggregate_cache_hit_rate'] ) ) : 0.0,
            'updated_at'               => intval( isset( $state['updated_at'] ) ? $state['updated_at'] : 0 ),
        );
    }

    private function format_prompt_cache_rate( $rate ) {
        $rate = max( 0.0, min( 1.0, (float) $rate ) );
        return number_format_i18n( $rate * 100, 1 ) . '%';
    }

    private function format_token_count( $tokens ) {
        $tokens = max( 0, intval( $tokens ) );
        return number_format_i18n( $tokens );
    }

    public function register_settings() {
        register_setting(
            'niwa_ai_admin_autonomy_group',
            Niwa_AI_Admin_Autonomy_Telegram::OPTION_KEY,
            array( $this, 'sanitize' )
        );
        register_setting(
            'niwa_ai_admin_autonomy_group',
            Niwa_AI_Constants::OPT_AUTONOMY_HEARTBEAT_CONFIG,
            array( $this, 'sanitize_heartbeat_config' )
        );
        if ( $this->email_service ) {
            register_setting(
                'niwa_ai_admin_autonomy_group',
                Niwa_AI_Admin_Autonomy_Email::OPTION_KEY,
                array( $this, 'sanitize_email_settings' )
            );
        }
    }

    public function sanitize( $input ) {
        $defaults = $this->telegram->get_defaults();
        $stored   = get_option( Niwa_AI_Admin_Autonomy_Telegram::OPTION_KEY, array() );
        $out      = wp_parse_args( is_array( $stored ) ? $stored : array(), $defaults );

        if ( ! is_array( $input ) ) {
            return $out;
        }

        $out['enabled']           = isset( $input['enabled'] ) ? 1 : 0;
        $out['transport']         = 'webhook';
        $raw_token                = isset( $input['bot_token'] ) ? sanitize_text_field( $input['bot_token'] ) : '';
        $clear_bot_token          = ! empty( $input['clear_bot_token'] );
        if ( $clear_bot_token || empty( $out['enabled'] ) ) {
            $out['bot_token'] = '';
        } elseif ( '' !== $raw_token ) {
            if ( class_exists( 'Niwa_AI_Admin_Autonomy_Telegram' ) && Niwa_AI_Admin_Autonomy_Telegram::is_token_encrypted( $raw_token ) ) {
                $out['bot_token'] = $raw_token;
            } elseif ( class_exists( 'Niwa_AI_Admin_Autonomy_Telegram' ) && Niwa_AI_Admin_Autonomy_Telegram::is_valid_bot_token( $raw_token ) ) {
                $out['bot_token'] = Niwa_AI_Admin_Autonomy_Telegram::encrypt_token( $raw_token );
            } elseif ( ! class_exists( 'Niwa_AI_Admin_Autonomy_Telegram' ) ) {
                $out['bot_token'] = $raw_token;
            }
        }
        $out['owner_chat_id']     = isset( $input['owner_chat_id'] ) ? preg_replace( '/[^0-9\\-]/', '', (string) $input['owner_chat_id'] ) : '';
        $out['founder_notes_seed'] = isset( $input['founder_notes_seed'] ) ? sanitize_textarea_field( $input['founder_notes_seed'] ) : '';
        $founder_auth_mode        = isset( $input['founder_auth_mode'] ) ? sanitize_key( (string) $input['founder_auth_mode'] ) : '';
        $out['founder_auth_mode'] = in_array( $founder_auth_mode, array( 'api_key', 'oauth' ), true ) ? $founder_auth_mode : '';
        $out['founder_agent_model'] = $this->sanitize_model_id( isset( $input['founder_agent_model'] ) ? $input['founder_agent_model'] : '' );
        $out['founder_subagent_model'] = $this->sanitize_model_id( isset( $input['founder_subagent_model'] ) ? $input['founder_subagent_model'] : '' );
        $out['founder_image_quality'] = $this->sanitize_image_quality( isset( $input['founder_image_quality'] ) ? $input['founder_image_quality'] : ( isset( $defaults['founder_image_quality'] ) ? $defaults['founder_image_quality'] : 'medium' ) );
        $out['rate_limit_max']    = isset( $input['rate_limit_max'] ) ? max( 1, min( 120, intval( $input['rate_limit_max'] ) ) ) : intval( $defaults['rate_limit_max'] );
        $out['rate_limit_window'] = isset( $input['rate_limit_window'] ) ? max( 10, min( 3600, intval( $input['rate_limit_window'] ) ) ) : intval( $defaults['rate_limit_window'] );
        $out['poll_timeout']      = isset( $input['poll_timeout'] ) ? max( 1, min( 25, intval( $input['poll_timeout'] ) ) ) : intval( $defaults['poll_timeout'] );
        $out['poll_limit']        = isset( $input['poll_limit'] ) ? max( 1, min( 50, intval( $input['poll_limit'] ) ) ) : intval( $defaults['poll_limit'] );
        $out['daily_report_enabled'] = isset( $input['daily_report_enabled'] ) ? 1 : 0;
        $out['daily_report_hour']    = isset( $input['daily_report_hour'] ) ? max( 0, min( 23, intval( $input['daily_report_hour'] ) ) ) : intval( $defaults['daily_report_hour'] );
        $out['daily_report_minute']  = isset( $input['daily_report_minute'] ) ? max( 0, min( 59, intval( $input['daily_report_minute'] ) ) ) : intval( $defaults['daily_report_minute'] );
        $out['show_tooluse_in_telegram_chat'] = isset( $input['show_tooluse_in_telegram_chat'] ) ? 1 : 0;
        $admin_launcher_scope        = isset( $input['admin_launcher_scope'] ) ? sanitize_key( (string) $input['admin_launcher_scope'] ) : (string) $defaults['admin_launcher_scope'];
        $out['admin_launcher_scope'] = in_array( $admin_launcher_scope, array( 'dashboard', 'niwa_dashboard', 'everywhere' ), true )
            ? $admin_launcher_scope
            : (string) $defaults['admin_launcher_scope'];

        // Stock managers.
        $out['stock_managers'] = $this->sanitize_stock_managers( $input );

        // Unauthorized intro auto-reply.
        $out['unauthorized_intro_enabled']  = isset( $input['unauthorized_intro_enabled'] ) ? 1 : 0;
        $out['unauthorized_intro_template'] = isset( $input['unauthorized_intro_template'] )
            ? sanitize_textarea_field( (string) $input['unauthorized_intro_template'] )
            : '';

        $out['whatsapp'] = $this->sanitize_whatsapp_settings( isset( $input['whatsapp'] ) ? $input['whatsapp'] : array(), isset( $stored['whatsapp'] ) && is_array( $stored['whatsapp'] ) ? $stored['whatsapp'] : array() );

        if ( function_exists( 'niwa_ai_queue_admin_autonomy_poll_restart' ) ) {
            niwa_ai_queue_admin_autonomy_poll_restart( 'settings-save' );
        }

        return $out;
    }

    public function sanitize_heartbeat_config( $input ) {
        $existing = get_option( Niwa_AI_Constants::OPT_AUTONOMY_HEARTBEAT_CONFIG, array() );
        $existing = is_array( $existing ) ? $existing : array();
        $out      = wp_parse_args( $existing, $this->get_default_heartbeat_config() );

        if ( is_array( $input ) ) {
            if ( array_key_exists( 'enabled', $input ) ) {
                $out['paused'] = empty( $input['enabled'] );
            }

            if ( array_key_exists( 'paused', $input ) ) {
                $out['paused'] = ! empty( $input['paused'] );
            }

            $out['engagement_enabled'] = false;
        }

        $hours = isset( $out['scheduled_hours'] ) && is_array( $out['scheduled_hours'] )
            ? $out['scheduled_hours']
            : array( 14 );
        $hours = array_values( array_unique( array_map( 'intval', array_filter( $hours, function ( $hour ) {
            return is_numeric( $hour ) && intval( $hour ) >= 0 && intval( $hour ) <= 23;
        } ) ) ) );
        sort( $hours );
        if ( empty( $hours ) ) {
            $hours = array( 14 );
        }

        return array(
            'scheduled_hours'    => $hours,
            'blog_per_day'       => max( 0, min( 3, intval( isset( $out['blog_per_day'] ) ? $out['blog_per_day'] : 1 ) ) ),
            'paused'             => ! empty( $out['paused'] ),
            'engagement_enabled' => false,
            'task_log'           => isset( $out['task_log'] ) && is_array( $out['task_log'] ) ? array_slice( $out['task_log'], -30 ) : array(),
            '_configured'        => true,
        );
    }

    private function get_effective_heartbeat_config() {
        $stored = get_option( Niwa_AI_Constants::OPT_AUTONOMY_HEARTBEAT_CONFIG, array() );
        return wp_parse_args( is_array( $stored ) ? $stored : array(), $this->get_default_heartbeat_config() );
    }

    private function get_default_heartbeat_config() {
        return array(
            'scheduled_hours'    => array( 14 ),
            'blog_per_day'       => 1,
            'paused'             => true,
            'engagement_enabled' => false,
            'task_log'           => array(),
        );
    }

    private function sanitize_whatsapp_settings( $input, $stored ) {
        $defaults = $this->whatsapp && method_exists( $this->whatsapp, 'get_defaults' )
            ? $this->whatsapp->get_defaults()
            : array(
                'enabled'             => 0,
                'admin_numbers'       => array(),
                'stock_manager_numbers' => array(),
                'lead_numbers'        => array(),
                'admin_group_jids'    => array(),
                'stock_manager_group_jids' => array(),
                'lead_group_jids'     => array(),
                'node_path'           => '',
                'gateway_mode'        => 'auto',
                'gateway_secret'      => '',
            );
        $out   = wp_parse_args( is_array( $stored ) ? $stored : array(), $defaults );
        $input = is_array( $input ) ? $input : array();

        $out['enabled'] = isset( $input['enabled'] ) ? 1 : 0;
        $out['admin_numbers'] = $this->sanitize_whatsapp_access_number_input(
            $input,
            'admin_numbers',
            isset( $out['admin_numbers'] ) ? $out['admin_numbers'] : array()
        );
        $out['stock_manager_numbers'] = $this->sanitize_whatsapp_access_number_input(
            $input,
            'stock_manager_numbers',
            isset( $out['stock_manager_numbers'] ) ? $out['stock_manager_numbers'] : array()
        );
        $out['lead_numbers'] = $this->sanitize_whatsapp_access_number_input(
            $input,
            'lead_numbers',
            isset( $out['lead_numbers'] ) ? $out['lead_numbers'] : array()
        );
        $out['admin_group_jids'] = $this->sanitize_whatsapp_access_group_input(
            $input,
            'admin_group_jids',
            isset( $out['admin_group_jids'] ) ? $out['admin_group_jids'] : array()
        );
        $out['stock_manager_group_jids'] = $this->sanitize_whatsapp_access_group_input(
            $input,
            'stock_manager_group_jids',
            isset( $out['stock_manager_group_jids'] ) ? $out['stock_manager_group_jids'] : array()
        );
        $out['lead_group_jids'] = $this->sanitize_whatsapp_access_group_input(
            $input,
            'lead_group_jids',
            isset( $out['lead_group_jids'] ) ? $out['lead_group_jids'] : array()
        );
        $out['node_path']         = '';
        $out['gateway_mode']      = 'auto';
        $out['gateway_secret']    = isset( $out['gateway_secret'] ) ? sanitize_text_field( (string) $out['gateway_secret'] ) : '';
        $out                      = array_intersect_key( $out, $defaults );
        if ( $this->whatsapp && method_exists( $this->whatsapp, 'enforce_number_role_precedence' ) ) {
            $out = $this->whatsapp->enforce_number_role_precedence( $out );
        }
        if ( '' !== $out['gateway_secret'] && $this->whatsapp && method_exists( $this->whatsapp, 'sync_gateway_secret_file' ) ) {
            $this->whatsapp->sync_gateway_secret_file( $out['gateway_secret'] );
        }
        if ( $this->whatsapp && method_exists( $this->whatsapp, 'sync_access_policy_file' ) ) {
            $this->whatsapp->sync_access_policy_file( $out );
        }

        return $out;
    }

    private function sanitize_whatsapp_access_number_input( $input, $key, $fallback = array() ) {
        $input     = is_array( $input ) ? $input : array();
        $submitted = ! empty( $input[ $key . '_submitted' ] );
        if ( $submitted || ( isset( $input[ $key ] ) && is_array( $input[ $key ] ) ) ) {
            $entries = isset( $input[ $key ] ) && is_array( $input[ $key ] ) ? $input[ $key ] : array();
            return $this->whatsapp && method_exists( $this->whatsapp, 'sanitize_access_number_entries' )
                ? $this->whatsapp->sanitize_access_number_entries( $entries )
                : $this->sanitize_whatsapp_access_number_lines( $this->format_whatsapp_access_number_lines( $entries ), array() );
        }

        $text_key = $key . '_text';
        return $this->sanitize_whatsapp_access_number_lines(
            isset( $input[ $text_key ] ) ? (string) $input[ $text_key ] : null,
            $fallback
        );
    }

    private function sanitize_whatsapp_access_number_lines( $text, $fallback = array() ) {
        if ( null === $text ) {
            return $this->whatsapp && method_exists( $this->whatsapp, 'sanitize_access_number_entries' )
                ? $this->whatsapp->sanitize_access_number_entries( $fallback )
                : ( is_array( $fallback ) ? $fallback : array() );
        }

        if ( $this->whatsapp && method_exists( $this->whatsapp, 'parse_access_number_lines' ) ) {
            return $this->whatsapp->parse_access_number_lines( $text );
        }

        $entries = array();
        foreach ( preg_split( '/\r\n|\r|\n/', (string) $text ) as $line ) {
            $line = trim( (string) $line );
            if ( '' === $line ) {
                continue;
            }
            $label = '';
            $phone = $line;
            if ( false !== strpos( $line, '|' ) ) {
                $parts = array_map( 'trim', explode( '|', $line, 2 ) );
                $label = isset( $parts[0] ) ? sanitize_text_field( $parts[0] ) : '';
                $phone = isset( $parts[1] ) ? $parts[1] : '';
            }
            $phone = '*' === trim( (string) $phone ) ? '*' : preg_replace( '/[^0-9+]/', '', (string) $phone );
            if ( 0 === strpos( $phone, '+' ) ) {
                $phone = substr( $phone, 1 );
            }
            if ( '' !== $phone ) {
                $entries[] = array(
                    'phone' => $phone,
                    'label' => $label,
                );
            }
        }

        return empty( $entries ) ? array() : $entries;
    }

    private function format_whatsapp_access_number_lines( $entries ) {
        if ( $this->whatsapp && method_exists( $this->whatsapp, 'sanitize_access_number_entries' ) ) {
            $entries = $this->whatsapp->sanitize_access_number_entries( $entries );
        } else {
            $entries = is_array( $entries ) ? $entries : array();
        }

        $lines = array();
        foreach ( $entries as $entry ) {
            $phone = isset( $entry['phone'] ) && '*' === trim( (string) $entry['phone'] )
                ? '*'
                : ( isset( $entry['phone'] ) ? preg_replace( '/[^0-9]/', '', (string) $entry['phone'] ) : '' );
            if ( '' === $phone ) {
                continue;
            }
            $label = isset( $entry['label'] ) ? trim( (string) $entry['label'] ) : '';
            $lines[] = '*' === $phone
                ? ( '' !== $label ? $label . ' | *' : '*' )
                : ( '' !== $label ? $label . ' | +' . $phone : '+' . $phone );
        }

        return implode( "\n", $lines );
    }

    private function sanitize_whatsapp_access_group_input( $input, $key, $fallback = array() ) {
        $input     = is_array( $input ) ? $input : array();
        $submitted = ! empty( $input[ $key . '_submitted' ] );
        if ( $submitted || ( isset( $input[ $key ] ) && is_array( $input[ $key ] ) ) ) {
            $entries = isset( $input[ $key ] ) && is_array( $input[ $key ] ) ? $input[ $key ] : array();
            return $this->whatsapp && method_exists( $this->whatsapp, 'sanitize_access_group_entries' )
                ? $this->whatsapp->sanitize_access_group_entries( $entries )
                : $this->sanitize_whatsapp_access_group_entries_locally( $entries );
        }

        return $this->whatsapp && method_exists( $this->whatsapp, 'sanitize_access_group_entries' )
            ? $this->whatsapp->sanitize_access_group_entries( $fallback )
            : $this->sanitize_whatsapp_access_group_entries_locally( $fallback );
    }

    private function sanitize_whatsapp_access_group_entries_locally( $entries ) {
        $entries = is_array( $entries ) ? $entries : array();
        $out     = array();
        $seen    = array();
        foreach ( $entries as $entry ) {
            if ( is_array( $entry ) ) {
                $jid   = isset( $entry['jid'] ) ? (string) $entry['jid'] : '';
                $label = isset( $entry['label'] ) ? (string) $entry['label'] : '';
            } else {
                $jid   = (string) $entry;
                $label = '';
            }

            $jid = preg_match( '/^([0-9]+)@g\.us$/', trim( $jid ), $matches )
                ? $matches[1] . '@g.us'
                : ( preg_match( '/^[0-9]{6,}$/', trim( $jid ) ) ? trim( $jid ) . '@g.us' : '' );
            if ( '' === $jid || isset( $seen[ $jid ] ) ) {
                continue;
            }
            $seen[ $jid ] = true;
            $out[] = array(
                'jid'   => $jid,
                'label' => sanitize_text_field( mb_substr( $label, 0, 100 ) ),
            );
        }

        return $out;
    }

    private function render_whatsapp_access_group( $option_key, $group_key, $title, $hint, $add_label, $entries ) {
        $entries = $this->whatsapp && method_exists( $this->whatsapp, 'sanitize_access_number_entries' )
            ? $this->whatsapp->sanitize_access_number_entries( $entries )
            : ( is_array( $entries ) ? $entries : array() );

        echo '<section class="niwa-wa-access__group" data-niwa-wa-access-group="' . esc_attr( $group_key ) . '">';
        echo '<input type="hidden" name="' . esc_attr( $option_key . '[whatsapp][' . $group_key . '_submitted]' ) . '" value="1" />';
        echo '<div class="niwa-wa-access__head">';
        echo '<div>';
        echo '<h3 class="niwa-wa-access__title">' . esc_html( $title ) . '</h3>';
        echo '<p class="niwa-wa-access__hint">' . esc_html( $hint ) . '</p>';
        echo '</div>';
        echo '<span class="niwa-wa-access__count" data-niwa-wa-access-count>' . esc_html( (string) count( $entries ) ) . '</span>';
        echo '</div>';
        echo '<div class="niwa-wa-access__rows" data-niwa-wa-access-rows>';
        foreach ( $entries as $index => $entry ) {
            $this->render_whatsapp_access_row( $option_key, $group_key, $index, $entry );
        }
        echo '</div>';
        echo '<p class="niwa-wa-access__empty" data-niwa-wa-access-empty' . ( empty( $entries ) ? '' : ' hidden' ) . '>' . esc_html__( 'No numbers added yet.', 'niwa-ai' ) . '</p>';
        echo '<button type="button" class="button niwa-btn niwa-btn--secondary niwa-wa-access__add" data-niwa-wa-access-add><span aria-hidden="true">+</span> ' . esc_html( $add_label ) . '</button>';
        echo '<template data-niwa-wa-access-template>';
        $this->render_whatsapp_access_row( $option_key, $group_key, '__INDEX__', array() );
        echo '</template>';
        echo '</section>';
    }

    private function render_whatsapp_group_access_group( $option_key, $group_key, $title, $hint, $add_label, $entries ) {
        $entries = $this->whatsapp && method_exists( $this->whatsapp, 'sanitize_access_group_entries' )
            ? $this->whatsapp->sanitize_access_group_entries( $entries )
            : $this->sanitize_whatsapp_access_group_entries_locally( $entries );

        echo '<section class="niwa-wa-access__group" data-niwa-wa-access-group="' . esc_attr( $group_key ) . '">';
        echo '<input type="hidden" name="' . esc_attr( $option_key . '[whatsapp][' . $group_key . '_submitted]' ) . '" value="1" />';
        echo '<div class="niwa-wa-access__head">';
        echo '<div>';
        echo '<h3 class="niwa-wa-access__title">' . esc_html( $title ) . '</h3>';
        echo '<p class="niwa-wa-access__hint">' . esc_html( $hint ) . '</p>';
        echo '</div>';
        echo '<span class="niwa-wa-access__count" data-niwa-wa-access-count>' . esc_html( (string) count( $entries ) ) . '</span>';
        echo '</div>';
        echo '<div class="niwa-wa-access__rows" data-niwa-wa-access-rows>';
        foreach ( $entries as $index => $entry ) {
            $this->render_whatsapp_group_access_row( $option_key, $group_key, $index, $entry );
        }
        echo '</div>';
        echo '<p class="niwa-wa-access__empty" data-niwa-wa-access-empty' . ( empty( $entries ) ? '' : ' hidden' ) . '>' . esc_html__( 'No groups added yet.', 'niwa-ai' ) . '</p>';
        echo '<button type="button" class="button niwa-btn niwa-btn--secondary niwa-wa-access__add" data-niwa-wa-access-add><span aria-hidden="true">+</span> ' . esc_html( $add_label ) . '</button>';
        echo '<template data-niwa-wa-access-template>';
        $this->render_whatsapp_group_access_row( $option_key, $group_key, '__INDEX__', array() );
        echo '</template>';
        echo '</section>';
    }

    private function render_whatsapp_access_row( $option_key, $group_key, $index, $entry ) {
        $entry = is_array( $entry ) ? $entry : array();
        $label = isset( $entry['label'] ) ? (string) $entry['label'] : '';
        $phone = isset( $entry['phone'] ) && '*' === trim( (string) $entry['phone'] )
            ? '*'
            : ( isset( $entry['phone'] ) ? preg_replace( '/[^0-9+]/', '', (string) $entry['phone'] ) : '' );
        if ( '' !== $phone && '*' !== $phone && 0 !== strpos( $phone, '+' ) ) {
            $phone = '+' . $phone;
        }

        $base = $option_key . '[whatsapp][' . $group_key . '][' . $index . ']';
        echo '<div class="niwa-wa-access__row" data-niwa-wa-access-row>';
        echo '<label class="niwa-wa-access__field">';
        echo '<span>' . esc_html__( 'Label', 'niwa-ai' ) . '</span>';
        echo '<input type="text" name="' . esc_attr( $base . '[label]' ) . '" value="' . esc_attr( $label ) . '" placeholder="' . esc_attr__( 'e.g. Store owner', 'niwa-ai' ) . '" />';
        echo '</label>';
        echo '<label class="niwa-wa-access__field">';
        echo '<span>' . esc_html__( 'Phone', 'niwa-ai' ) . '</span>';
        echo '<input type="tel" name="' . esc_attr( $base . '[phone]' ) . '" value="' . esc_attr( $phone ) . '" placeholder="' . esc_attr__( '+381...', 'niwa-ai' ) . '" />';
        echo '</label>';
        echo '<button type="button" class="niwa-wa-access__remove" data-niwa-wa-access-remove aria-label="' . esc_attr__( 'Remove number', 'niwa-ai' ) . '" title="' . esc_attr__( 'Remove', 'niwa-ai' ) . '">&times;</button>';
        echo '</div>';
    }

    private function render_whatsapp_group_access_row( $option_key, $group_key, $index, $entry ) {
        $entry = is_array( $entry ) ? $entry : array();
        $label = isset( $entry['label'] ) ? (string) $entry['label'] : '';
        $jid   = isset( $entry['jid'] ) ? (string) $entry['jid'] : '';

        $base = $option_key . '[whatsapp][' . $group_key . '][' . $index . ']';
        echo '<div class="niwa-wa-access__row" data-niwa-wa-access-row>';
        echo '<label class="niwa-wa-access__field">';
        echo '<span>' . esc_html__( 'Label', 'niwa-ai' ) . '</span>';
        echo '<input type="text" name="' . esc_attr( $base . '[label]' ) . '" value="' . esc_attr( $label ) . '" placeholder="' . esc_attr__( 'e.g. Sales group', 'niwa-ai' ) . '" />';
        echo '</label>';
        echo '<label class="niwa-wa-access__field">';
        echo '<span>' . esc_html__( 'Group JID', 'niwa-ai' ) . '</span>';
        echo '<input type="text" name="' . esc_attr( $base . '[jid]' ) . '" value="' . esc_attr( $jid ) . '" placeholder="' . esc_attr__( '120363...@g.us', 'niwa-ai' ) . '" />';
        echo '</label>';
        echo '<button type="button" class="niwa-wa-access__remove" data-niwa-wa-access-remove aria-label="' . esc_attr__( 'Remove group', 'niwa-ai' ) . '" title="' . esc_attr__( 'Remove', 'niwa-ai' ) . '">&times;</button>';
        echo '</div>';
    }

    public function ajax_whatsapp_connect() {
        $this->assert_whatsapp_ajax_access();
        if ( ! $this->whatsapp || ! method_exists( $this->whatsapp, 'start_gateway' ) ) {
            wp_send_json_error( array( 'error' => 'whatsapp_unavailable' ), 500 );
        }

        $this->persist_whatsapp_ajax_settings( true );
        $started = $this->whatsapp->start_gateway( false );
        if ( empty( $started['success'] ) ) {
            wp_send_json_error( $started, 500 );
        }

        wp_send_json_success( $started );
    }

    public function ajax_whatsapp_status() {
        $this->assert_whatsapp_ajax_access();
        if ( ! $this->whatsapp || ! method_exists( $this->whatsapp, 'get_gateway_status' ) ) {
            wp_send_json_error( array( 'error' => 'whatsapp_unavailable' ), 500 );
        }

        wp_send_json_success( $this->whatsapp->get_gateway_status() );
    }

    public function ajax_whatsapp_disconnect() {
        $this->assert_whatsapp_ajax_access();
        if ( ! $this->whatsapp || ! method_exists( $this->whatsapp, 'disconnect_gateway' ) ) {
            wp_send_json_error( array( 'error' => 'whatsapp_unavailable' ), 500 );
        }

        wp_send_json_success( $this->whatsapp->disconnect_gateway() );
    }

    private function assert_whatsapp_ajax_access() {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'error' => 'forbidden' ), 403 );
        }
        check_ajax_referer( 'niwa_ai_whatsapp_connect', 'nonce' );
    }

    private function persist_whatsapp_ajax_settings( $enabled ) {
        $settings = get_option( Niwa_AI_Admin_Autonomy_Telegram::OPTION_KEY, array() );
        $settings = is_array( $settings ) ? $settings : array();
        $wa       = isset( $settings['whatsapp'] ) && is_array( $settings['whatsapp'] ) ? $settings['whatsapp'] : array();
        $wa       = $this->sanitize_whatsapp_settings( $enabled ? array( 'enabled' => 1 ) : array(), $wa );

        $settings['whatsapp'] = $wa;
        update_option( Niwa_AI_Admin_Autonomy_Telegram::OPTION_KEY, $settings, false );
    }

    public function handle_disable_operative_heartbeat() {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'You do not have permission to change this setting.', 'niwa-ai' ) );
        }

        check_admin_referer( 'niwa_ai_disable_operative_heartbeat', 'niwa_ai_disable_operative_heartbeat_nonce' );

        $role = isset( $_POST['heartbeat_role'] ) ? sanitize_key( wp_unslash( $_POST['heartbeat_role'] ) ) : 'admin';
        $role = 'stock_manager' === $role ? 'stock_manager' : 'admin';

        $config_option = 'stock_manager' === $role
            ? Niwa_AI_Constants::OPT_AUTONOMY_STOCK_OPERATIVE_HB_CONFIG
            : Niwa_AI_Constants::OPT_AUTONOMY_OPERATIVE_HB_CONFIG;
        $state_option = 'stock_manager' === $role
            ? Niwa_AI_Constants::OPT_AUTONOMY_STOCK_OPERATIVE_HB_STATE
            : Niwa_AI_Constants::OPT_AUTONOMY_OPERATIVE_HB_STATE;

        $config = get_option( $config_option, array() );
        $config = is_array( $config ) ? $config : array();
        $config['enabled'] = false;
        update_option( $config_option, $config, false );

        $state = get_option( $state_option, array() );
        $state = is_array( $state ) ? $state : array();
        $state['last_status']     = 'manual_disabled';
        $state['last_attempt_at'] = time();
        $state['last_summary']    = __( 'Operative heartbeat was disabled from WordPress settings.', 'niwa-ai' );
        update_option( $state_option, $state, false );

        $lock_key = 'stock_manager' === $role ? 'niwa_ai_stock_operative_hb_lock' : 'niwa_ai_operative_hb_lock';
        delete_option( $lock_key );
        delete_transient( $lock_key );

        $this->redirect_runtime_notice(
            'success',
            'stock_manager' === $role
                ? __( 'Stock-manager operative heartbeat disabled.', 'niwa-ai' )
                : __( 'Admin operative heartbeat disabled.', 'niwa-ai' )
        );
    }




    private function redirect_runtime_notice( $type, $message ) {
        $this->redirect_admin_autonomy_notice( 'runtime', $type, $message );
    }

    private function redirect_admin_autonomy_notice( $tab, $type, $message ) {
        wp_safe_redirect(
            add_query_arg(
                array(
                    'page'                       => 'niwa-ai-admin-autonomy',
                    'tab'                        => sanitize_key( (string) $tab ),
                    'niwa_admin_autonomy_notice' => sanitize_key( (string) $type ),
                    'niwa_admin_autonomy_message'=> (string) $message,
                ),
                admin_url( 'admin.php' )
            )
        );
        exit;
    }

    private function sanitize_stock_managers( $input ) {
        $max = Niwa_AI_Admin_Autonomy_Telegram::MAX_STOCK_MANAGERS;
        $managers = array();

        if ( ! isset( $input['stock_managers'] ) || ! is_array( $input['stock_managers'] ) ) {
            return $managers;
        }

        $seen_ids = array();
        $owner_id = isset( $input['owner_chat_id'] ) ? preg_replace( '/[^0-9\\-]/', '', (string) $input['owner_chat_id'] ) : '';

        foreach ( $input['stock_managers'] as $entry ) {
            if ( ! is_array( $entry ) ) {
                continue;
            }

            $chat_id = isset( $entry['chat_id'] ) ? preg_replace( '/[^0-9\\-]/', '', (string) $entry['chat_id'] ) : '';
            if ( '' === $chat_id ) {
                continue;
            }

            // Skip duplicates and prevent admin ID from being added as stock manager.
            if ( isset( $seen_ids[ $chat_id ] ) ) {
                continue;
            }
            if ( '' !== $owner_id && $chat_id === $owner_id ) {
                continue;
            }

            $seen_ids[ $chat_id ] = true;
            $managers[] = array(
                'chat_id' => $chat_id,
                'label'   => isset( $entry['label'] ) ? sanitize_text_field( mb_substr( (string) $entry['label'], 0, 100 ) ) : '',
            );

            if ( count( $managers ) >= $max ) {
                break;
            }
        }

        return $managers;
    }

    public function sanitize_email_settings( $input ) {
        if ( ! $this->email_service ) {
            return is_array( $input ) ? $input : array();
        }

        $defaults = $this->email_service->get_defaults();
        $existing = $this->email_service->get_settings();
        $out      = wp_parse_args( is_array( $existing ) ? $existing : array(), $defaults );

        if ( ! is_array( $input ) ) {
            return $out;
        }

        $out['send_enabled']     = isset( $input['send_enabled'] ) ? 1 : 0;
        $out['from_name']        = isset( $input['from_name'] ) ? sanitize_text_field( $input['from_name'] ) : '';
        $out['from_email']       = isset( $input['from_email'] ) ? sanitize_email( $input['from_email'] ) : '';
        $out['known_recipients'] = isset( $input['known_recipients'] ) ? sanitize_textarea_field( $input['known_recipients'] ) : '';
        $out['smtp_enabled']     = isset( $input['smtp_enabled'] ) ? 1 : 0;
        $out['smtp_host']        = isset( $input['smtp_host'] ) && '' !== trim( (string) $input['smtp_host'] ) ? sanitize_text_field( $input['smtp_host'] ) : (string) $defaults['smtp_host'];
        $out['smtp_port']        = isset( $input['smtp_port'] ) ? max( 1, min( 65535, intval( $input['smtp_port'] ) ) ) : intval( $defaults['smtp_port'] );
        $out['smtp_encryption']  = isset( $input['smtp_encryption'] ) ? sanitize_key( $input['smtp_encryption'] ) : (string) $defaults['smtp_encryption'];
        if ( ! in_array( $out['smtp_encryption'], array( 'ssl', 'tls', 'none' ), true ) ) {
            $out['smtp_encryption'] = (string) $defaults['smtp_encryption'];
        }
        $out['smtp_username'] = isset( $input['smtp_username'] ) ? sanitize_text_field( $input['smtp_username'] ) : '';
        $smtp_password        = isset( $input['smtp_password'] ) ? (string) $input['smtp_password'] : '';
        if ( '' !== trim( $smtp_password ) ) {
            $out['smtp_password'] = $smtp_password;
        }
        $out['imap_enabled']     = isset( $input['imap_enabled'] ) ? 1 : 0;
        $out['imap_host']        = isset( $input['imap_host'] ) ? sanitize_text_field( $input['imap_host'] ) : '';
        $out['imap_port']        = isset( $input['imap_port'] ) ? max( 1, min( 65535, intval( $input['imap_port'] ) ) ) : intval( $defaults['imap_port'] );
        $out['imap_encryption']  = isset( $input['imap_encryption'] ) ? sanitize_key( $input['imap_encryption'] ) : (string) $defaults['imap_encryption'];
        if ( ! in_array( $out['imap_encryption'], array( 'ssl', 'tls', 'notls' ), true ) ) {
            $out['imap_encryption'] = (string) $defaults['imap_encryption'];
        }
        $out['imap_username'] = isset( $input['imap_username'] ) ? sanitize_text_field( $input['imap_username'] ) : '';
        $password             = isset( $input['imap_password'] ) ? (string) $input['imap_password'] : '';
        if ( '' !== trim( $password ) ) {
            $out['imap_password'] = $password;
        }
        $out['imap_mailbox'] = isset( $input['imap_mailbox'] ) ? sanitize_text_field( $input['imap_mailbox'] ) : (string) $defaults['imap_mailbox'];
        $out['inbound_poll_interval_minutes'] = class_exists( 'Niwa_AI_Admin_Autonomy_Email' )
            ? Niwa_AI_Admin_Autonomy_Email::normalize_inbound_poll_interval_minutes( isset( $input['inbound_poll_interval_minutes'] ) ? $input['inbound_poll_interval_minutes'] : $defaults['inbound_poll_interval_minutes'] )
            : 15;

        $this->reschedule_inbound_email_poll_cron( $out['inbound_poll_interval_minutes'] );

        return $out;
    }

    private function reschedule_inbound_email_poll_cron( $interval_minutes ) {
        if ( ! class_exists( 'Niwa_AI_Admin_Autonomy_Email' ) || ! function_exists( 'wp_schedule_event' ) ) {
            return;
        }

        $hook     = 'niwa_ai_inbound_email_poll';
        $schedule = Niwa_AI_Admin_Autonomy_Email::get_inbound_poll_schedule_for_interval( $interval_minutes );
        $slug     = isset( $schedule['schedule'] ) ? (string) $schedule['schedule'] : 'niwa_ai_fifteen_minutes';
        $seconds  = isset( $schedule['seconds'] ) ? max( 60, intval( $schedule['seconds'] ) ) : 900;

        if ( class_exists( 'Niwa_AI_Cron_Maintenance' ) ) {
            Niwa_AI_Cron_Maintenance::replace_single_recurring_event( $hook, $slug, min( 300, $seconds ) );
            return;
        }

        if ( function_exists( 'wp_clear_scheduled_hook' ) ) {
            wp_clear_scheduled_hook( $hook );
        }

        if ( ! wp_next_scheduled( $hook ) ) {
            wp_schedule_event( time() + min( 300, $seconds ), $slug, $hook );
        }
    }

    private function render_backend_avatar_card() {
        $settings = get_option( Niwa_AI_Constants::OPT_SETTINGS, array() );
        $settings = is_array( $settings ) ? $settings : array();

        echo '<div class="niwa-card niwa-card--focus" id="niwa-pa-chathead">';
        echo '<div class="niwa-card__header">';
        echo '<div class="niwa-card__icon"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="4"/><path d="M4 22c0-4 3.5-7 8-7s8 3 8 7"/></svg></div>';
        echo '<h2 class="niwa-card__title">' . esc_html__( 'Personal Assistant Chathead', 'niwa-ai' ) . '</h2>';
        echo '</div>';
        echo '<div class="niwa-card__body">';
        echo '<form method="post" action="options.php">';
        settings_fields( 'niwa_ai_general_group' );
        echo '<input type="hidden" name="niwa_ai_settings[_niwa_general_form]" value="backend_avatar" />';
        echo '<div class="niwa-alert niwa-alert--info">';
        echo '<strong>' . esc_html__( 'Separate save:', 'niwa-ai' ) . '</strong> ' . esc_html__( 'This changes only the image used by the wp-admin Personal Assistant launcher. Telegram, access, email, and runtime settings are saved in the main settings section below.', 'niwa-ai' );
        echo '</div>';
        $this->render_avatar_field(
            'backend_avatar_url',
            esc_html__( 'Chathead Image', 'niwa-ai' ),
            esc_html__( 'Changes only the wp-admin Personal Assistant image. The label follows the configured assistant name.', 'niwa-ai' ),
            isset( $settings['backend_avatar_url'] ) ? (string) $settings['backend_avatar_url'] : '',
            defined( 'NIWA_AI_DEFAULT_BACKEND_AVATAR_URL' )
                ? NIWA_AI_DEFAULT_BACKEND_AVATAR_URL
                : NIWA_AI_URL . 'assets/img/niwa-admin-personal-assistant.png'
        );
        echo '<div class="niwa-card__footer">';
        submit_button( __( 'Save Personal Assistant Chathead', 'niwa-ai' ), 'primary niwa-btn niwa-btn--primary', 'submit', false );
        echo '</div>';
        echo '</form>';
        echo '</div>';
        echo '</div>';
    }

    private function render_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>';
    }

    public function render_page() {
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }

        $settings        = $this->telegram->get_settings();
        $email_settings  = $this->email_service ? $this->email_service->get_settings() : array();
        $whatsapp_settings = $this->whatsapp && method_exists( $this->whatsapp, 'get_settings' ) ? $this->whatsapp->get_settings() : array();
        $whatsapp_status   = $this->whatsapp && method_exists( $this->whatsapp, 'get_gateway_status' ) ? $this->whatsapp->get_gateway_status() : array();
        $poll_state      = $this->telegram->get_poll_state();
        $webhook_state   = method_exists( $this->telegram, 'get_webhook_state' ) ? $this->telegram->get_webhook_state() : array();
        $runner_state    = $this->telegram->get_runner_state();
        $report_state    = get_option( Niwa_AI_Constants::OPT_AUTONOMY_REPORT_STATE, array() );
        $heartbeat_state = get_option( Niwa_AI_Constants::OPT_AUTONOMY_HEARTBEAT_STATE, array() );
        $heartbeat_config = $this->get_effective_heartbeat_config();
        $operative_heartbeat_status = array(
            $this->get_operative_heartbeat_runtime_status( 'admin' ),
            $this->get_operative_heartbeat_runtime_status( 'stock_manager' ),
        );
        $active_scheduled_jobs = $this->get_active_scheduled_jobs_runtime_status();
        $prompt_cache_status = $this->get_prompt_cache_runtime_status();
        if ( ! is_array( $report_state ) ) {
            $report_state = array();
        }
        if ( ! is_array( $heartbeat_state ) ) {
            $heartbeat_state = array();
        }
        $recent_runs  = $this->store->list_recent_runs( 12 );
        $next_poll    = wp_next_scheduled( 'niwa_ai_admin_autonomy_poll_telegram' );
        $notice_type  = isset( $_GET['niwa_admin_autonomy_notice'] ) ? sanitize_key( wp_unslash( $_GET['niwa_admin_autonomy_notice'] ) ) : '';
        $notice_msg   = isset( $_GET['niwa_admin_autonomy_message'] ) ? sanitize_text_field( wp_unslash( $_GET['niwa_admin_autonomy_message'] ) ) : '';
        $tg_key       = esc_attr( Niwa_AI_Admin_Autonomy_Telegram::OPTION_KEY );
        $heartbeat_key = esc_attr( Niwa_AI_Constants::OPT_AUTONOMY_HEARTBEAT_CONFIG );

        $valid_tabs = array( 'setup', 'runtime', 'performance', 'capabilities' );
        $active_tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'setup';
        if ( ! in_array( $active_tab, $valid_tabs, true ) ) {
            $active_tab = 'setup';
        }

        $tab_url = function ( $tab ) {
            return add_query_arg(
                array(
                    'page' => 'niwa-ai-admin-autonomy',
                    'tab'  => $tab,
                ),
                admin_url( 'admin.php' )
            );
        };

        $tab_definitions = array(
            'setup'        => array(
                'label'       => __( 'Setup & Access', 'niwa-ai' ),
                'eyebrow'     => __( 'Personal Assistant', 'niwa-ai' ),
                'title'       => __( 'Setup and access', 'niwa-ai' ),
                'description' => __( 'Configure the assistant image, model, Telegram and WhatsApp owner access, stock-manager access, email tools, reporting, and unauthorized-user reply.', 'niwa-ai' ),
            ),
            'runtime'      => array(
                'label'       => __( 'Runtime', 'niwa-ai' ),
                'eyebrow'     => __( 'Personal Assistant', 'niwa-ai' ),
                'title'       => __( 'Runtime monitor', 'niwa-ai' ),
                'description' => __( 'Check webhook, fallback polling, runner, report, and heartbeat status. The manual runtime button wakes processing now; it does not save settings.', 'niwa-ai' ),
            ),
            'performance'  => array(
                'label'       => __( 'Performance', 'niwa-ai' ),
                'eyebrow'     => __( 'Personal Assistant', 'niwa-ai' ),
                'title'       => __( 'Performance settings', 'niwa-ai' ),
                'description' => __( 'Tune background cron jobs and browser polling intervals for the Personal Assistant and related Niwa workloads.', 'niwa-ai' ),
            ),
            'capabilities' => array(
                'label'       => __( 'Capabilities & History', 'niwa-ai' ),
                'eyebrow'     => __( 'Personal Assistant', 'niwa-ai' ),
                'title'       => __( 'Capabilities and history', 'niwa-ai' ),
                'description' => __( 'Reference what the founder chat can do and inspect recent executed runs without changing configuration.', 'niwa-ai' ),
            ),
        );

        // ── Page wrapper ──
        echo '<div class="wrap niwa-wrap">';

        // ── Page 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 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>';
        echo '</div>';
        echo '<div class="niwa-page-header__text">';
        echo '<h1 class="niwa-page-header__title">' . esc_html( $this->get_personal_assistant_title() ) . '</h1>';
        echo '<p class="niwa-page-header__subtitle">' . esc_html__( 'Founder agent over Telegram and WhatsApp - natural chat, deep memory, retrieval tools, and immediate execution.', 'niwa-ai' ) . '</p>';
        echo '</div>';
        echo '</div>';

        // ── Admin notices ──
        if ( '' !== $notice_type && '' !== $notice_msg ) {
            $notice_class = 'success' === $notice_type ? 'niwa-alert niwa-alert--info' : 'niwa-alert niwa-alert--error';
            $notice_icon  = 'success' === $notice_type
                ? '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>'
                : '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" 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="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>';
            echo '<div class="' . esc_attr( $notice_class ) . '">' . $notice_icon . ' ' . esc_html( $notice_msg ) . '</div>';
        }

        // ── Tab navigation (real subpages via ?tab=) ──
        echo '<div class="niwa-tabs" role="tablist" aria-label="' . esc_attr__( 'Personal Assistant sections', 'niwa-ai' ) . '">';
        foreach ( $tab_definitions as $tab_key => $tab_meta ) {
            $is_active = ( $tab_key === $active_tab );
            $classes   = 'niwa-tabs__tab' . ( $is_active ? ' niwa-tabs__tab--active' : '' );
            $aria      = $is_active ? ' aria-current="page"' : '';
            echo '<a href="' . esc_url( $tab_url( $tab_key ) ) . '" class="' . esc_attr( $classes ) . '"' . $aria . '>' . esc_html( $tab_meta['label'] ) . '</a>';
        }
        echo '</div>';

        // ── Active section heading ──
        $current_meta = $tab_definitions[ $active_tab ];
        echo '<div class="niwa-section-heading" id="niwa-pa-' . esc_attr( $active_tab ) . '">';
        echo '<div>';
        echo '<span class="niwa-section-heading__eyebrow">' . esc_html( $current_meta['eyebrow'] ) . '</span>';
        echo '<h2>' . esc_html( $current_meta['title'] ) . '</h2>';
        echo '<p>' . esc_html( $current_meta['description'] ) . '</p>';
        echo '</div>';
        echo '</div>';

        // ══════════════════════════════════════════
        // ── SETUP TAB ──
        // ══════════════════════════════════════════
        if ( 'setup' === $active_tab ) {
        $this->render_backend_avatar_card();

        echo '<form method="post" action="options.php">';
        settings_fields( 'niwa_ai_admin_autonomy_group' );

        $auth_opts             = get_option( Niwa_AI_Constants::OPT_AUTH, array() );
        $auth_opts             = is_array( $auth_opts ) ? $auth_opts : array();
        $api_key_for_models    = isset( $auth_opts['api_key'] ) ? (string) $auth_opts['api_key'] : '';
        $founder_auth_mode     = $this->resolve_founder_auth_mode( $settings, $auth_opts );
        $founder_model_sets    = $this->model_catalog && method_exists( $this->model_catalog, 'get_all_founder_model_sets' )
            ? $this->model_catalog->get_all_founder_model_sets( $api_key_for_models )
            : array();
        $founder_model_options = $this->model_catalog && method_exists( $this->model_catalog, 'get_founder_models_for_mode' )
            ? $this->model_catalog->get_founder_models_for_mode( $founder_auth_mode, $api_key_for_models )
            : array();
        $founder_model         = isset( $settings['founder_agent_model'] ) ? trim( (string) $settings['founder_agent_model'] ) : '';
        $founder_default_model = $this->model_catalog && method_exists( $this->model_catalog, 'get_default_founder_model' )
            ? (string) $this->model_catalog->get_default_founder_model( $founder_auth_mode )
            : ( 'oauth' === $founder_auth_mode ? 'gpt-5.4' : 'gpt-5.4-mini' );

        if ( '' === $founder_model || ! $this->is_founder_model_allowed( $founder_auth_mode, $founder_model, $api_key_for_models ) ) {
            $founder_model = $founder_default_model;
        }
        if ( empty( $founder_model_options ) ) {
            $founder_model_options = array( $founder_model => $founder_model );
        } elseif ( ! isset( $founder_model_options[ $founder_model ] ) ) {
            if ( isset( $founder_model_options[ $founder_default_model ] ) ) {
                $founder_model = $founder_default_model;
            } else {
                $founder_keys  = array_keys( $founder_model_options );
                $founder_model = ! empty( $founder_keys ) ? (string) reset( $founder_keys ) : $founder_model;
            }
        }

        wp_enqueue_script(
            'niwa-admin-autonomy-settings',
            NIWA_AI_URL . 'assets/js/niwa-admin-autonomy-settings.js',
            array(),
            NIWA_AI_VERSION,
            array( 'in_footer' => true )
        );
        wp_localize_script(
            'niwa-admin-autonomy-settings',
            'niwaAdminAutonomySettings',
            array(
                'modelSets' => $founder_model_sets,
            )
        );

        echo '<div class="niwa-card">';
        echo '<div class="niwa-card__header">';
        echo '<div class="niwa-card__icon"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3l7 4v5c0 5-3.5 8.5-7 9-3.5-.5-7-4-7-9V7l7-4z"/><path d="M9 12l2 2 4-4"/></svg></div>';
        echo '<h2 class="niwa-card__title">' . esc_html__( 'Agent Runtime', 'niwa-ai' ) . '</h2>';
        echo '</div>';
        echo '<div class="niwa-card__body">';

        echo '<div class="niwa-field">';
        echo '<label class="niwa-field__label" for="niwa-founder-auth-mode">' . esc_html__( 'Founder Connection Mode', 'niwa-ai' ) . '</label>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Uses the global API Key and the global Codex OAuth connection, but you choose here which auth path the founder agent should run through.', 'niwa-ai' ) . '</span>';
        echo '<select id="niwa-founder-auth-mode" name="' . $tg_key . '[founder_auth_mode]">';
        echo '<option value="api_key" ' . selected( $founder_auth_mode, 'api_key', false ) . '>' . esc_html__( 'API Key', 'niwa-ai' ) . '</option>';
        echo '<option value="oauth" ' . selected( $founder_auth_mode, 'oauth', false ) . '>' . esc_html__( 'Codex OAuth', 'niwa-ai' ) . '</option>';
        echo '</select>';
        echo '</div>';

        echo '<div class="niwa-field">';
        echo '<label class="niwa-field__label" for="niwa-founder-agent-model">' . esc_html__( 'Founder Model', 'niwa-ai' ) . '</label>';
        echo '<span class="niwa-field__hint" id="niwa-founder-model-hint">' . esc_html__( 'Only chat-capable models are shown here. GPT-4 and GPT-4o variants are intentionally hidden for Personal Assistant.', 'niwa-ai' ) . '</span>';
        echo '<select id="niwa-founder-agent-model" name="' . $tg_key . '[founder_agent_model]" style="min-width:320px;">';
        foreach ( $founder_model_options as $model_id => $model_label ) {
            echo '<option value="' . esc_attr( $model_id ) . '" ' . selected( $founder_model, $model_id, false ) . '>' . esc_html( $model_label ) . '</option>';
        }
        echo '</select>';
        echo '</div>';

        $founder_image_quality = isset( $settings['founder_image_quality'] ) ? $this->sanitize_image_quality( $settings['founder_image_quality'] ) : 'medium';
        echo '<div class="niwa-field">';
        echo '<label class="niwa-field__label" for="niwa-founder-image-quality">' . esc_html__( 'Image Generation Quality', 'niwa-ai' ) . '</label>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Default quality for generated images created by the Personal Assistant through Codex OAuth / GPT Image 2.', 'niwa-ai' ) . '</span>';
        echo '<select id="niwa-founder-image-quality" name="' . esc_attr( Niwa_AI_Admin_Autonomy_Telegram::OPTION_KEY ) . '[founder_image_quality]">';
        foreach ( array( 'low' => __( 'Low — fastest', 'niwa-ai' ), 'medium' => __( 'Medium — balanced', 'niwa-ai' ), 'high' => __( 'High — best quality', 'niwa-ai' ) ) as $quality_value => $quality_label ) {
            echo '<option value="' . esc_attr( $quality_value ) . '" ' . selected( $founder_image_quality, $quality_value, false ) . '>' . esc_html( $quality_label ) . '</option>';
        }
        echo '</select>';
        echo '</div>';

        $admin_launcher_scope = isset( $settings['admin_launcher_scope'] ) ? sanitize_key( (string) $settings['admin_launcher_scope'] ) : 'niwa_dashboard';
        if ( ! in_array( $admin_launcher_scope, array( 'dashboard', 'niwa_dashboard', 'everywhere' ), true ) ) {
            $admin_launcher_scope = 'niwa_dashboard';
        }
        echo '<div class="niwa-field">';
        echo '<label class="niwa-field__label" for="niwa-admin-launcher-scope">' . esc_html__( 'WP Admin Chat Display', 'niwa-ai' ) . '</label>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Choose where the floating Personal Assistant chat appears inside WordPress admin.', 'niwa-ai' ) . '</span>';
        echo '<select id="niwa-admin-launcher-scope" name="' . $tg_key . '[admin_launcher_scope]" style="min-width:320px;">';
        echo '<option value="dashboard" ' . selected( $admin_launcher_scope, 'dashboard', false ) . '>' . esc_html__( 'Dashboard only', 'niwa-ai' ) . '</option>';
        echo '<option value="niwa_dashboard" ' . selected( $admin_launcher_scope, 'niwa_dashboard', false ) . '>' . esc_html__( 'Niwa pages + Dashboard', 'niwa-ai' ) . '</option>';
        echo '<option value="everywhere" ' . selected( $admin_launcher_scope, 'everywhere', false ) . '>' . esc_html__( 'Everywhere in wp-admin', 'niwa-ai' ) . '</option>';
        echo '</select>';
        echo '</div>';

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

        // ─────────────────────────────────────────
        // Card 1: Telegram Bot
        // ─────────────────────────────────────────
        echo '<div class="niwa-card">';
        echo '<div class="niwa-card__header">';
        echo '<div class="niwa-card__icon"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg></div>';
        echo '<h2 class="niwa-card__title">' . esc_html__( 'Telegram Bot', 'niwa-ai' ) . '</h2>';
        echo '</div>';
        echo '<div class="niwa-card__body">';

        // Enable toggle.
        echo '<div class="niwa-field">';
        echo '<label class="niwa-toggle">';
        echo '<input type="checkbox" name="' . $tg_key . '[enabled]" value="1" ' . checked( ! empty( $settings['enabled'] ), true, false ) . ' />';
        echo esc_html__( 'Enable founder agent over Telegram webhook', 'niwa-ai' );
        echo '</label>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Activates the Telegram webhook and queue runtime so the founder can chat with Niwa directly.', 'niwa-ai' ) . '</span>';
        echo '</div>';

        echo '<div class="niwa-field">';
        echo '<label class="niwa-toggle">';
        echo '<input type="checkbox" name="' . $tg_key . '[show_tooluse_in_telegram_chat]" value="1" ' . checked( ! empty( $settings['show_tooluse_in_telegram_chat'] ), true, false ) . ' />';
        echo esc_html__( 'Show tooluse in Telegram chat', 'niwa-ai' );
        echo '</label>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Optional debugging for live runs. Leave off in production to keep Telegram focused on the final answer.', 'niwa-ai' ) . '</span>';
        echo '</div>';

        $bot_token_placeholder = ! empty( $settings['bot_token'] ) ? '************' : '';

        // Bot Token.
        echo '<div class="niwa-field">';
        echo '<label class="niwa-field__label">' . esc_html__( 'Bot Token', 'niwa-ai' ) . '</label>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'BotFather token for the founder/admin Telegram bot. Leave blank to keep the existing token while Telegram is enabled. Turning Telegram off or using Remove saved Bot Token deletes it.', 'niwa-ai' ) . '</span>';
        echo '<input type="password" name="' . $tg_key . '[bot_token]" value="" placeholder="' . esc_attr( $bot_token_placeholder ) . '" class="niwa-input--wide" autocomplete="off" />';
        echo '</div>';

        echo '<div class="niwa-field">';
        echo '<label class="niwa-toggle">';
        echo '<input type="checkbox" name="' . $tg_key . '[clear_bot_token]" value="1" />';
        echo esc_html__( 'Remove saved Bot Token', 'niwa-ai' );
        echo '</label>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Deletes the stored Telegram token on save. With no token, no Telegram outbound service can send.', 'niwa-ai' ) . '</span>';
        echo '</div>';

        // Owner Chat ID.
        echo '<div class="niwa-field">';
        echo '<label class="niwa-field__label">' . esc_html__( 'Owner Chat ID', 'niwa-ai' ) . '</label>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Only this Telegram chat ID has access to the founder agent.', 'niwa-ai' ) . '</span>';
        echo '<input type="text" name="' . $tg_key . '[owner_chat_id]" value="' . esc_attr( isset( $settings['owner_chat_id'] ) ? $settings['owner_chat_id'] : '' ) . '" class="niwa-input--wide" />';
        echo '</div>';

        // Founder Notes.
        echo '<div class="niwa-field">';
        echo '<label class="niwa-field__label">' . esc_html__( 'Founder Notes', 'niwa-ai' ) . '</label>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Pinned notes the agent always sees alongside rolling memory. Use for permanent preferences, rules, and important site/founder details.', 'niwa-ai' ) . '</span>';
        echo '<textarea name="' . $tg_key . '[founder_notes_seed]" rows="4" class="niwa-input--wide" style="max-width:100%;">' . esc_textarea( isset( $settings['founder_notes_seed'] ) ? $settings['founder_notes_seed'] : '' ) . '</textarea>';
        echo '</div>';

        echo '</div>'; // .niwa-card__body
        echo '</div>'; // .niwa-card

        // ─────────────────────────────────────────
        // Card 2: WhatsApp Web QR pairing
        // ─────────────────────────────────────────
		$whatsapp_status_label = isset( $whatsapp_status['status'] ) && '' !== (string) $whatsapp_status['status'] ? (string) $whatsapp_status['status'] : 'not_started';
		$whatsapp_qr_data_url  = isset( $whatsapp_status['qrDataUrl'] ) ? (string) $whatsapp_status['qrDataUrl'] : '';
		$whatsapp_qr_visible   = 0 === strpos( $whatsapp_qr_data_url, 'data:image/png;base64,' );
		if ( ! $whatsapp_qr_visible ) {
			$whatsapp_qr_data_url = '';
		}

        echo '<div class="niwa-card">';
        echo '<div class="niwa-card__header">';
        echo '<div class="niwa-card__icon"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg></div>';
        echo '<h2 class="niwa-card__title">' . esc_html__( 'WhatsApp Web', 'niwa-ai' ) . '</h2>';
        echo '</div>';
        echo '<div class="niwa-card__body">';

        echo '<div class="niwa-field">';
        echo '<label class="niwa-toggle">';
        echo '<input type="checkbox" name="' . $tg_key . '[whatsapp][enabled]" value="1" ' . checked( ! empty( $whatsapp_settings['enabled'] ), true, false ) . ' />';
        echo esc_html__( 'Enable founder agent over WhatsApp Web', 'niwa-ai' );
        echo '</label>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Connects by QR scan from WhatsApp Linked Devices. Telegram stays unchanged.', 'niwa-ai' ) . '</span>';
        echo '</div>';

        echo '<div class="niwa-wa-access" aria-label="' . esc_attr__( 'WhatsApp access lists', 'niwa-ai' ) . '">';
        $this->render_whatsapp_access_group(
            $tg_key,
            'admin_numbers',
            __( 'Admins', 'niwa-ai' ),
            __( 'Full founder access over WhatsApp.', 'niwa-ai' ),
            __( 'Add admin', 'niwa-ai' ),
            isset( $whatsapp_settings['admin_numbers'] ) ? $whatsapp_settings['admin_numbers'] : array()
        );
        $this->render_whatsapp_access_group(
            $tg_key,
            'stock_manager_numbers',
            __( 'Stock managers', 'niwa-ai' ),
            __( 'Product-management access only.', 'niwa-ai' ),
            __( 'Add stock manager', 'niwa-ai' ),
            isset( $whatsapp_settings['stock_manager_numbers'] ) ? $whatsapp_settings['stock_manager_numbers'] : array()
        );
        $this->render_whatsapp_access_group(
            $tg_key,
            'lead_numbers',
            __( 'Leads', 'niwa-ai' ),
            __( 'Customer chat and WhatsApp marketing access through Niwa. Add * only when every inbound WhatsApp contact should be treated as a lead.', 'niwa-ai' ),
            __( 'Add lead', 'niwa-ai' ),
            isset( $whatsapp_settings['lead_numbers'] ) ? $whatsapp_settings['lead_numbers'] : array()
        );
        $this->render_whatsapp_group_access_group(
            $tg_key,
            'admin_group_jids',
            __( 'Admin groups', 'niwa-ai' ),
            __( 'WhatsApp group chats with founder/admin access. Messages must address Niwa by slash command, structured button, mention, or reply.', 'niwa-ai' ),
            __( 'Add admin group', 'niwa-ai' ),
            isset( $whatsapp_settings['admin_group_jids'] ) ? $whatsapp_settings['admin_group_jids'] : array()
        );
        $this->render_whatsapp_group_access_group(
            $tg_key,
            'stock_manager_group_jids',
            __( 'Stock manager groups', 'niwa-ai' ),
            __( 'WhatsApp group chats with stock-manager access only. Messages must address Niwa by slash command, structured button, mention, or reply.', 'niwa-ai' ),
            __( 'Add stock group', 'niwa-ai' ),
            isset( $whatsapp_settings['stock_manager_group_jids'] ) ? $whatsapp_settings['stock_manager_group_jids'] : array()
        );
        $this->render_whatsapp_group_access_group(
            $tg_key,
            'lead_group_jids',
            __( 'Lead groups', 'niwa-ai' ),
            __( 'WhatsApp group chats with lead/customer access. Messages must address Niwa by slash command, structured button, mention, or reply.', 'niwa-ai' ),
            __( 'Add lead group', 'niwa-ai' ),
            isset( $whatsapp_settings['lead_group_jids'] ) ? $whatsapp_settings['lead_group_jids'] : array()
        );
        echo '</div>';

        echo '<div class="niwa-field">';
        echo '<label class="niwa-field__label">' . esc_html__( 'Connection', 'niwa-ai' ) . '</label>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Click Connect WhatsApp, then scan the QR code with WhatsApp Linked Devices.', 'niwa-ai' ) . '</span>';
        echo '<div class="niwa-btn-group" style="margin-top:8px;">';
        echo '<button type="button" class="button niwa-btn niwa-btn--primary" id="niwa-whatsapp-connect">' . esc_html__( 'Connect WhatsApp', 'niwa-ai' ) . '</button>';
        echo '<button type="button" class="button niwa-btn niwa-btn--secondary" id="niwa-whatsapp-disconnect">' . esc_html__( 'Disconnect', 'niwa-ai' ) . '</button>';
        echo '<span id="niwa-whatsapp-status" class="niwa-badge">' . esc_html( $whatsapp_status_label ) . '</span>';
        echo '</div>';
		echo '<div id="niwa-whatsapp-qr-wrap" style="display:' . ( $whatsapp_qr_visible ? 'block' : 'none' ) . ';margin-top:14px;">';
		echo '<img id="niwa-whatsapp-qr" src="' . esc_attr( $whatsapp_qr_data_url ) . '" alt="' . esc_attr__( 'WhatsApp QR code', 'niwa-ai' ) . '" style="width:240px;height:240px;border:1px solid rgba(15,23,42,0.12);background:#fff;padding:10px;border-radius:8px;" />';
        echo '<div class="niwa-field__hint" style="margin-top:8px;">' . esc_html__( 'Scan this QR in WhatsApp Linked Devices.', 'niwa-ai' ) . '</div>';
        echo '</div>';
        echo '</div>';

        if ( ! class_exists( 'Niwa_AI_Admin_Autonomy_Process_Runtime' ) || ! Niwa_AI_Admin_Autonomy_Process_Runtime::is_proc_open_available() ) {
            echo '<div class="niwa-alert niwa-alert--error">';
            echo '<strong>' . esc_html__( 'WhatsApp cannot generate the QR code because PHP process spawning is disabled on this server.', 'niwa-ai' ) . '</strong>';
            echo '<p>' . esc_html__( 'Niwa needs the PHP functions proc_open, proc_get_status, and proc_close to start its local WhatsApp gateway.', 'niwa-ai' ) . '</p>';
            echo '<ol>';
            echo '<li>' . esc_html__( 'Open your hosting control panel and find PHP Settings, PHP Options, or PHP Configuration for this site.', 'niwa-ai' ) . '</li>';
            echo '<li>' . wp_kses_post( __( 'Find <code>disabled_functions</code> and remove <code>proc_open</code>, <code>proc_get_status</code>, and <code>proc_close</code> from the disabled list.', 'niwa-ai' ) ) . '</li>';
            echo '<li>' . esc_html__( 'Save the PHP settings, reload this page, and click Connect WhatsApp again to generate the QR code.', 'niwa-ai' ) . '</li>';
            echo '</ol>';
            echo '<p>' . esc_html__( 'If your hosting panel does not allow this change, ask hosting support to enable these functions for the active PHP version. The server must also provide Node.js and /bin/sh.', 'niwa-ai' ) . '</p>';
            echo '</div>';
        }

        echo '</div>'; // .niwa-card__body
        echo '</div>'; // .niwa-card

        $whatsapp_ajax = array(
            'ajaxUrl' => admin_url( 'admin-ajax.php' ),
            'nonce'   => wp_create_nonce( 'niwa_ai_whatsapp_connect' ),
        );
        echo '<script>';
        echo '(function(){';
        echo 'const cfg=' . wp_json_encode( $whatsapp_ajax ) . ';';
        echo 'const statusEl=document.getElementById("niwa-whatsapp-status");';
        echo 'const qrWrap=document.getElementById("niwa-whatsapp-qr-wrap");';
        echo 'const qrImg=document.getElementById("niwa-whatsapp-qr");';
        echo 'const connectBtn=document.getElementById("niwa-whatsapp-connect");';
        echo 'const disconnectBtn=document.getElementById("niwa-whatsapp-disconnect");';
        echo 'let timer=null;';
        echo 'let stopTimer=null;';
        echo 'let activeMode="";';
        echo 'let pollCount=0;';
        echo 'function setStatus(text){if(statusEl){statusEl.textContent=text||"-";}}';
        echo 'function setBusy(busy){if(connectBtn){connectBtn.disabled=!!busy;}if(disconnectBtn){disconnectBtn.disabled=!!busy;}}';
        echo 'function stopPoll(){clearTimeout(timer);timer=null;clearTimeout(stopTimer);stopTimer=null;activeMode="";pollCount=0;setBusy(false);}';
        echo 'function pollDelay(){pollCount++;return pollCount<=2?1500:(pollCount<=8?3000:7000);}';
        echo 'function schedulePoll(delay){clearTimeout(timer);if(!activeMode){return;}timer=setTimeout(poll,delay||pollDelay());}';
        echo 'function startPoll(ms){clearTimeout(timer);clearTimeout(stopTimer);pollCount=0;stopTimer=setTimeout(stopPoll,ms||180000);}';
        echo 'function form(action){const d=new FormData();d.append("action",action);d.append("nonce",cfg.nonce);return d;}';
        echo 'function applyStatus(payload,mode){mode=mode||activeMode;const s=(payload&&typeof payload.status==="object"&&payload.status!==null)?payload.status:(payload||{});let label=s.status||(s.running?"running":"not_started");if(mode==="disconnect"&&label==="connected"){label="disconnecting";}setStatus(label);if(s.qrDataUrl){qrImg.src=s.qrDataUrl;qrWrap.style.display="block";}else if(["connected","logged_out","gateway_crashed","not_started","disconnecting","stopping"].indexOf(label)!==-1){qrImg.removeAttribute("src");qrWrap.style.display="none";}if(s.lastError){setStatus((label||"error")+": "+s.lastError);}if(mode==="connect"&&(label==="connected"||label==="logged_out"||label==="gateway_crashed")){stopPoll();}if(mode==="disconnect"&&(label==="logged_out"||label==="not_started"||label==="gateway_crashed")){stopPoll();}}';
        echo 'async function call(action){const res=await fetch(cfg.ajaxUrl,{method:"POST",credentials:"same-origin",body:form(action)});const json=await res.json();if(!json.success){throw new Error((json.data&&json.data.error)||"request_failed");}return json.data;}';
        echo 'async function poll(){try{applyStatus(await call("niwa_ai_whatsapp_status"));}catch(e){setStatus(e.message);}finally{if(activeMode){schedulePoll();}}}';
        echo 'connectBtn?.addEventListener("click",async function(){try{activeMode="connect";setBusy(true);setStatus("starting");if(qrWrap){qrWrap.style.display="none";}startPoll(180000);applyStatus(await call("niwa_ai_whatsapp_connect"),"connect");if(activeMode){schedulePoll(600);}}catch(e){stopPoll();setStatus(e.message);}});';
        echo 'disconnectBtn?.addEventListener("click",async function(){try{activeMode="disconnect";setBusy(true);setStatus("disconnecting");if(qrWrap){qrWrap.style.display="none";}startPoll(60000);applyStatus(await call("niwa_ai_whatsapp_disconnect"),"disconnect");if(activeMode){schedulePoll(600);}}catch(e){stopPoll();setStatus(e.message);}});';
        echo '})();';
        echo '</script>';

        // ─────────────────────────────────────────
        // Card 3: Webhook Configuration
        // ─────────────────────────────────────────
        echo '<div class="niwa-card">';
        echo '<div class="niwa-card__header">';
        echo '<div class="niwa-card__icon"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></svg></div>';
        echo '<h2 class="niwa-card__title">' . esc_html__( 'Webhook Configuration', 'niwa-ai' ) . '</h2>';
        echo '</div>';
        echo '<div class="niwa-card__body">';

        // Webhook fallback poll timeout + batch limit (inline row).
        echo '<div class="niwa-field">';
        echo '<span class="niwa-field__label">' . esc_html__( 'Webhook Fallback Poll Parameters', 'niwa-ai' ) . '</span>';
        echo '<div class="niwa-field__row">';
        echo '<div>';
        echo '<label style="font-size:12px;color:var(--niwa-ink-3);display:block;margin-bottom:3px;">' . esc_html__( 'Timeout (sec)', 'niwa-ai' ) . '</label>';
        echo '<input type="number" min="1" max="25" name="' . $tg_key . '[poll_timeout]" value="' . esc_attr( isset( $settings['poll_timeout'] ) ? $settings['poll_timeout'] : 5 ) . '" />';
        echo '</div>';
        echo '<div>';
        echo '<label style="font-size:12px;color:var(--niwa-ink-3);display:block;margin-bottom:3px;">' . esc_html__( 'Batch Limit', 'niwa-ai' ) . '</label>';
        echo '<input type="number" min="1" max="50" name="' . $tg_key . '[poll_limit]" value="' . esc_attr( isset( $settings['poll_limit'] ) ? $settings['poll_limit'] : 10 ) . '" />';
        echo '</div>';
        echo '</div>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Only used when the Telegram webhook is unhealthy or unavailable. Controls how long one fallback getUpdates request waits and how many updates it can fetch.', 'niwa-ai' ) . '</span>';
        echo '</div>';

        // Rate Limit Max + Rate Limit Window (inline row).
        echo '<div class="niwa-field">';
        echo '<span class="niwa-field__label">' . esc_html__( 'Rate Limiting', 'niwa-ai' ) . '</span>';
        echo '<div class="niwa-field__row">';
        echo '<div>';
        echo '<label style="font-size:12px;color:var(--niwa-ink-3);display:block;margin-bottom:3px;">' . esc_html__( 'Max Messages', 'niwa-ai' ) . '</label>';
        echo '<input type="number" min="1" max="120" name="' . $tg_key . '[rate_limit_max]" value="' . esc_attr( isset( $settings['rate_limit_max'] ) ? $settings['rate_limit_max'] : 120 ) . '" />';
        echo '</div>';
        echo '<div>';
        echo '<label style="font-size:12px;color:var(--niwa-ink-3);display:block;margin-bottom:3px;">' . esc_html__( 'Window (sec)', 'niwa-ai' ) . '</label>';
        echo '<input type="number" min="10" max="3600" name="' . $tg_key . '[rate_limit_window]" value="' . esc_attr( isset( $settings['rate_limit_window'] ) ? $settings['rate_limit_window'] : 60 ) . '" />';
        echo '</div>';
        echo '</div>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Maximum founder messages per window and the window duration in seconds.', 'niwa-ai' ) . '</span>';
        echo '</div>';

        // Morning Report toggle.
        echo '<div class="niwa-field">';
        echo '<label class="niwa-toggle">';
        echo '<input type="checkbox" name="' . $tg_key . '[daily_report_enabled]" value="1" ' . checked( ! empty( $settings['daily_report_enabled'] ), true, false ) . ' />';
        echo esc_html__( 'Enable morning report', 'niwa-ai' );
        echo '</label>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Send a founder briefing every morning when there are meaningful positive changes. Uses WooCommerce, Niwa analytics, and external providers hooked into niwa_ai_admin_autonomy_external_metrics.', 'niwa-ai' ) . '</span>';
        echo '</div>';

        // Report Hour : Minute (inline row).
        echo '<div class="niwa-field">';
        echo '<span class="niwa-field__label">' . esc_html__( 'Report Time', 'niwa-ai' ) . '</span>';
        echo '<div class="niwa-field__row">';
        echo '<div>';
        echo '<label style="font-size:12px;color:var(--niwa-ink-3);display:block;margin-bottom:3px;">' . esc_html__( 'Hour', 'niwa-ai' ) . '</label>';
        echo '<input type="number" min="0" max="23" name="' . $tg_key . '[daily_report_hour]" value="' . esc_attr( isset( $settings['daily_report_hour'] ) ? $settings['daily_report_hour'] : 8 ) . '" />';
        echo '</div>';
        echo '<div>';
        echo '<label style="font-size:12px;color:var(--niwa-ink-3);display:block;margin-bottom:3px;">' . esc_html__( 'Minute', 'niwa-ai' ) . '</label>';
        echo '<input type="number" min="0" max="59" name="' . $tg_key . '[daily_report_minute]" value="' . esc_attr( isset( $settings['daily_report_minute'] ) ? $settings['daily_report_minute'] : 30 ) . '" />';
        echo '</div>';
        echo '</div>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Time is in the WordPress timezone.', 'niwa-ai' ) . '</span>';
        echo '</div>';

        echo '<div class="niwa-field">';
        echo '<label class="niwa-toggle">';
        echo '<input type="hidden" name="' . $heartbeat_key . '[enabled]" value="0" />';
        echo '<input type="checkbox" name="' . $heartbeat_key . '[enabled]" value="1" ' . checked( empty( $heartbeat_config['paused'] ), true, false ) . ' />';
        echo esc_html__( 'Enable proactive heartbeat', 'niwa-ai' );
        echo '</label>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Runs the built-in proactive workspace check once daily at 14:00 by default, plus due runtime maintenance on background ticks.', 'niwa-ai' ) . '</span>';
        echo '</div>';

        echo '</div>'; // .niwa-card__body
        echo '</div>'; // .niwa-card

        // ─────────────────────────────────────────
        // Card 3: Email Integration (conditional)
        // ─────────────────────────────────────────
        if ( $this->email_service ) {
            $em_key = esc_attr( Niwa_AI_Admin_Autonomy_Email::OPTION_KEY );

            echo '<div class="niwa-card">';
            echo '<div class="niwa-card__header">';
            echo '<div class="niwa-card__icon"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg></div>';
            echo '<h2 class="niwa-card__title">' . esc_html__( 'Email Integration', 'niwa-ai' ) . '</h2>';
            echo '</div>';
            echo '<div class="niwa-card__body">';

            // Enable outbound email toggle.
            echo '<div class="niwa-field">';
            echo '<label class="niwa-toggle">';
            echo '<input type="checkbox" name="' . $em_key . '[send_enabled]" value="1" ' . checked( ! empty( $email_settings['send_enabled'] ), true, false ) . ' />';
            echo esc_html__( 'Enable outbound email', 'niwa-ai' );
            echo '</label>';
            echo '<span class="niwa-field__hint">' . esc_html__( 'Allow Niwa to send email. When Niwa SMTP is enabled below, outbound mail does not require a separate SMTP plugin.', 'niwa-ai' ) . '</span>';
            echo '</div>';

            // From Name.
            echo '<div class="niwa-field">';
            echo '<label class="niwa-field__label">' . esc_html__( 'From Name', 'niwa-ai' ) . '</label>';
            echo '<span class="niwa-field__hint">' . esc_html__( 'Optional sender name. Leave blank to use existing WordPress/SMTP setting.', 'niwa-ai' ) . '</span>';
            echo '<input type="text" name="' . $em_key . '[from_name]" value="' . esc_attr( isset( $email_settings['from_name'] ) ? $email_settings['from_name'] : '' ) . '" class="niwa-input--wide" />';
            echo '</div>';

            // From Email.
            echo '<div class="niwa-field">';
            echo '<label class="niwa-field__label">' . esc_html__( 'From Email', 'niwa-ai' ) . '</label>';
            echo '<span class="niwa-field__hint">' . esc_html__( 'Optional sender address. Leave blank to use the active mail transport.', 'niwa-ai' ) . '</span>';
            echo '<input type="email" name="' . $em_key . '[from_email]" value="' . esc_attr( isset( $email_settings['from_email'] ) ? $email_settings['from_email'] : '' ) . '" class="niwa-input--wide" />';
            echo '</div>';

            // Known Recipients.
            echo '<div class="niwa-field">';
            echo '<label class="niwa-field__label">' . esc_html__( 'Known Recipients', 'niwa-ai' ) . '</label>';
            echo '<span class="niwa-field__hint">' . esc_html__( 'One contact per line. Example: bookkeeper | books@example.com or admin@example.com', 'niwa-ai' ) . '</span>';
            echo '<textarea name="' . $em_key . '[known_recipients]" rows="4" class="niwa-input--wide" style="max-width:100%;">' . esc_textarea( isset( $email_settings['known_recipients'] ) ? $email_settings['known_recipients'] : '' ) . '</textarea>';
            echo '</div>';

            // Enable SMTP toggle.
            echo '<div class="niwa-field">';
            echo '<label class="niwa-toggle">';
            echo '<input type="checkbox" name="' . $em_key . '[smtp_enabled]" value="1" ' . checked( ! empty( $email_settings['smtp_enabled'] ), true, false ) . ' />';
            echo esc_html__( 'Enable Niwa SMTP sending', 'niwa-ai' );
            echo '</label>';
            echo '<span class="niwa-field__hint">' . esc_html__( 'Niwa will configure PHPMailer only for its own email sends. If SMTP username/password are blank, Niwa uses the IMAP username/password below as fallback.', 'niwa-ai' ) . '</span>';
            echo '</div>';

            // SMTP Host.
            echo '<div class="niwa-field">';
            echo '<label class="niwa-field__label">' . esc_html__( 'SMTP Host', 'niwa-ai' ) . '</label>';
            echo '<input type="text" name="' . $em_key . '[smtp_host]" value="' . esc_attr( isset( $email_settings['smtp_host'] ) ? $email_settings['smtp_host'] : 'smtp.hostinger.com' ) . '" class="niwa-input--wide" />';
            echo '</div>';

            // SMTP Port + Encryption.
            echo '<div class="niwa-field">';
            echo '<span class="niwa-field__label">' . esc_html__( 'SMTP Connection', 'niwa-ai' ) . '</span>';
            echo '<div class="niwa-field__row">';
            echo '<div>';
            echo '<label style="font-size:12px;color:var(--niwa-ink-3);display:block;margin-bottom:3px;">' . esc_html__( 'Port', 'niwa-ai' ) . '</label>';
            echo '<input type="number" min="1" max="65535" name="' . $em_key . '[smtp_port]" value="' . esc_attr( isset( $email_settings['smtp_port'] ) ? $email_settings['smtp_port'] : 465 ) . '" />';
            echo '</div>';
            echo '<div>';
            echo '<label style="font-size:12px;color:var(--niwa-ink-3);display:block;margin-bottom:3px;">' . esc_html__( 'Encryption', 'niwa-ai' ) . '</label>';
            echo '<select name="' . $em_key . '[smtp_encryption]">';
            foreach ( array( 'ssl' => 'SSL', 'tls' => 'TLS', 'none' => 'None' ) as $smtp_key => $smtp_label ) {
                echo '<option value="' . esc_attr( $smtp_key ) . '" ' . selected( isset( $email_settings['smtp_encryption'] ) ? $email_settings['smtp_encryption'] : 'ssl', $smtp_key, false ) . '>' . esc_html( $smtp_label ) . '</option>';
            }
            echo '</select>';
            echo '</div>';
            echo '</div>';
            echo '</div>';

            // SMTP Username.
            echo '<div class="niwa-field">';
            echo '<label class="niwa-field__label">' . esc_html__( 'SMTP Username', 'niwa-ai' ) . '</label>';
            echo '<span class="niwa-field__hint">' . esc_html__( 'Leave blank to use the IMAP username.', 'niwa-ai' ) . '</span>';
            echo '<input type="text" name="' . $em_key . '[smtp_username]" value="' . esc_attr( isset( $email_settings['smtp_username'] ) ? $email_settings['smtp_username'] : '' ) . '" class="niwa-input--wide" />';
            echo '</div>';

            // SMTP Password.
            echo '<div class="niwa-field">';
            echo '<label class="niwa-field__label">' . esc_html__( 'SMTP Password', 'niwa-ai' ) . '</label>';
            echo '<span class="niwa-field__hint">' . esc_html__( 'Leave blank to keep the existing SMTP password; if no SMTP password is stored, Niwa uses the IMAP password.', 'niwa-ai' ) . '</span>';
            echo '<input type="password" name="' . $em_key . '[smtp_password]" value="" class="niwa-input--wide" autocomplete="off" />';
            echo '</div>';

            // Enable IMAP toggle.
            echo '<div class="niwa-field">';
            echo '<label class="niwa-toggle">';
            echo '<input type="checkbox" name="' . $em_key . '[imap_enabled]" value="1" ' . checked( ! empty( $email_settings['imap_enabled'] ), true, false ) . ' />';
            echo esc_html__( 'Enable IMAP inbox read', 'niwa-ai' );
            echo '</label>';
            echo '<span class="niwa-field__hint">' . esc_html__( 'Allow Niwa to read received messages via IMAP.', 'niwa-ai' ) . ' PHP IMAP: <strong>' . esc_html( function_exists( 'imap_open' ) ? 'available' : 'missing' ) . '</strong></span>';
            echo '</div>';

            // IMAP Host.
            echo '<div class="niwa-field">';
            echo '<label class="niwa-field__label">' . esc_html__( 'IMAP Host', 'niwa-ai' ) . '</label>';
            echo '<input type="text" name="' . $em_key . '[imap_host]" value="' . esc_attr( isset( $email_settings['imap_host'] ) ? $email_settings['imap_host'] : '' ) . '" class="niwa-input--wide" />';
            echo '</div>';

            // IMAP Port + Encryption (inline row).
            echo '<div class="niwa-field">';
            echo '<span class="niwa-field__label">' . esc_html__( 'IMAP Connection', 'niwa-ai' ) . '</span>';
            echo '<div class="niwa-field__row">';
            echo '<div>';
            echo '<label style="font-size:12px;color:var(--niwa-ink-3);display:block;margin-bottom:3px;">' . esc_html__( 'Port', 'niwa-ai' ) . '</label>';
            echo '<input type="number" min="1" max="65535" name="' . $em_key . '[imap_port]" value="' . esc_attr( isset( $email_settings['imap_port'] ) ? $email_settings['imap_port'] : 993 ) . '" />';
            echo '</div>';
            echo '<div>';
            echo '<label style="font-size:12px;color:var(--niwa-ink-3);display:block;margin-bottom:3px;">' . esc_html__( 'Encryption', 'niwa-ai' ) . '</label>';
            echo '<select name="' . $em_key . '[imap_encryption]">';
            foreach ( array( 'ssl' => 'SSL', 'tls' => 'TLS', 'notls' => 'No TLS' ) as $imap_key => $imap_label ) {
                echo '<option value="' . esc_attr( $imap_key ) . '" ' . selected( isset( $email_settings['imap_encryption'] ) ? $email_settings['imap_encryption'] : 'ssl', $imap_key, false ) . '>' . esc_html( $imap_label ) . '</option>';
            }
            echo '</select>';
            echo '</div>';
            echo '</div>';
            echo '</div>';

            // IMAP Username.
            echo '<div class="niwa-field">';
            echo '<label class="niwa-field__label">' . esc_html__( 'IMAP Username', 'niwa-ai' ) . '</label>';
            echo '<input type="text" name="' . $em_key . '[imap_username]" value="' . esc_attr( isset( $email_settings['imap_username'] ) ? $email_settings['imap_username'] : '' ) . '" class="niwa-input--wide" />';
            echo '</div>';

            // IMAP Password.
            echo '<div class="niwa-field">';
            echo '<label class="niwa-field__label">' . esc_html__( 'IMAP Password', 'niwa-ai' ) . '</label>';
            echo '<span class="niwa-field__hint">' . esc_html__( 'Leave blank to keep the existing password.', 'niwa-ai' ) . '</span>';
            echo '<input type="password" name="' . $em_key . '[imap_password]" value="" class="niwa-input--wide" autocomplete="off" />';
            echo '</div>';

            // IMAP Mailbox.
            echo '<div class="niwa-field">';
            echo '<label class="niwa-field__label">' . esc_html__( 'IMAP Mailbox', 'niwa-ai' ) . '</label>';
            echo '<span class="niwa-field__hint">' . esc_html__( 'Usually INBOX.', 'niwa-ai' ) . '</span>';
            echo '<input type="text" name="' . $em_key . '[imap_mailbox]" value="' . esc_attr( isset( $email_settings['imap_mailbox'] ) ? $email_settings['imap_mailbox'] : 'INBOX' ) . '" class="niwa-input--wide" />';
            echo '</div>';

            // Mail watcher cadence.
            $poll_interval = class_exists( 'Niwa_AI_Admin_Autonomy_Email' )
                ? Niwa_AI_Admin_Autonomy_Email::normalize_inbound_poll_interval_minutes( isset( $email_settings['inbound_poll_interval_minutes'] ) ? $email_settings['inbound_poll_interval_minutes'] : 15 )
                : 15;
            $poll_choices = class_exists( 'Niwa_AI_Admin_Autonomy_Email' )
                ? Niwa_AI_Admin_Autonomy_Email::get_inbound_poll_interval_choices()
                : array();

            echo '<div class="niwa-field">';
            echo '<label class="niwa-field__label">' . esc_html__( 'Mail watcher interval', 'niwa-ai' ) . '</label>';
            echo '<span class="niwa-field__hint">' . esc_html__( 'How often Niwa checks the IMAP mailbox for new messages. Default is 15 minutes.', 'niwa-ai' ) . '</span>';
            echo '<select name="' . $em_key . '[inbound_poll_interval_minutes]">';
            foreach ( $poll_choices as $minutes => $choice ) {
                $label = isset( $choice['label'] ) ? (string) $choice['label'] : sprintf( __( 'Every %d minutes', 'niwa-ai' ), (int) $minutes );
                echo '<option value="' . esc_attr( (string) $minutes ) . '" ' . selected( $poll_interval, (int) $minutes, false ) . '>' . esc_html( $label ) . '</option>';
            }
            echo '</select>';
            echo '</div>';

            echo '</div>'; // .niwa-card__body
            echo '</div>'; // .niwa-card
        }

        // ─────────────────────────────────────────
        // Card 4: Stock Managers
        // ─────────────────────────────────────────
        $stock_managers = $this->telegram->get_stock_managers();
        $max_sm         = Niwa_AI_Admin_Autonomy_Telegram::MAX_STOCK_MANAGERS;
        $option_key     = esc_attr( Niwa_AI_Admin_Autonomy_Telegram::OPTION_KEY );

        echo '<div class="niwa-card">';
        echo '<div class="niwa-card__header">';
        echo '<div class="niwa-card__icon"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg></div>';
        echo '<h2 class="niwa-card__title">' . esc_html__( 'Stock Managers', 'niwa-ai' ) . ' <span class="niwa-badge niwa-badge--neutral">' . count( $stock_managers ) . '/' . intval( $max_sm ) . '</span></h2>';
        echo '</div>';
        echo '<div class="niwa-card__body">';

        echo '<span class="niwa-field__hint" style="display:block;margin-bottom:12px;">' . esc_html__( 'Stock managers have bot access exclusively for managing WooCommerce products. They cannot access WordPress core files, sales data, email, or anything outside the product catalog. Each stock manager uses their own Telegram chat ID.', 'niwa-ai' ) . '</span>';

        echo '<table class="niwa-table" id="niwa-stock-managers-table">';
        echo '<thead><tr><th>' . esc_html__( 'Telegram Chat ID', 'niwa-ai' ) . '</th><th>' . esc_html__( 'Label', 'niwa-ai' ) . '</th><th style="width:70px;">' . esc_html__( 'Action', 'niwa-ai' ) . '</th></tr></thead>';
        echo '<tbody id="niwa-stock-managers-body">';

        if ( ! empty( $stock_managers ) ) {
            foreach ( $stock_managers as $i => $sm ) {
                $chat_id = esc_attr( isset( $sm['chat_id'] ) ? $sm['chat_id'] : '' );
                $label   = esc_attr( isset( $sm['label'] ) ? $sm['label'] : '' );
                echo '<tr class="niwa-sm-row">';
                echo '<td><input type="text" name="' . $option_key . '[stock_managers][' . $i . '][chat_id]" value="' . $chat_id . '" class="niwa-input--wide" placeholder="e.g. 123456789" /></td>';
                echo '<td><input type="text" name="' . $option_key . '[stock_managers][' . $i . '][label]" value="' . $label . '" class="niwa-input--wide" placeholder="e.g. Marko - warehouse" /></td>';
                echo '<td><button type="button" class="button niwa-btn niwa-btn--secondary niwa-sm-remove" title="' . esc_attr__( 'Remove', 'niwa-ai' ) . '">&times;</button></td>';
                echo '</tr>';
            }
        }

        echo '</tbody>';
        echo '</table>';

        echo '<div style="margin-top:10px;">';
        echo '<button type="button" class="button niwa-btn niwa-btn--secondary" id="niwa-sm-add">+ ' . esc_html__( 'Add Stock Manager', 'niwa-ai' ) . '</button>';
        echo '</div>';

        wp_enqueue_script(
            'niwa-admin-stock-managers',
            NIWA_AI_URL . 'assets/js/niwa-admin-stock-managers.js',
            array(),
            NIWA_AI_VERSION,
            array( 'in_footer' => true )
        );
        wp_localize_script( 'niwa-admin-stock-managers', 'niwaAdminStockManagers', array(
            'maxSm'  => intval( $max_sm ),
            'optKey' => Niwa_AI_Admin_Autonomy_Telegram::OPTION_KEY,
        ) );

        echo '</div>'; // .niwa-card__body
        echo '</div>'; // .niwa-card

        // ─────────────────────────────────────────
        // Card 4b: Unauthorized User Introduction
        // ─────────────────────────────────────────
        $intro_enabled  = ! empty( $settings['unauthorized_intro_enabled'] );
        $intro_template = isset( $settings['unauthorized_intro_template'] ) ? (string) $settings['unauthorized_intro_template'] : '';
        $recent_unauth  = $this->telegram->get_recent_unauthorized_chats();
        $option_key     = esc_attr( Niwa_AI_Admin_Autonomy_Telegram::OPTION_KEY );

        echo '<div class="niwa-card">';
        echo '<div class="niwa-card__header">';
        echo '<div class="niwa-card__icon"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v6"/><path d="M5 12a7 7 0 0 1 14 0"/><circle cx="12" cy="18" r="4"/></svg></div>';
        echo '<h2 class="niwa-card__title">' . esc_html__( 'Unauthorized User Introduction', 'niwa-ai' ) . '</h2>';
        echo '</div>';
        echo '<div class="niwa-card__body">';

        echo '<span class="niwa-field__hint" style="display:block;margin-bottom:12px;">' . esc_html__( 'When a Telegram user who is not the admin owner and not on the Stock Managers list writes to the bot, Niwa replies once with their own Telegram chat ID and short instructions so they can forward it to you. Throttled to one reply per chat in 24h, capped at 3 replies per hour per chat.', 'niwa-ai' ) . '</span>';

        echo '<div class="niwa-field">';
        echo '<label class="niwa-field__checkbox">';
        echo '<input type="checkbox" name="' . $option_key . '[unauthorized_intro_enabled]" value="1" ' . checked( $intro_enabled, true, false ) . ' /> ';
        echo '<span>' . esc_html__( 'Auto-reply to unauthorized users with their Telegram ID', 'niwa-ai' ) . '</span>';
        echo '</label>';
        echo '</div>';

        echo '<div class="niwa-field" style="margin-top:12px;">';
        echo '<label class="niwa-field__label">' . esc_html__( 'Custom message template (optional)', 'niwa-ai' ) . '</label>';
        echo '<textarea name="' . $option_key . '[unauthorized_intro_template]" rows="6" class="niwa-input--wide" style="max-width:100%;" placeholder="' . esc_attr__( 'Leave empty to use the default localized message. Available placeholders: {chat_id}, {username}, {username_raw}, {first_name}', 'niwa-ai' ) . '">' . esc_textarea( $intro_template ) . '</textarea>';
        echo '<span class="niwa-field__hint">' . esc_html__( 'Available placeholders: {chat_id}, {username}, {username_raw}, {first_name}. Telegram HTML formatting is supported (<b>, <i>, <code>, etc.).', 'niwa-ai' ) . '</span>';
        echo '</div>';

        if ( ! empty( $recent_unauth ) ) {
            echo '<div class="niwa-field" style="margin-top:18px;">';
            echo '<label class="niwa-field__label">' . esc_html__( 'Recent unauthorized users (last 10)', 'niwa-ai' ) . '</label>';
            echo '<table class="niwa-table" style="margin-top:6px;">';
            echo '<thead><tr>';
            echo '<th>' . esc_html__( 'Telegram Chat ID', 'niwa-ai' ) . '</th>';
            echo '<th>' . esc_html__( 'Username', 'niwa-ai' ) . '</th>';
            echo '<th>' . esc_html__( 'First name', 'niwa-ai' ) . '</th>';
            echo '<th>' . esc_html__( 'Last seen', 'niwa-ai' ) . '</th>';
            echo '<th>' . esc_html__( 'Hits', 'niwa-ai' ) . '</th>';
            echo '</tr></thead>';
            echo '<tbody>';
            foreach ( $recent_unauth as $row ) {
                $username_disp = '' !== $row['username'] ? '@' . $row['username'] : '—';
                echo '<tr>';
                echo '<td><code>' . esc_html( $row['chat_id'] ) . '</code></td>';
                echo '<td>' . esc_html( $username_disp ) . '</td>';
                echo '<td>' . esc_html( '' !== $row['first_name'] ? $row['first_name'] : '—' ) . '</td>';
                echo '<td>' . esc_html( $this->format_timestamp( intval( $row['last_seen'] ) ) ) . '</td>';
                echo '<td>' . intval( $row['count'] ) . '</td>';
                echo '</tr>';
            }
            echo '</tbody></table>';
            echo '<span class="niwa-field__hint" style="margin-top:6px;display:block;">' . esc_html__( 'Copy any Chat ID above into the Owner field or add it as a Stock Manager to grant access.', 'niwa-ai' ) . '</span>';
            echo '</div>';
        }

        echo '</div>'; // .niwa-card__body
        echo '</div>'; // .niwa-card

        echo '<div class="niwa-save-panel">';
        echo '<div class="niwa-save-panel__copy">';
        echo '<strong>' . esc_html__( 'Save setup and access settings', 'niwa-ai' ) . '</strong>';
        echo '<span>' . esc_html__( 'Saves Telegram, runtime parameters, email, stock managers, and unauthorized-user introduction settings together.', 'niwa-ai' ) . '</span>';
        echo '</div>';
        submit_button( __( 'Save Personal Assistant Settings', 'niwa-ai' ), 'primary niwa-btn niwa-btn--primary', 'submit', false );
        echo '</div>';

        // ══════════════════════════════════════════
        // ── FORM END ──
        // ══════════════════════════════════════════
        echo '</form>';

        } // end setup tab

        // ══════════════════════════════════════════
        // ── PERFORMANCE TAB ──
        // ══════════════════════════════════════════
        if ( 'performance' === $active_tab ) {
            if ( $this->performance_settings_ui && method_exists( $this->performance_settings_ui, 'render_embedded_form' ) ) {
                $this->performance_settings_ui->render_embedded_form();
            } else {
                echo '<div class="niwa-alert niwa-alert--error">';
                echo esc_html__( 'Performance settings are unavailable in this runtime.', 'niwa-ai' );
                echo '</div>';
            }
        }

        // ══════════════════════════════════════════
        // ── RUNTIME TAB ──
        // ══════════════════════════════════════════
        if ( 'runtime' === $active_tab ) {

        // ─────────────────────────────────────────
        // Card 5: Telegram Webhook Runtime (read-only, outside form)
        // ─────────────────────────────────────────
        echo '<div class="niwa-card">';
        echo '<div class="niwa-card__header">';
        echo '<div class="niwa-card__icon"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" 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></div>';
        echo '<h2 class="niwa-card__title">' . esc_html__( 'Telegram Webhook Runtime', 'niwa-ai' ) . '</h2>';
        echo '</div>';
        echo '<div class="niwa-card__body">';
        echo '<div class="niwa-alert niwa-alert--info">';
        echo '<strong>' . esc_html__( 'Status and manual action:', 'niwa-ai' ) . '</strong> ' . esc_html__( 'These values show the live Telegram runtime state. Use the button at the bottom only when you want to force an immediate runtime pass.', 'niwa-ai' );
        echo '</div>';

        echo '<div class="niwa-alert niwa-alert--info">';
        echo '<strong>' . esc_html__( 'Prompt cache:', 'niwa-ai' ) . '</strong> ' . esc_html__( 'Shows cached input-token reuse reported by OpenAI usage data. Higher hit rate means more of the repeated prompt prefix is being reused.', 'niwa-ai' );
        echo '</div>';

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

        $last_cache = isset( $prompt_cache_status['last'] ) && is_array( $prompt_cache_status['last'] ) ? $prompt_cache_status['last'] : array();
        $last_rate  = isset( $last_cache['cache_hit_rate'] ) ? (float) $last_cache['cache_hit_rate'] : 0.0;

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Last Cache Hit Rate', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( ! empty( $prompt_cache_status['has_samples'] ) ? $this->format_prompt_cache_rate( $last_rate ) : '-' ) . '</div>';
        echo '</div>';

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Rolling Cache Hit Rate', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( ! empty( $prompt_cache_status['has_samples'] ) ? $this->format_prompt_cache_rate( $prompt_cache_status['aggregate_cache_hit_rate'] ) : '-' ) . '</div>';
        echo '</div>';

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Cached Input Tokens', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( ! empty( $prompt_cache_status['has_samples'] ) ? $this->format_token_count( $prompt_cache_status['aggregate_cached_tokens'] ) : '0' ) . '</div>';
        echo '</div>';

        echo '</div>'; // .niwa-stats

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

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Prompt Tokens Observed', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( ! empty( $prompt_cache_status['has_samples'] ) ? $this->format_token_count( $prompt_cache_status['aggregate_prompt_tokens'] ) : '0' ) . '</div>';
        echo '</div>';

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Samples', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( (string) intval( $prompt_cache_status['sample_count'] ) ) . '</div>';
        echo '</div>';

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Last Sample', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( ! empty( $prompt_cache_status['updated_at'] ) ? $this->format_timestamp( $prompt_cache_status['updated_at'] ) : '-' ) . '</div>';
        echo '</div>';

        echo '</div>'; // .niwa-stats

        if ( ! empty( $last_cache ) ) {
            $last_model  = isset( $last_cache['model'] ) && '' !== (string) $last_cache['model'] ? (string) $last_cache['model'] : '-';
            $last_prompt = isset( $last_cache['prompt_tokens'] ) ? intval( $last_cache['prompt_tokens'] ) : 0;
            $last_cached = isset( $last_cache['cached_tokens'] ) ? intval( $last_cache['cached_tokens'] ) : 0;
            echo '<div class="niwa-alert niwa-alert--info">';
            echo '<strong>' . esc_html__( 'Last prompt cache sample:', 'niwa-ai' ) . '</strong> ';
            echo esc_html( $last_model . ' | ' . $this->format_token_count( $last_cached ) . ' cached / ' . $this->format_token_count( $last_prompt ) . ' input tokens' );
            echo '</div>';
        }

        // Row 1: Webhook status, last webhook delivery, fallback offset.
        echo '<div class="niwa-stats">';

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Webhook Status', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( ! empty( $webhook_state['active'] ) ? __( 'Active', 'niwa-ai' ) : __( 'Fallback', 'niwa-ai' ) ) . '</div>';
        echo '</div>';

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Last Webhook Delivery', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( $this->format_timestamp( isset( $webhook_state['last_delivery_at'] ) ? $webhook_state['last_delivery_at'] : 0 ) ) . '</div>';
        echo '</div>';

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Fallback Offset', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( isset( $poll_state['offset'] ) ? (string) $poll_state['offset'] : '0' ) . '</div>';
        echo '</div>';

        echo '</div>'; // .niwa-stats

        // Row 2: Runner Last Seen, Runner Active Until, next fallback poll.
        echo '<div class="niwa-stats">';

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Runner Last Seen', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( $this->format_timestamp( isset( $runner_state['last_seen_at'] ) ? $runner_state['last_seen_at'] : 0 ) ) . '</div>';
        echo '</div>';

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Runner Active Until', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( $this->format_timestamp( isset( $runner_state['active_until'] ) ? $runner_state['active_until'] : 0 ) ) . '</div>';
        echo '</div>';

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Next Fallback Poll', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( $this->format_timestamp( $next_poll ) ) . '</div>';
        echo '</div>';

        echo '</div>'; // .niwa-stats

        // Row 3: Report Last Sent, Report Status.
        echo '<div class="niwa-stats">';

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Report Last Sent', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( isset( $report_state['last_sent_at'] ) ? $this->format_timestamp( $report_state['last_sent_at'] ) : '-' ) . '</div>';
        echo '</div>';

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Report Status', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( isset( $report_state['last_status'] ) ? (string) $report_state['last_status'] : '-' ) . '</div>';
        echo '</div>';

        echo '</div>'; // .niwa-stats

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

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Report Last Attempt', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( isset( $report_state['last_attempt_at'] ) ? $this->format_timestamp( $report_state['last_attempt_at'] ) : '-' ) . '</div>';
        echo '</div>';

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Report Reason', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( isset( $report_state['last_reason'] ) && '' !== (string) $report_state['last_reason'] ? (string) $report_state['last_reason'] : '-' ) . '</div>';
        echo '</div>';

        echo '</div>'; // .niwa-stats

        if ( ! empty( $report_state['last_summary'] ) ) {
            echo '<div class="niwa-alert niwa-alert--info">';
            echo '<strong>' . esc_html__( 'Morning Report Summary:', 'niwa-ai' ) . '</strong> ' . esc_html( (string) $report_state['last_summary'] );
            echo '</div>';
        }

        if ( ! empty( $report_state['last_error'] ) ) {
            echo '<div class="niwa-alert niwa-alert--error">';
            echo '<strong>' . esc_html__( 'Morning Report Error:', 'niwa-ai' ) . '</strong> ' . esc_html( (string) $report_state['last_error'] );
            echo '</div>';
        }

        // Row 4: Heartbeat Last Check, Heartbeat Status.
        echo '<div class="niwa-stats">';

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Heartbeat Last Check', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( isset( $heartbeat_state['last_checked_at'] ) ? $this->format_timestamp( $heartbeat_state['last_checked_at'] ) : '-' ) . '</div>';
        echo '</div>';

        echo '<div class="niwa-stat">';
        echo '<div class="niwa-stat__label">' . esc_html__( 'Heartbeat Status', 'niwa-ai' ) . '</div>';
        echo '<div class="niwa-stat__value">' . esc_html( isset( $heartbeat_state['last_status'] ) ? (string) $heartbeat_state['last_status'] : '-' ) . '</div>';
        echo '</div>';

        // Heartbeat error detail (when last_status is llm_error).
        if ( ! empty( $heartbeat_state['last_error'] ) ) {
            echo '<div class="niwa-stat" style="grid-column: 1 / -1;">';
            echo '<div class="niwa-stat__label">' . esc_html__( 'Heartbeat Error', 'niwa-ai' ) . '</div>';
            echo '<div class="niwa-stat__value" style="color: var(--niwa-danger, #ef4444); font-size: 0.8rem; word-break: break-word;">' . esc_html( (string) $heartbeat_state['last_error'] ) . '</div>';
            echo '</div>';
        }

        echo '</div>'; // .niwa-stats

        echo '<div class="niwa-alert niwa-alert--info">';
        echo '<strong>' . esc_html__( 'Operative heartbeat:', 'niwa-ai' ) . '</strong> ' . esc_html__( 'These channels run founder-requested recurring or multi-tick work. One-time future site changes should appear as scheduled jobs, not as heartbeat tasks.', 'niwa-ai' );
        echo '</div>';

        foreach ( $operative_heartbeat_status as $hb_status ) {
            echo '<div class="niwa-stats">';

            echo '<div class="niwa-stat">';
            echo '<div class="niwa-stat__label">' . esc_html( $hb_status['label'] ) . '</div>';
            echo '<div class="niwa-stat__value">' . esc_html( ! empty( $hb_status['enabled'] ) ? __( 'Enabled', 'niwa-ai' ) : __( 'Disabled', 'niwa-ai' ) ) . '</div>';
            echo '</div>';

            echo '<div class="niwa-stat">';
            echo '<div class="niwa-stat__label">' . esc_html__( 'Interval', 'niwa-ai' ) . '</div>';
            echo '<div class="niwa-stat__value">' . esc_html( rtrim( rtrim( number_format( (float) $hb_status['interval_minutes'], 2, '.', '' ), '0' ), '.' ) . ' min' ) . '</div>';
            echo '</div>';

            echo '<div class="niwa-stat">';
            echo '<div class="niwa-stat__label">' . esc_html__( 'Delivery', 'niwa-ai' ) . '</div>';
            echo '<div class="niwa-stat__value">' . esc_html( '' !== (string) $hb_status['delivery_channel'] ? (string) $hb_status['delivery_channel'] : 'telegram' ) . '</div>';
            echo '</div>';

            echo '</div>'; // .niwa-stats

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

            echo '<div class="niwa-stat">';
            echo '<div class="niwa-stat__label">' . esc_html__( 'Last Attempt', 'niwa-ai' ) . '</div>';
            echo '<div class="niwa-stat__value">' . esc_html( $this->format_timestamp( $hb_status['last_attempt_at'] ) ) . '</div>';
            echo '</div>';

            echo '<div class="niwa-stat">';
            echo '<div class="niwa-stat__label">' . esc_html__( 'Last Run', 'niwa-ai' ) . '</div>';
            echo '<div class="niwa-stat__value">' . esc_html( $this->format_timestamp( $hb_status['last_run_at'] ) ) . '</div>';
            echo '</div>';

            echo '<div class="niwa-stat">';
            echo '<div class="niwa-stat__label">' . esc_html__( 'Status', 'niwa-ai' ) . '</div>';
            echo '<div class="niwa-stat__value">' . esc_html( '' !== (string) $hb_status['last_status'] ? (string) $hb_status['last_status'] : '-' ) . '</div>';
            echo '</div>';

            echo '</div>'; // .niwa-stats

            if ( '' !== (string) $hb_status['brief_summary'] || '' !== (string) $hb_status['last_summary'] ) {
                echo '<div class="niwa-alert niwa-alert--info">';
                echo '<strong>' . esc_html( (string) $hb_status['brief_file'] ) . ':</strong> ';
                echo esc_html( '' !== (string) $hb_status['brief_summary'] ? (string) $hb_status['brief_summary'] : (string) $hb_status['last_summary'] );
                echo '</div>';
            }
        }

        echo '<div class="niwa-alert niwa-alert--info">';
        echo '<strong>' . esc_html__( 'Scheduled jobs:', 'niwa-ai' ) . '</strong> ' . esc_html__( 'One-time future site changes and delayed reversals should appear here.', 'niwa-ai' );
        echo '</div>';

        if ( empty( $active_scheduled_jobs ) ) {
            echo '<div class="niwa-stat" style="margin-bottom:14px;">';
            echo '<div class="niwa-stat__label">' . esc_html__( 'Active Scheduled Jobs', 'niwa-ai' ) . '</div>';
            echo '<div class="niwa-stat__value">' . esc_html__( 'None', 'niwa-ai' ) . '</div>';
            echo '</div>';
        } else {
            foreach ( $active_scheduled_jobs as $job ) {
                $title = isset( $job['title'] ) ? (string) $job['title'] : __( 'Scheduled job', 'niwa-ai' );
                $meta  = sprintf(
                    '#%1$d %2$s | %3$s | %4$s',
                    intval( isset( $job['id'] ) ? $job['id'] : 0 ),
                    isset( $job['job_type'] ) ? (string) $job['job_type'] : '',
                    isset( $job['schedule_kind'] ) ? (string) $job['schedule_kind'] : '',
                    isset( $job['next_run_local'] ) && '' !== (string) $job['next_run_local'] ? (string) $job['next_run_local'] : '-'
                );

                echo '<div class="niwa-stats">';
                echo '<div class="niwa-stat" style="grid-column: 1 / -1;">';
                echo '<div class="niwa-stat__label">' . esc_html( $meta ) . '</div>';
                echo '<div class="niwa-stat__value">' . esc_html( $this->truncate_runtime_text( $title, 180 ) ) . '</div>';
                if ( ! empty( $job['last_error'] ) ) {
                    echo '<div class="niwa-stat__value" style="color: var(--niwa-danger, #ef4444); font-size: 0.8rem; word-break: break-word;">' . esc_html( (string) $job['last_error'] ) . '</div>';
                }
                echo '</div>';
                echo '</div>';
            }
        }

        // Runner last error alert.
        if ( ! empty( $runner_state['last_error'] ) ) {
            echo '<div class="niwa-alert niwa-alert--error">';
            echo '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" 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="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg> ';
            echo '<strong>' . esc_html__( 'Runner Error:', 'niwa-ai' ) . '</strong> ' . esc_html( (string) $runner_state['last_error'] );
            echo '</div>';
        }

        // Info alert about founder memory and auto-runner.
        echo '<div class="niwa-alert niwa-alert--info">';
        echo '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" 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="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg> ';
        echo esc_html__( 'Transport: Telegram webhook wakes Niwa on every delivered update. Fallback polling stays available only when Telegram reports the webhook as unhealthy.', 'niwa-ai' );
        echo '</div>';

        echo '</div>'; // .niwa-card__body

        // Poll Now button in footer.
        echo '<div class="niwa-card__footer">';
        echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '" style="display:inline-block;">';
        echo '<input type="hidden" name="action" value="niwa_ai_admin_autonomy_poll_now" />';
        wp_nonce_field( 'niwa_ai_admin_autonomy_poll_now', 'niwa_ai_admin_autonomy_poll_now_nonce' );
        submit_button( __( 'Run Telegram Runtime Now', 'niwa-ai' ), 'secondary niwa-btn niwa-btn--secondary', 'submit', false );
        echo '</form>';
        foreach ( $operative_heartbeat_status as $hb_status ) {
            if ( empty( $hb_status['enabled'] ) ) {
                continue;
            }
            echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '" style="display:inline-block;margin-left:8px;">';
            echo '<input type="hidden" name="action" value="niwa_ai_admin_autonomy_disable_operative_heartbeat" />';
            echo '<input type="hidden" name="heartbeat_role" value="' . esc_attr( (string) $hb_status['role'] ) . '" />';
            wp_nonce_field( 'niwa_ai_disable_operative_heartbeat', 'niwa_ai_disable_operative_heartbeat_nonce' );
            submit_button(
                sprintf(
                    /* translators: %s: heartbeat channel label */
                    __( 'Disable %s', 'niwa-ai' ),
                    (string) $hb_status['label']
                ),
                'secondary niwa-btn niwa-btn--secondary',
                'submit',
                false
            );
            echo '</form>';
        }
        echo '</div>';

        echo '</div>'; // .niwa-card

        } // end runtime tab

        // ══════════════════════════════════════════
        // ── CAPABILITIES TAB ──
        // ══════════════════════════════════════════
        if ( 'capabilities' === $active_tab ) {

        // ─────────────────────────────────────────
        // Card 6: Founder Chat (info card)
        // ─────────────────────────────────────────
        echo '<div class="niwa-card">';
        echo '<div class="niwa-card__header">';
        echo '<div class="niwa-card__icon"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg></div>';
        echo '<h2 class="niwa-card__title">' . esc_html__( 'Founder Chat', 'niwa-ai' ) . '</h2>';
        echo '</div>';
        echo '<div class="niwa-card__body">';

        echo '<p>' . esc_html__( 'Founder chat accepts natural conversation without special commands or formatting. The agent decides when to read memory, search code, check the database, WooCommerce data, or execute changes.', 'niwa-ai' ) . '</p>';

        echo '<div class="niwa-kv">';
        echo '<span class="niwa-kv__key">' . esc_html__( 'Site & Content', 'niwa-ai' ) . '</span>';
        echo '<span class="niwa-kv__value">' . esc_html__( 'Pages, posts, headlines, Elementor/raw content where a reliable target exists', 'niwa-ai' ) . '</span>';
        echo '</div>';

        echo '<div class="niwa-kv">';
        echo '<span class="niwa-kv__key">' . esc_html__( 'WooCommerce', 'niwa-ai' ) . '</span>';
        echo '<span class="niwa-kv__value">' . esc_html__( 'Products, trash restore, simple/variable products, stock, price, featured image', 'niwa-ai' ) . '</span>';
        echo '</div>';

        echo '<div class="niwa-kv">';
        echo '<span class="niwa-kv__key">' . esc_html__( 'Code & Environment', 'niwa-ai' ) . '</span>';
        echo '<span class="niwa-kv__value">' . esc_html__( 'Search and read the entire local WordPress environment, raw file edit within the same installation', 'niwa-ai' ) . '</span>';
        echo '</div>';

        echo '<div class="niwa-kv">';
        echo '<span class="niwa-kv__key">' . esc_html__( 'Discovery & DB', 'niwa-ai' ) . '</span>';
        echo '<span class="niwa-kv__value">' . esc_html__( 'Active plugins, hooks, shortcodes, CPTs, REST routes, tables, schema, fingerprints, sample rows', 'niwa-ai' ) . '</span>';
        echo '</div>';

        echo '<div class="niwa-kv">';
        echo '<span class="niwa-kv__key">' . esc_html__( 'Workspace & Reporting', 'niwa-ai' ) . '</span>';
        echo '<span class="niwa-kv__value">' . esc_html__( 'Snapshots, XLSX export and file delivery to the founder via Telegram', 'niwa-ai' ) . '</span>';
        echo '</div>';

        echo '<div class="niwa-kv">';
        echo '<span class="niwa-kv__key">' . esc_html__( 'Email', 'niwa-ai' ) . '</span>';
        echo '<span class="niwa-kv__value">' . esc_html__( 'Send mail via WordPress/SMTP, lookup known recipients, lead follow-up, and inbox reading via IMAP', 'niwa-ai' ) . '</span>';
        echo '</div>';

        echo '<div class="niwa-alert niwa-alert--info" style="margin-top:14px;">';
        echo '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" 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="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg> ';
        echo '<strong>' . esc_html__( 'Execution Mode:', 'niwa-ai' ) . '</strong> ' . esc_html__( 'Immediate execution is active. Founder requests are executed immediately or queued in background when the request requires a deeper agent turn.', 'niwa-ai' );
        echo '</div>';

        echo '<p style="margin-top:10px;font-size:12px;color:var(--niwa-ink-3);">' . esc_html__( 'Optional slash commands:', 'niwa-ai' ) . ' <code>/status</code> <code>/sessions</code> <code>/sessions status &lt;id&gt;</code> <code>/sessions history &lt;id&gt;</code> <code>/sessions send &lt;id&gt; &lt;task&gt;</code> <code>/subagents</code> <code>/subagents logs &lt;id&gt;</code> <code>/subagents kill &lt;id&gt;</code> <code>/subagents steer &lt;id&gt; &lt;msg&gt;</code> <code>/compact</code> <code>/new</code> <code>/rollback</code> <code>/heartbeat</code></p>';

        echo '</div>'; // .niwa-card__body
        echo '</div>'; // .niwa-card

        // ─────────────────────────────────────────
        // Card 7: Recent Runs
        // ─────────────────────────────────────────
        if ( ! empty( $recent_runs ) ) {
            echo '<div class="niwa-card">';
            echo '<div class="niwa-card__header">';
            echo '<div class="niwa-card__icon"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" 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"/><polyline points="12 6 12 12 16 14"/></svg></div>';
            echo '<h2 class="niwa-card__title">' . esc_html__( 'Recent Runs', 'niwa-ai' ) . '</h2>';
            echo '</div>';
            echo '<div class="niwa-card__body" style="padding:0;overflow-x:auto;">';

            echo '<table class="niwa-table">';
            echo '<thead><tr>';
            echo '<th>' . esc_html__( 'ID', 'niwa-ai' ) . '</th>';
            echo '<th>' . esc_html__( 'Prompt', 'niwa-ai' ) . '</th>';
            echo '<th>' . esc_html__( 'Approval', 'niwa-ai' ) . '</th>';
            echo '<th>' . esc_html__( 'Execution', 'niwa-ai' ) . '</th>';
            echo '<th>' . esc_html__( 'Rollback', 'niwa-ai' ) . '</th>';
            echo '<th>' . esc_html__( 'Created', 'niwa-ai' ) . '</th>';
            echo '<th>' . esc_html__( 'Executed', 'niwa-ai' ) . '</th>';
            echo '</tr></thead>';
            echo '<tbody>';

            foreach ( $recent_runs as $run ) {
                // Badge helpers for status columns.
                $approval_raw  = isset( $run['approval_state'] ) ? (string) $run['approval_state'] : '';
                $execution_raw = isset( $run['execution_state'] ) ? (string) $run['execution_state'] : '';
                $rollback_raw  = isset( $run['rollback_status'] ) ? (string) $run['rollback_status'] : '';

                echo '<tr>';
                echo '<td>' . esc_html( (string) $run['id'] ) . '</td>';
                echo '<td>' . esc_html( $this->truncate_text( isset( $run['prompt_text'] ) ? (string) $run['prompt_text'] : '' ) ) . '</td>';
                echo '<td>' . $this->render_status_badge( $approval_raw ) . '</td>';
                echo '<td>' . $this->render_status_badge( $execution_raw ) . '</td>';
                echo '<td>' . $this->render_status_badge( $rollback_raw ) . '</td>';
                echo '<td>' . esc_html( $this->format_mysql_date( isset( $run['created_at'] ) ? $run['created_at'] : '' ) ) . '</td>';
                echo '<td>' . esc_html( $this->format_mysql_date( isset( $run['executed_at'] ) ? $run['executed_at'] : '' ) ) . '</td>';
                echo '</tr>';
            }

            echo '</tbody>';
            echo '</table>';

            echo '</div>'; // .niwa-card__body
            echo '</div>'; // .niwa-card
        }

        } // end capabilities tab

        echo '</div>'; // .wrap .niwa-wrap
    }



















    /**
     * Render a status badge for recent runs table.
     *
     * @param string $status The status string.
     * @return string HTML badge markup.
     */
    private function render_status_badge( $status ) {
        if ( '' === $status ) {
            return '<span class="niwa-badge niwa-badge--neutral">-</span>';
        }

        $lower = strtolower( $status );
        $class = 'niwa-badge--neutral';

        if ( in_array( $lower, array( 'approved', 'executed', 'done', 'success', 'complete', 'completed' ), true ) ) {
            $class = 'niwa-badge--success';
        } elseif ( in_array( $lower, array( 'pending', 'queued', 'waiting', 'running', 'in_progress' ), true ) ) {
            $class = 'niwa-badge--warning';
        } elseif ( in_array( $lower, array( 'failed', 'error', 'rejected', 'rolled_back', 'rollback' ), true ) ) {
            $class = 'niwa-badge--error';
        }

        return '<span class="niwa-badge ' . esc_attr( $class ) . '">' . esc_html( $status ) . '</span>';
    }

    private function format_mysql_date( $gmt_mysql ) {
        $gmt_mysql = trim( (string) $gmt_mysql );
        if ( '' === $gmt_mysql || '0000-00-00 00:00:00' === $gmt_mysql ) {
            return '-';
        }

        return get_date_from_gmt( $gmt_mysql, 'Y-m-d H:i:s' );
    }

    private function format_timestamp( $timestamp ) {
        $timestamp = intval( $timestamp );
        if ( $timestamp <= 0 ) {
            return '-';
        }

        return wp_date( 'Y-m-d H:i:s', $timestamp );
    }

    private function sanitize_model_id( $value ) {
        $value = sanitize_text_field( (string) $value );
        return preg_match( '/^[a-zA-Z0-9._:-]{2,80}$/', $value ) ? $value : '';
    }

    private function sanitize_image_quality( $value ) {
        $value = sanitize_key( (string) $value );
        return in_array( $value, array( 'low', 'medium', 'high' ), true ) ? $value : 'medium';
    }

    private function resolve_founder_auth_mode( $settings, $auth_opts ) {
        $settings = is_array( $settings ) ? $settings : array();
        $auth_opts = is_array( $auth_opts ) ? $auth_opts : array();

        $founder_mode = isset( $settings['founder_auth_mode'] ) ? sanitize_key( (string) $settings['founder_auth_mode'] ) : '';
        if ( in_array( $founder_mode, array( 'api_key', 'oauth' ), true ) ) {
            return $founder_mode;
        }

        $global_mode = isset( $auth_opts['mode'] ) ? sanitize_key( (string) $auth_opts['mode'] ) : 'api_key';
        return in_array( $global_mode, array( 'api_key', 'oauth' ), true ) ? $global_mode : 'api_key';
    }

    private function is_founder_model_allowed( $mode, $model, $api_key = '' ) {
        $mode  = sanitize_key( (string) $mode );
        $model = trim( (string) $model );
        if ( '' === $model ) {
            return false;
        }

        if ( $this->model_catalog && method_exists( $this->model_catalog, 'is_valid_founder_model_for_mode' ) ) {
            return (bool) $this->model_catalog->is_valid_founder_model_for_mode( $mode, $model, $api_key );
        }

        return (bool) preg_match( '/^gpt-5([-.].+)?$/i', $model ) || false !== strpos( strtolower( $model ), 'codex' );
    }

    private function truncate_text( $text, $max = 90 ) {
        $text = trim( wp_strip_all_tags( (string) $text ) );
        if ( strlen( $text ) <= $max ) {
            return $text;
        }

        return substr( $text, 0, $max - 3 ) . '...';
    }
}
