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

/**
 * Public-Facing Functionality
 *
 * Handles frontend chatbot widgets, forms, and public assets
 *
 * @package    Kitsune_AI_Platform
 * @subpackage Kitsune_AI_Platform/public
 */

class Kitsune_AI_Platform_Public {

    private $plugin_name;
    private $version;

    public function __construct($plugin_name, $version) {
        $this->plugin_name = $plugin_name;
        $this->version = $version;
    }

    /**
     * Basic per-IP transient rate limiting for public AJAX endpoints.
     *
     * @param string $action Action scope.
     * @param int    $limit  Max requests in window.
     * @param int    $window Window in seconds.
     * @return bool
     */
    private function check_rate_limit($action = 'default', $limit = 60, $window = HOUR_IN_SECONDS, $scope_by_session = false) {
        $ip = sanitize_text_field($_SERVER['REMOTE_ADDR'] ?? '127.0.0.1');
        $key = 'kitsune_ai_platform_rate_' . sanitize_key($action) . '_' . md5($ip);

        if ($scope_by_session) {
            $session_token = $this->get_public_session_token();
            if ($session_token === '') {
                $session_token = $this->ensure_public_session_token();
            }
            if ($session_token !== '') {
                $key .= '_' . md5($session_token);
            }
        }

        $count = (int) get_transient($key);

        if ($count >= $limit) {
            wp_send_json_error(array(
                'message' => __('Too many requests. Please try again later.', 'kitsune-ai-platform'),
            ), 429);
            return false;
        }

        set_transient($key, $count + 1, $window);
        return true;
    }

    /**
     * Normalize conversation identifier.
     *
     * @param mixed $conversation_id Raw conversation identifier.
     * @return string
     */
    private function sanitize_conversation_id($conversation_id) {
        if (!is_scalar($conversation_id)) {
            return '';
        }

        $conversation_id = sanitize_text_field((string) $conversation_id);
        $conversation_id = preg_replace('/[^a-zA-Z0-9_-]/', '', $conversation_id);
        if (!is_string($conversation_id)) {
            return '';
        }

        return substr($conversation_id, 0, 64);
    }

    /**
     * Get sanitized public AJAX nonce from request payload.
     *
     * @return string
     */
    private function get_public_request_nonce() {
        if (!isset($_POST['nonce'])) {
            return '';
        }

        return sanitize_text_field(wp_unslash($_POST['nonce']));
    }

    /**
     * Normalize a public chatbot session token.
     *
     * @param mixed $token Raw token value.
     * @return string
     */
    private function sanitize_public_session_token($token) {
        if (!is_scalar($token)) {
            return '';
        }

        $token = sanitize_text_field((string) $token);
        $token = preg_replace('/[^a-zA-Z0-9_-]/', '', $token);
        if (!is_string($token)) {
            return '';
        }

        return substr($token, 0, 128);
    }

    /**
     * Get cookie name used for public chatbot session binding.
     *
     * @return string
     */
    private function get_public_session_cookie_name() {
        return 'kitsune_ai_platform_public_session';
    }

    /**
     * Read public chatbot session token from cookie.
     *
     * @return string
     */
    private function get_public_session_token() {
        $cookie_name = $this->get_public_session_cookie_name();
        return $this->sanitize_public_session_token(wp_unslash($_COOKIE[$cookie_name] ?? ''));
    }

    /**
     * Persist the public chatbot session token in an HttpOnly cookie.
     *
     * @param string $token Session token.
     * @return void
     */
    private function set_public_session_token_cookie($token) {
        $token = $this->sanitize_public_session_token($token);
        if ($token === '' || headers_sent()) {
            return;
        }

        $cookie_name = $this->get_public_session_cookie_name();
        $expires = time() + (30 * DAY_IN_SECONDS);
        $path = defined('COOKIEPATH') && COOKIEPATH ? COOKIEPATH : '/';
        $domain = defined('COOKIE_DOMAIN') ? COOKIE_DOMAIN : '';
        $secure = is_ssl();

        if (PHP_VERSION_ID >= 70300) {
            setcookie($cookie_name, $token, array(
                'expires'  => $expires,
                'path'     => $path,
                'domain'   => $domain,
                'secure'   => $secure,
                'httponly' => true,
                'samesite' => 'Lax',
            ));
        } else {
            setcookie($cookie_name, $token, $expires, $path . '; samesite=Lax', $domain, $secure, true);
        }

        $_COOKIE[$cookie_name] = $token;
    }

    /**
     * Ensure a public chatbot session token exists.
     *
     * @return string
     */
    private function ensure_public_session_token() {
        $token = $this->get_public_session_token();
        if ($token !== '') {
            return $token;
        }

        $token = $this->sanitize_public_session_token('sess_' . wp_generate_password(64, false, false));
        if ($token === '') {
            return '';
        }

        $this->set_public_session_token_cookie($token);
        return $token;
    }

    /**
     * Hash the current session token before storing for conversation binding.
     *
     * @param string $token Plain token.
     * @return string
     */
    private function hash_public_session_token($token) {
        $token = $this->sanitize_public_session_token($token);
        if ($token === '') {
            return '';
        }

        return hash_hmac('sha256', $token, wp_salt('auth'));
    }

    /**
     * Hash a client-generated conversation UUID for ownership binding.
     *
     * @param string $conversation_id Client UUID conversation ID.
     * @return string
     */
    private function hash_conversation_uuid($conversation_id) {
        $conversation_id = $this->sanitize_conversation_id($conversation_id);
        if ($conversation_id === '') {
            return '';
        }

        return hash_hmac('sha256', 'uuid:' . $conversation_id, wp_salt('auth'));
    }

    /**
     * Check whether a conversation already has an ownership binding stored.
     *
     * @param int    $chatbot_id      Chatbot ID.
     * @param string $conversation_id Conversation ID.
     * @return bool
     */
    private function conversation_has_binding($chatbot_id, $conversation_id) {
        $chatbot_id      = absint($chatbot_id);
        $conversation_id = $this->sanitize_conversation_id($conversation_id);
        if (!$chatbot_id || $conversation_id === '') {
            return false;
        }

        $stored = get_transient($this->get_conversation_owner_cache_key($chatbot_id, $conversation_id));
        return is_string($stored) && $stored !== '';
    }

    /**
     * Bind a new conversation to the client UUID (possession = ownership).
     * Stores hash(conversation_id) as the owner so any client that presents
     * the same UUID can access the conversation across page loads.
     *
     * @param int    $chatbot_id      Chatbot ID.
     * @param string $conversation_id Client UUID conversation ID.
     * @return bool
     */
    private function bind_conversation_to_uuid($chatbot_id, $conversation_id) {
        $chatbot_id      = absint($chatbot_id);
        $conversation_id = $this->sanitize_conversation_id($conversation_id);
        if (!$chatbot_id || $conversation_id === '') {
            return false;
        }

        $uuid_hash = $this->hash_conversation_uuid($conversation_id);
        if ($uuid_hash === '') {
            return false;
        }

        return (bool) set_transient(
            $this->get_conversation_owner_cache_key($chatbot_id, $conversation_id),
            $uuid_hash,
            30 * DAY_IN_SECONDS
        );
    }

    /**
     * Get transient key that binds a conversation to a public session.
     *
     * @param int    $chatbot_id Chatbot ID.
     * @param string $conversation_id Conversation ID.
     * @return string
     */
    private function get_conversation_owner_cache_key($chatbot_id, $conversation_id) {
        return 'kitsune_ai_platform_conv_owner_' . md5((int) $chatbot_id . '|' . $conversation_id);
    }

    /**
     * Bind conversation access to the current browser session.
     *
     * @param int    $chatbot_id Chatbot ID.
     * @param string $conversation_id Conversation ID.
     * @return bool
     */
    private function bind_conversation_to_current_session($chatbot_id, $conversation_id) {
        $chatbot_id = absint($chatbot_id);
        $conversation_id = $this->sanitize_conversation_id($conversation_id);
        if (!$chatbot_id || $conversation_id === '') {
            return false;
        }

        $token = $this->ensure_public_session_token();
        $token_hash = $this->hash_public_session_token($token);
        if ($token_hash === '') {
            return false;
        }

        return (bool) set_transient(
            $this->get_conversation_owner_cache_key($chatbot_id, $conversation_id),
            $token_hash,
            30 * DAY_IN_SECONDS
        );
    }

    /**
     * Verify that current browser session can access a conversation.
     *
     * Accepts two ownership styles:
     *  1. UUID-based: hash(conversation_id) — for client-generated UUIDs stored in
     *     localStorage. Possession of the UUID equals ownership.
     *  2. Cookie-based (legacy): hash(session_token) — for server-generated IDs
     *     bound to a PHP session cookie.
     *
     * @param int    $chatbot_id Chatbot ID.
     * @param string $conversation_id Conversation ID.
     * @return bool
     */
    private function current_session_can_access_conversation($chatbot_id, $conversation_id) {
        $chatbot_id = absint($chatbot_id);
        $conversation_id = $this->sanitize_conversation_id($conversation_id);
        if (!$chatbot_id || $conversation_id === '') {
            return false;
        }

        $stored_hash = get_transient($this->get_conversation_owner_cache_key($chatbot_id, $conversation_id));
        if (!is_string($stored_hash) || $stored_hash === '') {
            return false;
        }

        // Check UUID-based ownership (client-generated conversation ID in localStorage).
        $uuid_hash = $this->hash_conversation_uuid($conversation_id);
        if ($uuid_hash !== '' && hash_equals($stored_hash, $uuid_hash)) {
            return true;
        }

        // Check legacy cookie-based ownership.
        $token = $this->get_public_session_token();
        $token_hash = $this->hash_public_session_token($token);
        if ($token_hash === '') {
            return false;
        }

        return hash_equals($stored_hash, $token_hash);
    }

    /**
     * Remove conversation-to-session binding.
     *
     * @param int    $chatbot_id Chatbot ID.
     * @param string $conversation_id Conversation ID.
     * @return void
     */
    private function clear_conversation_session_binding($chatbot_id, $conversation_id) {
        $chatbot_id = absint($chatbot_id);
        $conversation_id = $this->sanitize_conversation_id($conversation_id);
        if (!$chatbot_id || $conversation_id === '') {
            return;
        }

        delete_transient($this->get_conversation_owner_cache_key($chatbot_id, $conversation_id));
    }

    /**
     * Normalize history messages used for model context.
     *
     * @param mixed $history Raw history payload.
     * @return array
     */
    private function normalize_history_messages($history) {
        if (!is_array($history)) {
            return array();
        }

        $normalized = array();
        foreach ($history as $message) {
            if (!is_array($message)) {
                continue;
            }

            $role = sanitize_key($message['role'] ?? '');
            if (!in_array($role, array('system', 'user', 'assistant'), true)) {
                continue;
            }

            $content = sanitize_textarea_field($message['content'] ?? '');
            if ($content === '') {
                continue;
            }

            $normalized[] = array(
                'role' => $role,
                'content' => $content,
            );
        }

        return $normalized;
    }

    /**
     * Normalize stored conversation messages.
     *
     * @param mixed  $messages Raw messages payload.
     * @param string $fallback_time Timestamp to use when missing.
     * @return array
     */
    private function normalize_conversation_messages($messages, $fallback_time = '') {
        if (!is_array($messages)) {
            return array();
        }

        $normalized = array();
        foreach ($messages as $index => $message) {
            if (!is_array($message)) {
                continue;
            }

            $role = sanitize_key($message['role'] ?? '');
            if (!in_array($role, array('system', 'user', 'assistant'), true)) {
                continue;
            }

            $content = sanitize_textarea_field($message['content'] ?? '');
            if ($content === '') {
                continue;
            }

            $created_at = sanitize_text_field($message['created_at'] ?? '');
            if ($created_at === '') {
                $created_at = $fallback_time ?: current_time('c');
            }

            $id = sanitize_text_field($message['id'] ?? '');
            if ($id === '') {
                $id = (string) ((int) $index + 1);
            }

            $normalized[] = array(
                'id' => $id,
                'role' => $role,
                'content' => $content,
                'created_at' => $created_at,
            );
        }

        return $normalized;
    }

    /**
     * Get transient cache key for dashboard stats.
     *
     * @return string
     */
    private function get_dashboard_stats_cache_key() {
        return 'kitsune_ai_platform_dashboard_stats_' . gmdate('Ym', current_time('timestamp'));
    }

    /**
     * Enqueue public scripts and styles
     */
    public function enqueue_public_scripts() {
        // Always enqueue chatbot preview CSS (lightweight)
        wp_enqueue_style(
            $this->plugin_name . '-chatbot-preview',
            KITSUNE_AI_PLATFORM_PLUGIN_URL . 'admin/assets/css/chatbot-preview.css',
            array(),
            $this->version
        );

        // Enqueue chatbot widget JS
        wp_enqueue_script(
            $this->plugin_name . '-chatbot-widget',
            KITSUNE_AI_PLATFORM_PLUGIN_URL . 'public/assets/js/chatbot-widget.js',
            array('jquery'),
            $this->version,
            true
        );

        // Localize script
        wp_localize_script(
            $this->plugin_name . '-chatbot-widget',
            'kitsuneAiPlatformPublic',
            array(
                'ajaxUrl' => admin_url('admin-ajax.php'),
                'nonce'   => wp_create_nonce('kitsune_ai_platform_public'),
                'i18n'    => array(
                    'error'   => __('Sorry, something went wrong. Please try again.', 'kitsune-ai-platform'),
                    'sending' => __('Sending...', 'kitsune-ai-platform'),
                    'formError' => __('Sorry, we could not submit the form. Please try again.', 'kitsune-ai-platform'),
                    'formSuccess' => __('Thanks! Your submission has been received.', 'kitsune-ai-platform'),
                    'formSubmitting' => __('Submitting...', 'kitsune-ai-platform'),
                    'formSubmit' => __('Submit', 'kitsune-ai-platform'),
                    'formSubmitted' => __('Submitted', 'kitsune-ai-platform'),
                    'formDefaultTitle' => __('Form', 'kitsune-ai-platform'),
                    'formNoFields' => __('No fields configured.', 'kitsune-ai-platform'),
                    'formSelect' => __('Select', 'kitsune-ai-platform'),
                    'formNoOptions' => __('No options', 'kitsune-ai-platform'),
                    'formCaptchaPlaceholder' => __('Captcha placeholder', 'kitsune-ai-platform'),
                    'welcomeMessage' => __('Hi! How can I help you today?', 'kitsune-ai-platform'),
                    'endSessionConfirm' => __('End this chat session?', 'kitsune-ai-platform'),
                ),
            )
        );
    }

    /**
     * Handle direct chatbot URL access
     * URL format: /?ai-chatbot={id}
     */
    public function handle_direct_chatbot_url() {
        if (!isset($_GET['ai-chatbot'])) {
            return;
        }

        $chatbot_id = absint(wp_unslash($_GET['ai-chatbot']));
        if (!$chatbot_id) {
            return;
        }

        // Load chatbot model
        require_once KITSUNE_AI_PLATFORM_PLUGIN_DIR . 'includes/models/class-kitsune-ai-platform-chatbot-model.php';
        $model = new Kitsune_AI_Platform_Chatbot_Model();
        $chatbot = $model->get($chatbot_id);

        if (!$chatbot || $chatbot->status !== 'active') {
            wp_die(
                __('Chatbot not found or inactive.', 'kitsune-ai-platform'),
                __('Chatbot Unavailable', 'kitsune-ai-platform'),
                array('response' => 404)
            );
        }

        // Parse config
        $config = is_array($chatbot->config) ? $chatbot->config : array();
        $linked_forms_data = $this->get_linked_forms_data($config);

        // Enqueue assets
        $this->enqueue_public_scripts();

        // Render fullpage template
        include KITSUNE_AI_PLATFORM_PLUGIN_DIR . 'public/partials/chatbot-fullpage.php';
        exit;
    }

    /**
     * Render chatbot widget in footer (for floating widgets)
     */
    public function render_chatbot_widget() {
        // Do not render floating widgets on the direct fullpage view
        if (isset($_GET['ai-chatbot'])) {
            return;
        }

        require_once KITSUNE_AI_PLATFORM_PLUGIN_DIR . 'includes/models/class-kitsune-ai-platform-chatbot-model.php';
        $model = new Kitsune_AI_Platform_Chatbot_Model();

        // Get all active chatbots and check for widget-enabled ones
        $chatbots = $model->get_all(array('status' => 'active', 'limit' => 100));

        foreach ($chatbots as $chatbot) {
            $config = is_array($chatbot->config) ? $chatbot->config : array();
            $deployment = $config['deployment'] ?? array();

            // Check if widget is enabled for this chatbot
            if (!empty($deployment['widgetEnabled'])) {
                // Check target pages
                if ($this->should_display_on_page($deployment)) {
                    $mode = 'widget';
                    $linked_forms_data = $this->get_linked_forms_data($config);
                    include KITSUNE_AI_PLATFORM_PLUGIN_DIR . 'public/partials/chatbot-embed.php';
                    break; // Only show one widget per page
                }
            }
        }
    }

    /**
     * Check if chatbot should display on current page
     *
     * @param array $deployment Deployment config
     * @return bool
     */
    private function should_display_on_page($deployment) {
        $target = $deployment['targetPages'] ?? 'all';
        $selected = $deployment['selectedPages'] ?? array();

        if ($target === 'all') {
            return true;
        }

        $current_page_id = get_queried_object_id();

        if ($target === 'selected') {
            return in_array($current_page_id, $selected);
        }

        if ($target === 'exclude') {
            return !in_array($current_page_id, $selected);
        }

        return true;
    }

    /**
     * Shortcode: Render chatbot
     * Usage: [ai-platform-chatbot id="1"] or [ai_platform_chatbot id="1"]
     */
    public function render_chatbot_shortcode($atts) {
        $atts = shortcode_atts(array(
            'id' => 0,
        ), $atts);

        if (empty($atts['id'])) {
            return '';
        }

        require_once KITSUNE_AI_PLATFORM_PLUGIN_DIR . 'includes/models/class-kitsune-ai-platform-chatbot-model.php';
        $model = new Kitsune_AI_Platform_Chatbot_Model();
        $chatbot = $model->get(absint($atts['id']));

        if (!$chatbot) {
            return '<!-- Chatbot not found -->';
        }

        if ($chatbot->status !== 'active') {
            return '<!-- Chatbot inactive -->';
        }

        $config = is_array($chatbot->config) ? $chatbot->config : array();
        $linked_forms_data = $this->get_linked_forms_data($config);
        $mode = 'inline';

        // Enqueue assets
        $this->enqueue_public_scripts();

        ob_start();
        include KITSUNE_AI_PLATFORM_PLUGIN_DIR . 'public/partials/chatbot-embed.php';
        return ob_get_clean();
    }

    /**
     * Shortcode: Render AI form
     * Usage: [ai_platform_form id="1"]
     */
    public function render_form_shortcode($atts) {
        $atts = shortcode_atts(array(
            'id' => 0,
        ), $atts);

        if (empty($atts['id'])) {
            return '';
        }

        require_once KITSUNE_AI_PLATFORM_PLUGIN_DIR . 'includes/models/class-kitsune-ai-platform-form-model.php';
        $model = new Kitsune_AI_Platform_Form_Model();
        $form = $model->get(absint($atts['id']));

        if (!$form) {
            return '<!-- AI Form not found -->';
        }

        return sprintf(
            '<div class="ai-platform-form" data-form-id="%d"><h3>%s</h3><!-- Form rendered by chatbot widget --></div>',
            esc_attr($form->id),
            esc_html($form->name)
        );
    }

    /**
     * Shortcode: Render semantic search
     * Usage: [ai_platform_search]
     */
    public function render_search_shortcode($atts) {
        ob_start();
        ?>
        <div class="ai-platform-search-widget">
            <input
                type="text"
                class="ai-platform-search-input"
                placeholder="<?php esc_attr_e('Search...', 'kitsune-ai-platform'); ?>"
            />
            <div class="ai-platform-search-results"></div>
        </div>
        <?php
        return ob_get_clean();
    }

    /**
     * AJAX: Return chatbot config and linked forms on demand (guest-accessible)
     */
    public function ajax_get_chatbot_config() {
        if (!wp_verify_nonce($this->get_public_request_nonce(), 'kitsune_ai_platform_public')) {
            wp_send_json_error(array('message' => __('Security check failed', 'kitsune-ai-platform')));
            return;
        }

        $chatbot_id = absint($_POST['chatbot_id'] ?? 0);
        if (!$chatbot_id) {
            wp_send_json_error(array('message' => __('Invalid chatbot ID', 'kitsune-ai-platform')), 400);
            return;
        }

        require_once KITSUNE_AI_PLATFORM_PLUGIN_DIR . 'includes/models/class-kitsune-ai-platform-chatbot-model.php';
        $model = new Kitsune_AI_Platform_Chatbot_Model();
        $chatbot = $model->get($chatbot_id);

        if (!$chatbot || $chatbot->status !== 'active') {
            wp_send_json_error(array('message' => __('Chatbot not found or inactive', 'kitsune-ai-platform')), 404);
            return;
        }

        $config = is_array($chatbot->config) ? $chatbot->config : array();
        $linked_forms_data = $this->get_linked_forms_data($config);

        // Only expose UI-facing config to the browser.
        // Never send aiConfig (model, systemPrompt, temperature, RAG settings, etc.)
        // or any deployment internals — all backend settings are resolved server-side.
        $public_config = array(
            'appearance' => $config['appearance'] ?? array(),
            'behavior'   => array(
                'welcomeMessage'             => $config['behavior']['welcomeMessage'] ?? '',
                'suggestedQuestions'         => $config['behavior']['suggestedQuestions'] ?? array(),
                'suggestedQuestionsTrigger'  => $config['behavior']['suggestedQuestionsTrigger'] ?? 'immediate',
            ),
            'features'   => array(
                'formsEnabled'        => $config['features']['formsEnabled'] ?? false,
                'linkedForms'         => $config['features']['linkedForms'] ?? array(),
                'formDisplayTrigger'  => $config['features']['formDisplayTrigger'] ?? 'immediate',
            ),
        );

        wp_send_json_success(array(
            'config' => $public_config,
            'forms'  => $linked_forms_data,
        ));
    }

    /**
     * AJAX: Handle chatbot message (guest-accessible)
     */
    public function ajax_chatbot_message() {
        // Verify nonce
        if (!wp_verify_nonce($this->get_public_request_nonce(), 'kitsune_ai_platform_public')) {
            wp_send_json_error(array('message' => __('Security check failed', 'kitsune-ai-platform')));
            return;
        }
        if (!$this->check_rate_limit('chatbot_message', 60, HOUR_IN_SECONDS, true)) {
            return;
        }

        $chatbot_id = absint($_POST['chatbot_id'] ?? 0);
        $message = sanitize_textarea_field(wp_unslash($_POST['message'] ?? ''));
        $requested_conversation_id = $this->sanitize_conversation_id(wp_unslash($_POST['conversation_id'] ?? ''));
        if (function_exists('mb_strlen') && mb_strlen($message) > 4000) {
            $message = mb_substr($message, 0, 4000);
        } elseif (strlen($message) > 4000) {
            $message = substr($message, 0, 4000);
        }

        if (!$chatbot_id || empty($message)) {
            wp_send_json_error(array('message' => __('Invalid request', 'kitsune-ai-platform')));
            return;
        }

        if ($requested_conversation_id !== '') {
            if (!$this->conversation_has_binding($chatbot_id, $requested_conversation_id)) {
                // First message with a client-generated UUID: create the ownership binding now.
                $this->bind_conversation_to_uuid($chatbot_id, $requested_conversation_id);
            } elseif (!$this->current_session_can_access_conversation($chatbot_id, $requested_conversation_id)) {
                wp_send_json_error(array('message' => __('Session access denied. Please start a new chat.', 'kitsune-ai-platform')), 403);
                return;
            }
            $conversation_id = $requested_conversation_id;
        } else {
            $conversation_id = 'conv_' . wp_generate_password(24, false, false);
            $this->bind_conversation_to_current_session($chatbot_id, $conversation_id);
        }

        // Load chatbot
        require_once KITSUNE_AI_PLATFORM_PLUGIN_DIR . 'includes/models/class-kitsune-ai-platform-chatbot-model.php';
        $model = new Kitsune_AI_Platform_Chatbot_Model();
        $chatbot = $model->get($chatbot_id);

        if (!$chatbot || $chatbot->status !== 'active') {
            wp_send_json_error(array('message' => __('Chatbot not available', 'kitsune-ai-platform')));
            return;
        }

        // Get AI config
        $config = is_array($chatbot->config) ? $chatbot->config : array();
        $ai_config = $config['aiConfig'] ?? array();

        // Build system prompt
        /* translators: %s: Helpful assistant context */
        $system_prompt = $ai_config['systemPrompt'] ?? __('You are a helpful assistant.', 'kitsune-ai-platform');

        // Get conversation history from session (simplified)
        $history = $this->normalize_history_messages($this->get_conversation_history($conversation_id));
        $history[] = array('role' => 'user', 'content' => sanitize_textarea_field($message));
        $form_id = null;

        $linked_forms_data = $this->get_linked_forms_data($config);
        $features = $config['features'] ?? array();
        $form_display_trigger = $features['formDisplayTrigger'] ?? 'immediate';
        $smart_forms = array();
        $smart_form_ids = array();
        if ($form_display_trigger === 'smart') {
            foreach ($linked_forms_data as $form) {
                $smart_forms[] = array(
                    'id' => $form['id'],
                    'name' => $form['name'],
                    'description' => $form['description'] ?? '',
                    'fields' => $form['config']['fields'] ?? array(),
                );
                $smart_form_ids[] = (int) $form['id'];
            }
        }

        if (!empty($smart_forms)) {
            $system_prompt .= "\n\n" . $this->build_smart_form_prompt($smart_forms);
        }
        $behavior = $config['behavior'] ?? array();
        /* translators: %s: Welcome message text */
        $welcome_message = $behavior['welcomeMessage'] ?? __('Hi! How can I help you today?', 'kitsune-ai-platform');
        $this->record_conversation_message($chatbot_id, $conversation_id, array(
            'id' => $this->generate_message_id(),
            'role' => 'user',
            'content' => sanitize_textarea_field($message),
            'created_at' => current_time('c'),
        ), $welcome_message);

        try {
            // Call AI API
            require_once KITSUNE_AI_PLATFORM_PLUGIN_DIR . 'includes/class-kitsune-ai-platform-api.php';
            $api = new Kitsune_AI_Platform_API();

            // When context memory is disabled, only the current message (last 1) is sent.
            $context_limit = !empty($ai_config['contextMemory']) ? max(1, intval($ai_config['contextLength'] ?? 10)) : 1;

            $chat_params = array(
                'model'        => $ai_config['model'] ?? KITSUNE_AI_PLATFORM_DEFAULT_MODEL,
                'messages'     => array_merge(
                    array(array('role' => 'system', 'content' => $system_prompt)),
                    array_slice($history, -$context_limit)
                ),
                'temperature'  => floatval($ai_config['temperature'] ?? 0.7),
                'max_tokens'   => intval($ai_config['maxTokens'] ?? 500),
                'feature'      => 'chatbot',
                'rag'          => array(
                    'enabled'          => !empty($ai_config['useKnowledgeBase']),
                    'limit'            => isset($ai_config['ragLimit']) ? max(1, min(20, intval($ai_config['ragLimit']))) : 6,
                    'source_ids'       => !empty($ai_config['knowledgeSources']) && is_array($ai_config['knowledgeSources'])
                        ? array_map('intval', $ai_config['knowledgeSources'])
                        : array(),
                ),
            );

            // Merge any model-specific options stored with the chatbot (e.g. reasoning_effort).
            $stored_model_options = isset($ai_config['modelOptions']) && is_array($ai_config['modelOptions'])
                ? $ai_config['modelOptions']
                : array();
            if (!empty($stored_model_options)) {
                $chat_params = array_merge($chat_params, $stored_model_options);
            }

            $response = $api->chatRAG($chat_params);

            if (isset($response['success']) && $response['success'] && isset($response['data']['content'])) {
                $bot_response = $response['data']['content'];

                if (!empty($smart_forms)) {
                    $parsed = $this->parse_smart_form_response($bot_response, $smart_form_ids);
                    if ($parsed) {
                        $bot_response = $parsed['reply'];
                        $form_id = $parsed['form_id'];
                    }
                }

                // Save conversation
                $history[] = array('role' => 'assistant', 'content' => $bot_response);
                $this->save_conversation_history($conversation_id, $history);
                $this->record_conversation_message($chatbot_id, $conversation_id, array(
                    'id' => $this->generate_message_id(),
                    'role' => 'assistant',
                    'content' => sanitize_textarea_field($bot_response),
                    'created_at' => current_time('c'),
                ));

                wp_send_json_success(array(
                    'response' => $bot_response,
                    'conversation_id' => $conversation_id,
                    'form_id' => $form_id ?? null,
                ));
            } else {
                wp_send_json_error(array(
                    'message' => $response['message'] ?? __('Failed to get AI response', 'kitsune-ai-platform')
                ));
            }
        } catch (Exception $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('AI Platform Chatbot Error: ' . $e->getMessage());
            }
            wp_send_json_error(array('message' => $e->getMessage()));
        }
    }

    /**
     * AJAX: End chatbot session (guest-accessible)
     */
    public function ajax_end_session() {
        // Verify nonce
        if (!wp_verify_nonce($this->get_public_request_nonce(), 'kitsune_ai_platform_public')) {
            wp_send_json_error(array('message' => __('Security check failed', 'kitsune-ai-platform')));
            return;
        }
        if (!$this->check_rate_limit('end_session', 120, HOUR_IN_SECONDS, true)) {
            return;
        }

        $chatbot_id = absint($_POST['chatbot_id'] ?? 0);
        $conversation_id = $this->sanitize_conversation_id($_POST['conversation_id'] ?? '');

        if (!$chatbot_id || empty($conversation_id)) {
            wp_send_json_error(array('message' => __('Invalid request', 'kitsune-ai-platform')));
            return;
        }

        if (!$this->current_session_can_access_conversation($chatbot_id, $conversation_id)) {
            wp_send_json_error(array('message' => __('Session access denied', 'kitsune-ai-platform')), 403);
            return;
        }

        $this->record_conversation_message($chatbot_id, $conversation_id, array(
            'id' => $this->generate_message_id(),
            'role' => 'system',
            'content' => __('Session ended', 'kitsune-ai-platform'),
            'created_at' => current_time('c'),
        ));
        $this->clear_conversation_history($conversation_id);
        $this->clear_conversation_session_binding($chatbot_id, $conversation_id);

        wp_send_json_success(array());
    }

    /**
     * AJAX: Get session messages (guest-accessible)
     */
    public function ajax_get_session_messages() {
        // Verify nonce
        if (!wp_verify_nonce($this->get_public_request_nonce(), 'kitsune_ai_platform_public')) {
            wp_send_json_error(array('message' => __('Security check failed', 'kitsune-ai-platform')));
            return;
        }
        if (!$this->check_rate_limit('get_session_messages', 120, HOUR_IN_SECONDS, true)) {
            return;
        }

        $chatbot_id = absint($_POST['chatbot_id'] ?? 0);
        $conversation_id = $this->sanitize_conversation_id($_POST['conversation_id'] ?? '');
        $page = isset($_POST['page']) ? max(1, intval($_POST['page'])) : 1;
        $per_page = isset($_POST['per_page']) ? min(100, max(1, intval($_POST['per_page']))) : 20;

        if (!$chatbot_id || empty($conversation_id)) {
            wp_send_json_error(array('message' => __('Invalid request', 'kitsune-ai-platform')));
            return;
        }

        if (!$this->current_session_can_access_conversation($chatbot_id, $conversation_id)) {
            wp_send_json_error(array('message' => __('Session access denied', 'kitsune-ai-platform')), 403);
            return;
        }

        require_once KITSUNE_AI_PLATFORM_PLUGIN_DIR . 'includes/models/class-kitsune-ai-platform-conversation-model.php';
        $model = new Kitsune_AI_Platform_Conversation_Model();
        $conversation = $model->get_by_identifier($chatbot_id, $conversation_id);

        if (!$conversation || empty($conversation->messages)) {
            wp_send_json_success(array(
                'messages' => array(),
                'total' => 0,
                'has_more' => false,
            ));
            return;
        }

        $messages = $this->normalize_conversation_messages(
            json_decode($conversation->messages, true),
            mysql2date('c', $conversation->created_at)
        );

        $total = count($messages);
        $offset_from_end = ($page - 1) * $per_page;
        if ($offset_from_end >= $total) {
            $paged = array();
            $has_more = false;
        } else {
            $end = $total - $offset_from_end;
            $start = max(0, $end - $per_page);
            $paged = array_slice($messages, $start, $end - $start);
            $has_more = $start > 0;
        }

        wp_send_json_success(array(
            'messages' => $paged,
            'total' => $total,
            'has_more' => $has_more,
        ));
    }

    /**
     * Get conversation history from transient
     *
     * @param string $conversation_id
     * @return array
     */
    private function get_conversation_history($conversation_id) {
        $conversation_id = $this->sanitize_conversation_id($conversation_id);
        if ($conversation_id === '') {
            return array();
        }

        $key = 'kitsune_ai_platform_conv_' . md5($conversation_id);
        $history = get_transient($key);
        return $this->normalize_history_messages($history);
    }

    /**
     * Save conversation history to transient
     *
     * @param string $conversation_id
     * @param array $history
     */
    private function save_conversation_history($conversation_id, $history) {
        $conversation_id = $this->sanitize_conversation_id($conversation_id);
        if ($conversation_id === '') {
            return;
        }

        $key = 'kitsune_ai_platform_conv_' . md5($conversation_id);
        // Keep last 20 messages, expire after 1 hour
        $normalized_history = $this->normalize_history_messages($history);
        set_transient($key, array_slice($normalized_history, -20), HOUR_IN_SECONDS);
    }

    /**
     * Clear conversation history transient
     *
     * @param string $conversation_id
     */
    private function clear_conversation_history($conversation_id) {
        $conversation_id = $this->sanitize_conversation_id($conversation_id);
        if ($conversation_id === '') {
            return;
        }

        $key = 'kitsune_ai_platform_conv_' . md5($conversation_id);
        delete_transient($key);
    }

    /**
     * AJAX: Submit AI form (guest-accessible)
     */
    public function ajax_submit_form() {
        if (!wp_verify_nonce($this->get_public_request_nonce(), 'kitsune_ai_platform_public')) {
            wp_send_json_error(array('message' => __('Security check failed', 'kitsune-ai-platform')));
            return;
        }
        if (!$this->check_rate_limit('submit_form', 30, HOUR_IN_SECONDS, true)) {
            return;
        }

        $chatbot_id = absint($_POST['chatbot_id'] ?? 0);
        $conversation_id = $this->sanitize_conversation_id($_POST['conversation_id'] ?? '');
        if ($conversation_id !== '' && !$this->current_session_can_access_conversation($chatbot_id, $conversation_id)) {
            $conversation_id = '';
        }

        $form_id = absint($_POST['form_id'] ?? 0);
        $data_raw = wp_unslash($_POST['data'] ?? '');
        $data = json_decode($data_raw, true);

        if (!$form_id || !is_array($data)) {
            wp_send_json_error(array('message' => __('Invalid form submission', 'kitsune-ai-platform')));
            return;
        }

        require_once KITSUNE_AI_PLATFORM_PLUGIN_DIR . 'includes/models/class-kitsune-ai-platform-form-model.php';
        $form_model = new Kitsune_AI_Platform_Form_Model();
        $form = $form_model->get_by_id($form_id);
        if (!$form || $form->status !== 'active') {
            wp_send_json_error(array('message' => __('Form not available', 'kitsune-ai-platform')));
            return;
        }

        $data = $this->sanitize_form_submission_data($data);

        // Attach basic context
        $data['_meta'] = array(
            'chatbot_id' => $chatbot_id,
            'conversation_id' => $conversation_id,
            'submitted_at' => current_time('c'),
        );

        global $wpdb;
        $table = $wpdb->prefix . 'kitsune_ai_platform_form_submissions';
        $result = $wpdb->insert(
            $table,
            array(
                'form_id' => $form_id,
                'data' => wp_json_encode($data),
                'status' => 'new',
                'user_ip' => sanitize_text_field($_SERVER['REMOTE_ADDR'] ?? ''),
                'user_agent' => sanitize_textarea_field($_SERVER['HTTP_USER_AGENT'] ?? ''),
                'user_id' => get_current_user_id() ?: null,
                'created_at' => current_time('mysql'),
            ),
            array('%d', '%s', '%s', '%s', '%s', '%d', '%s')
        );

        if (!$result) {
            wp_send_json_error(array('message' => __('Failed to save form submission', 'kitsune-ai-platform')));
            return;
        }

        $form_model->update_submission_count($form_id);

        // Read global form settings to determine confirmation behaviour
        $global_settings = wp_parse_args(
            (array) get_option( 'kitsune_ai_platform_form_settings', array() ),
            array(
                'confirmationType'    => 'message',
                'confirmationMessage' => __( 'Thank you for your submission! We will get back to you shortly.', 'kitsune-ai-platform' ),
                'redirectUrl'         => '',
            )
        );

        wp_send_json_success(array(
            'confirmationType'    => sanitize_text_field($global_settings['confirmationType']),
            'confirmationMessage' => sanitize_textarea_field($global_settings['confirmationMessage']),
            'redirectUrl'         => esc_url_raw($global_settings['redirectUrl']),
        ));
    }

    private function generate_message_id() {
        return 'msg_' . wp_generate_uuid4();
    }

    private function record_conversation_message($chatbot_id, $conversation_id, $message, $welcome_message = null) {
        $conversation_id = $this->sanitize_conversation_id($conversation_id);
        if (!$chatbot_id || $conversation_id === '' || !is_array($message)) {
            return;
        }

        $normalized_message = $this->normalize_conversation_messages(array($message), current_time('c'));
        if (empty($normalized_message)) {
            return;
        }
        $message = $normalized_message[0];

        require_once KITSUNE_AI_PLATFORM_PLUGIN_DIR . 'includes/models/class-kitsune-ai-platform-conversation-model.php';
        $model = new Kitsune_AI_Platform_Conversation_Model();
        $conversation = $model->get_by_identifier($chatbot_id, $conversation_id);

        $messages = array();
        $is_new = true;
        if ($conversation && !empty($conversation->messages)) {
            $messages = $this->normalize_conversation_messages(
                json_decode($conversation->messages, true),
                mysql2date('c', $conversation->created_at)
            );
            if (!empty($messages)) {
                $is_new = false;
            }
        }

        if ($is_new && $welcome_message) {
            $messages[] = array(
                'id' => $this->generate_message_id(),
                'role' => 'assistant',
                'content' => sanitize_textarea_field($welcome_message),
                'created_at' => current_time('c'),
            );
        }

        $messages[] = $message;

        $result = null;
        $conversation_record_id = $conversation ? (int) $conversation->id : 0;
        $conversation_created = false;
        if ($conversation) {
            $result = $model->update($conversation->id, array(
                'messages' => wp_json_encode($messages),
            ));
        } else {
            $result = $model->create(array(
                'chatbot_id' => $chatbot_id,
                'user_identifier' => $conversation_id,
                'messages' => wp_json_encode($messages),
                'created_at' => current_time('mysql'),
            ));
            if ($result && !is_wp_error($result)) {
                $conversation_record_id = (int) $result;
                $conversation_created = true;
            }
        }

        if ($result !== false && $result !== null) {
            delete_transient($this->get_dashboard_stats_cache_key());

            if ($conversation_created) {
                do_action('kitsune_ai_platform_conversation_created', $conversation_record_id, $chatbot_id, $conversation_id, $messages);
                Kitsune_AI_Platform_Webhook_Dispatcher::dispatch(
                    Kitsune_AI_Platform_Webhook_Events::CONVERSATION_CREATED,
                    array(
                        'conversation_db_id' => $conversation_record_id,
                        'chatbot_id' => (int) $chatbot_id,
                        'conversation_id' => $conversation_id,
                        'messages' => $messages,
                        'actor_id' => get_current_user_id(),
                    )
                );
            }

            do_action(
                'kitsune_ai_platform_conversation_message_created',
                $conversation_record_id,
                $chatbot_id,
                $conversation_id,
                $message
            );
            Kitsune_AI_Platform_Webhook_Dispatcher::dispatch(
                Kitsune_AI_Platform_Webhook_Events::CONVERSATION_MESSAGE_CREATED,
                array(
                    'conversation_db_id' => $conversation_record_id,
                    'chatbot_id' => (int) $chatbot_id,
                    'conversation_id' => $conversation_id,
                    'message' => $message,
                    'actor_id' => get_current_user_id(),
                )
            );
        }
    }

    private function get_linked_forms_data($config) {
        $features = is_array($config) ? ($config['features'] ?? array()) : array();
        $linked_forms = isset($features['linkedForms']) ? $features['linkedForms'] : array();
        if (!is_array($linked_forms)) {
            $linked_forms = $linked_forms !== '' && $linked_forms !== null ? array($linked_forms) : array();
        }
        if (empty($linked_forms)) {
            return array();
        }

        require_once KITSUNE_AI_PLATFORM_PLUGIN_DIR . 'includes/models/class-kitsune-ai-platform-form-model.php';
        $model = new Kitsune_AI_Platform_Form_Model();

        $forms = array();
        foreach ($linked_forms as $form_id) {
            $form = $model->get_by_id(absint($form_id));
            if (!$form || $form->status !== 'active') {
                continue;
            }

            $forms[] = array(
                'id' => $form->id,
                'name' => $form->name,
                'description' => $form->description,
                'config' => $form->config,
            );
        }

        return $forms;
    }

    private function sanitize_form_submission_data($data) {
        $sanitized = array();
        foreach ($data as $key => $value) {
            $clean_key = sanitize_key($key);
            if (is_array($value)) {
                $sanitized[$clean_key] = array_map(array($this, 'sanitize_form_submission_value'), $value);
            } else {
                $sanitized[$clean_key] = $this->sanitize_form_submission_value($value);
            }
        }
        return $sanitized;
    }

    private function sanitize_form_submission_value($value) {
        if (is_string($value)) {
            return sanitize_textarea_field($value);
        }
        return $value;
    }

    private function build_smart_form_prompt($forms) {
        $lines = array();
        foreach ($forms as $form) {
            $field_labels = array();
            if (!empty($form['fields']) && is_array($form['fields'])) {
                foreach ($form['fields'] as $field) {
                    if (!empty($field['label'])) {
                        $field_labels[] = sanitize_text_field($field['label']);
                    }
                }
            }

            $lines[] = sprintf(
                /* translators: 1: Form name, 2: Form ID, 3: Form description, 4: Form fields */
                __('Form "%1$s" (ID %2$d): Description: %3$s | Fields: %4$s', 'kitsune-ai-platform'),
                sanitize_text_field($form['name']),
                intval($form['id']),
                sanitize_textarea_field($form['description'] ?? ''),
                sanitize_text_field(implode(', ', $field_labels))
            );
        }

        return __("You must respond ONLY in JSON with keys \"reply\" and \"form_id\".\n", 'kitsune-ai-platform')
            . __("Set \"form_id\" to the single best matching form ID when a form should be shown based on the rules below. Otherwise set \"form_id\" to null.\n", 'kitsune-ai-platform')
            . __("Always include a helpful \"reply\" string for the user.\n", 'kitsune-ai-platform')
            . __("Form rules:\n- ", 'kitsune-ai-platform') . implode("\n- ", $lines);
    }

    private function parse_smart_form_response($response, $allowed_ids = array()) {
        $decoded = json_decode(trim($response), true);
        if (!is_array($decoded)) {
            return null;
        }

        if (!isset($decoded['reply'])) {
            return null;
        }

        $form_id = null;
        if (array_key_exists('form_id', $decoded) && $decoded['form_id'] !== '' && $decoded['form_id'] !== null) {
            $form_id = (int) $decoded['form_id'];
        }

        if ($form_id && !empty($allowed_ids) && !in_array($form_id, $allowed_ids, true)) {
            $form_id = null;
        }

        return array(
            'reply' => sanitize_textarea_field($decoded['reply']),
            'form_id' => $form_id,
        );
    }
}
