<?php

namespace Limb_Chatbot\Includes\Utilities;

use Limb_Chatbot\Includes\Data_Objects\Action_Plan;
use Limb_Chatbot\Includes\Data_Objects\Action_Plan_Step;
use Limb_Chatbot\Includes\Data_Objects\AI_Model;
use Limb_Chatbot\Includes\Data_Objects\Chat;
use Limb_Chatbot\Includes\Data_Objects\Config;
use Limb_Chatbot\Includes\Data_Objects\Message;
use Limb_Chatbot\Includes\Data_Objects\Parameter;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;
use Limb_Chatbot\Includes\Integrations\Integrations;
use Limb_Chatbot\Includes\Interfaces\Utility_Interface;
use Limb_Chatbot\Includes\Services\Collection;

/**
 * Class Copilot Utility
 *
 * Utility class for managing copilot operations
 *
 * @package Limb_Chatbot\Includes\Utilities
 *
 * @since 1.0.0
 */
class Copilot_Utility extends Utility implements Utility_Interface {

	/**
	 * Static utility name.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	public static string $name = 'Copilot';

	/**
	 * The AI model associated with this utility.
	 *
	 * @var AI_Model|null
	 * @since 1.0.0
	 */
	public ?AI_Model $ai_model = null;

	/**
	 * Optional configuration related to the AI model.
	 *
	 * @var Config|null
	 * @since 1.0.0
	 */
	public ?Config $config = null;

	/**
	 * AI provider identifier.
	 *
	 * @var string|null
	 * @since 1.0.0
	 */
	public ?string $ai_provider_id;

	/**
	 * Constructor.
	 *
	 * Initializes the Copilot_Utility with optional model, config, and AI provider ID.
	 *
	 * @param  AI_Model|null  $ai_model  AI model instance. If null, must be set later.
	 * @param  Config|null  $config  Optional configuration object.
	 * @param  string|null  $ai_provider_id  Optional AI provider ID; if null, derived from the model.
	 *
	 * @since 1.0.0
	 */
	public function __construct(
		?Ai_Model $ai_model = null,
		?Config $config = null,
		?string $ai_provider_id = null
	) {
		$this->ai_provider_id = $ai_provider_id;
		if ( ! is_null( $ai_model ) ) {
			$this->ai_model = $ai_model;
		}
		if ( is_null( $this->ai_provider_id ) && ! is_null( $this->ai_model ) ) {
			$this->ai_provider_id = $this->ai_model->get_ai_provider_id();
		}
		if ( ! is_null( $config ) ) {
			$this->config = $config;
		}
	}

	/**
	 * Get the AI provider ID associated with this utility.
	 *
	 * @return string|null AI provider identifier or null if not set.
	 *
	 * @since 1.0.0
	 */
	public function get_ai_provider_id(): ?string {
		return $this->ai_provider_id;
	}

	/**
	 * Get the configuration object.
	 *
	 * @return Config|null Config object or null if not set.
	 *
	 * @since 1.0.0
	 */
	public function get_config(): ?Config {
		return $this->config;
	}

	/**
	 * Get the AI_Model object
	 *
	 * @return AI_Model AI_Model object or null
	 * @since 1.0.0
	 */
	public function get_ai_model(): AI_Model {
		return $this->ai_model;
	}

	/**
	 * Determines if a user message is suitable for retrieval from the vector store.
	 *
	 * Only considers the current message. Returns '1' if vector search is appropriate, '0' otherwise.
	 *
	 * @param  string  $user_input  User's message text.
	 *
	 * @return string '1' if eligible, '0' if not.
	 * @since 1.0.0
	 * @throws Exception
	 */
	public function is_vector_search_applicable( $user_input ) {
		$messages       = new Collection();
		$system_message = implode( "\n", [
			"You are an AI chatbot embedded in " . ucfirst( get_option( 'blogname' ) ?? '' ) . " website. Your task is to decide whether a user message is suitable for retrieval from the vector store.",
			"## Rules",
			"- If the message relies only on vague pronouns (e.g., 'it', 'this', 'there') **without meaningful keywords**, return `0`.",
			"- If the message has clear, standalone, search-friendly keywords, return `1`.",
			"- Return `0` if the message has only 1 or 2 words.",
			"- Output **only** `1` or `0`. No explanations, formatting, or extra text.",
			"- Treat every message independently for vector search eligibility."
		] );

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => 'User: ' . $user_input ],
				]
			],
		] ) );

		return $this->get_ai_provider_utility()->is_vector_search_applicable( $messages );
	}

	/**
	 * Enhanced conversation analysis with essential AI context
	 */
	public function analyze_turn( $payload ) {
		$system_message = implode( "\n", [
			"You are an expert conversation analyst specializing in maintaining coherent, context-aware AI conversations.",
			"Your analysis directly impacts AI response quality and user experience.",
			"",
			"## CRITICAL OUTPUT FORMAT:",
			"Return ONLY a valid JSON object with EXACTLY these keys:",
			"intent, topic, entities, summary, confidence, sentiment, user_preferences, user_intent_history",
			"",
			"## ANALYSIS REQUIREMENTS:",
			"",
			"### Core State Management:",
			"- topic: Maintain continuity. Only change if user explicitly shifts topics. Preserve context.",
			"- entities: MUST be structured as object with categories. NO flat arrays allowed:",
			"  { people:[], orgs:[], locations:[], dates:[], products:[], services:[], misc:[] }",
			"  - people: Names of individuals mentioned",
			"  - orgs: Company/organization names",
			"  - locations: Geographic locations, addresses, countries, cities",
			"  - dates: Specific dates, time periods",
			"  - products: Specific product names, models, equipment",
			"  - services: Service types, offerings, solutions",
			"  - misc: Other important entities not fitting above categories",
			"- summary: REPLACE previous summary with updated version. Do NOT append. Keep concise (max 2 sentences).",
			"",
			"### AI Response Quality:",
			"- confidence: 0.0-1.0. Analyze user's reaction to LAST assistant message:",
			"  * 0.8-1.0: User is satisfied, continuing topic",
			"  * 0.5-0.7: User is neutral, slight topic shift",
			"  * 0.2-0.4: User seems confused or unsatisfied",
			"  * 0.0-0.1: User is frustrated or asking for clarification",
			"",
			"### Conversation Intelligence:",
			"- sentiment: From CURRENT user message only: ['positive','negative','neutral','frustrated','satisfied']",
			"",
			"### Context Preservation:",
			"- user_preferences: MUST be a key-value object (no arrays allowed). Capture all preferences (personal info, favourite things, settings, etc.) and any information the user mentioned about themselves using clear, descriptive keys. This data will be used for analytics and reporting purposes:",
			"  { \"desired_contact_way\": \"email\", \"color_preference\": \"red\", \"location\": \"Armenia\", \"customer_type\": \"business\" }",
			"- user_intent_history: Append current intent. Maintain chronological order",
			"",
			"## MERGING RULES:",
			"- ALWAYS merge entities, preferences, and intent history with previous_state values",
			"- NEVER reset topic or summary unless user explicitly changes subject",
			"- Confidence should reflect user feedback to last assistant response",
			"- Maintain conversation continuity across turns",
			"- REMOVE duplicates when merging entities and preferences",
			"- YOU MUST categorize entities intelligently - do NOT put everything in misc",
			"- YOU MUST extract preferences as key-value pairs - do NOT use flat arrays",
			"",
			"## OUTPUT VALIDATION:",
			"- Include ALL required keys",
			"- Use exact schema values",
			"- Provide null for uncertain values",
			"- No commentary or extra text",
			"- Ensure JSON is valid and parseable",
			"- entities MUST be object with proper categorization, NOT array",
			"- user_preferences MUST be object with key-value pairs, NOT array",
			"- CRITICAL: Do the intelligent categorization yourself - don't rely on fallbacks",
			"",
			"## EXAMPLE OUTPUT:",
			'{"intent":"Location inquiry","topic":"Solar solutions inquiry","entities":{"people":[],"orgs":["Solar Integrations"],"locations":["Armenia"],"dates":[],"products":[],"services":["Solar Power","Energy Storage","EV Infrastructure","Incentive Management"],"misc":[]},"summary":"Business customer inquiring about Solar Integrations solar solutions in Armenia, with preference for red-colored systems and email contact.","confidence":0.7,"sentiment":"neutral","user_preferences":{"desired_contact_way":"email","color_preference":"red","location":"Armenia","customer_type":"business"},"user_intent_history":["Identity inquiry","Service inquiry","Business inquiry","Contact inquiry","Product inquiry","Location inquiry"]}'
		] );

		$messages = new Collection();
		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [ [ 'type' => 'text', 'text' => [ 'value' => $system_message ] ] ]
		] ) );
		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $payload ]
				]
			]
		] ) );

		return $this->get_ai_provider_utility()->analyze_turn( $messages );
	}

	/**
	 * Generate a natural question for collecting a specific parameter in an action plan.
	 *
	 * This method uses AI to generate a conversational question that requests the user
	 * to provide a specific field value. The AI considers the context of the action,
	 * previous steps, and any previously collected data to generate an appropriate
	 * and natural-sounding question or acknowledgment.
	 *
	 * @param  Action_Plan  $plan  The action plan being executed.
	 * @param  Action_Plan_Step  $step  The current step requiring data collection.
	 *
	 * @return string The generated question or message.
	 * @throws Exception If question generation fails.
	 * @since 1.0.0
	 */
	public function generate_data_collect_question( Action_Plan $plan, Action_Plan_Step $step, $repeat = false, $stream = false ) {
		$messages = new Collection();

		// Get the current parameter
		$data      = $step->get_data();
		$parameter = $data['parameter'] ?? null;

		if ( ! $parameter instanceof Parameter ) {
			throw new Exception( Error_Codes::TECHNICAL_ERROR, __( 'Invalid parameter in action plan step.', 'limb-chatbot' ) );
		}
		// Get the action
		$action = $plan->action();
		// Build context about previously collected parameters
		$previous_step = $plan->get_previous_step();
		if ( ! $previous_step ) {
			$chat = Chat::find_by_uuid( $plan->get_chat_uuid() );
			if ( $chat instanceof Chat ) {
				$last_user_message = $chat->get_last_user_message();
				if ( $last_user_message instanceof Message ) {
					$previous_step = [
						'field' => 'initial input',
						'value' => $last_user_message->extract_text(),
					];
				}
			}
		}

		// Build the system message with detailed context
		$system_message = implode( "\n", [
			"You are a friendly assistant helping users complete a form by collecting information step by step.",
			"Your task is to generate a natural, conversational question or acknowledgment message to collect the next piece of information.",
			"",
			"## CONTEXT:",
			"- Action: " . ( $action ? $action->get_name() : 'Unknown' ),
			"- Action Purpose: " . ( $action && $action->get_ai_instructions() ? $action->get_ai_instructions() : 'Collecting user information' ),
			"- Current Field to Collect: " . ( $parameter->get_label() ?? $parameter->get_name() ),
			"- Field Type: " . ( $parameter->get_type() ?? 'text' ),
			! empty( $parameter->get_placeholder() ) ? "- Example: " . $parameter->get_placeholder() : "",
			! empty( $parameter->get_validation_rules() ) ? "- Requirements: " . $parameter->get_validation_rules( true ) : "",
			"",
			"## YOUR TASK:",
			! empty( $previous_step ) ? "1. First, briefly acknowledge the user's previous input: \"{$previous_step['value']}\" for {$previous_step['field']}" : "1. This is the first field, no acknowledgment needed",
			"2. Then, ask for the next field: " . ( $parameter->get_label() ?? $parameter->get_name() ),
			$repeat ? "IMPORTANT: This is a repeat request because the previous attempt failed. You MUST include the word 'again' in your question naturally. For example: 'Could you please share your email address again?' or 'I need your phone number again, please.'" : "",
			"3. Be conversational, friendly, and natural",
			"4. Don't use formal language or bullet points",
			"5. Keep it concise (1-2 sentences maximum)",
			! empty( $parameter->get_placeholder() ) ? "6. You can mention an example if helpful: " . $parameter->get_placeholder() : "",
			"",
			"## OUTPUT FORMAT:",
			"Return ONLY the question/message text, without any formatting, quotes, or extra commentary.",
			"You can use emojis, for your response warmness.",
			"",
			"## EXAMPLES:",
			$repeat ? "Good (repeat): \"Could you please share your email address again?\"" : "",
			$repeat ? "Good (repeat): \"I need your phone number again, please.\"" : "",
			"Good: \"Great! Now, what's your email address so we can send you the details?\"",
			"Good: \"Perfect! And which product are you interested in?\"",
			"Good: \"Thanks! Could you share your phone number?\"",
			"Bad: \"Please provide your email address.\" (too formal)",
			"Bad: \"Email: \" (not conversational)"
		] );

		// Remove empty lines from system message
		$system_message = implode( "\n", array_filter( explode( "\n", $system_message ) ) );
		$system_message = apply_filters( 'lbaic_copilot_system_message', $system_message );

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		// Build the user message with field details
		$user_context = [
			'field_name'    => $parameter->get_name(),
			'field_label'   => $parameter->get_label() ?? $parameter->get_name(),
			'field_type'    => $parameter->get_type() ?? 'text',
			'required'      => $parameter->get_required() === '1' || $parameter->get_required() === 1,
			'placeholder'   => $parameter->get_placeholder(),
			'previous_step' => $previous_step,
		];

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => json_encode( $user_context, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT ) ],
				]
			],
		] ) );

		return $this->get_ai_provider_utility()->generate_data_collect_question( $messages, $stream );
	}

	/**
	 * Generate a beautiful success message for action completion
	 *
	 * Generates a warm, celebratory message to show users after they complete an action.
	 * Uses AI to create personalized, engaging success messages based on the action context.
	 *
	 * @param  string  $action_context  The action context/purpose (e.g., "Generate Voucher")
	 * @param  bool  $is_success  Whether the action was successful or failed
	 *
	 * @return Message The generated success message
	 * @throws Exception If message generation fails
	 * @since 1.0.0
	 */
	public function generate_action_success_message( string $action_context, bool $is_success = true, $stream = false ) {
		$messages = new Collection();

		// Build system message for success message generation
		$system_message = implode( "\n", [
			"You are a friendly assistant helping users by providing positive, encouraging feedback about success/failure of the given action.",
			"Your task is to generate a short, warm, celebratory success message for a completed action.",
			"",
			"## GUIDELINES:",
			"- Keep it to ONE sentence maximum",
			"- Use 1-2 appropriate emojis to add warmth",
			$is_success ? "- Make it celebratory and positive" : "- Be empathetic and supportive if it failed",
			"- Be conversational and friendly",
			"- No technical details or jargon",
			"- Make the user feel appreciated",
			"",
			"## OUTPUT FORMAT:",
			"Return ONLY the success/failure message text, without any formatting, quotes, or extra commentary."
		] );

		$system_message = apply_filters( 'lbaic_copilot_system_message', $system_message );

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		// Build user message with action context and status
		$status_text = $is_success ? 'successfully completed' : 'attempted but failed';
		$user_prompt = sprintf(
		/* translators: %1$s: action context, %2$s: status (success/failed) */
			__( 'Generate a message for a user who just %1$s: "%2$s"', 'limb-chatbot' ),
			$status_text,
			$action_context
		);

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $user_prompt ],
				]
			],
		] ) );

		// Delegate to AI provider utility
		return $this->get_ai_provider_utility()->generate_action_success_message( $messages, $stream );
	}

	/**
	 * Generate action success message with extracted variables context
	 *
	 * Creates a beautiful success message that includes information from extracted variables.
	 * Example: "Congrats, your voucher code is: SAVE20XYZ"
	 *
	 * @param string $action_context The action context/purpose
	 * @param string $details        Formatted extracted variables details (pre-formatted string)
	 * @param bool   $is_success     Whether the action was successful or failed
	 *
	 * @return Message The AI-generated success message
	 * @throws Exception
	 * @since 1.0.0
	 */
	public function generate_action_success_message_with_context( string $action_context, string $details, bool $is_success = true, $stream = false ) {
		$messages = new Collection();

		// Build system message for success message generation with context
		$system_message = implode( "\n", [
			"You are a friendly assistant helping users by providing positive, encouraging feedback about success or failure of the given action.",
			"Your task is to generate a short, warm, celebratory success message for a completed action.",
			"",
			"## GUIDELINES:",
			"- Keep it to ONE sentence maximum",
			"- Use 1-2 appropriate emojis to add warmth",
			$is_success ? "- Make it celebratory and positive" : "- Be empathetic and supportive if it failed",
			"- Be conversational and friendly",
			"- No technical details or jargon",
			"- Make the user feel appreciated",
			"- IMPORTANT: Include the provided data/values in the message naturally",
			"",
			"## OUTPUT FORMAT:",
			"If you are going to use any detail in your response like visitor name, make sure the detail is filled, do not use something like {visitor_name}.",
			"Return ONLY the success/failure message text, without any formatting, quotes, or extra commentary.",
		] );

		// Build user prompt with status information
		$status_text = $is_success ? 'successfully completed' : 'attempted but failed';
		$user_prompt = sprintf( __( 'Generate a message for a user who %1$s: "%2$s" with these details: %3$s', 'limb-chatbot' ), $status_text, $action_context, $details );

		$system_message = apply_filters( 'lbaic_copilot_system_message', $system_message );

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $user_prompt ],
				]
			],
		] ) );

		// Delegate to AI provider utility
		return $this->get_ai_provider_utility()->generate_action_success_message( $messages, $stream );
	}

	/**
	 * Refines a user's search query for vector search optimization.
	 *
	 * Extracts meaningful search keywords from natural language input,
	 * improving vector search accuracy by focusing on key terms and removing noise.
	 *
	 * Returns a Message with empty/null content if no valid search intent is detected,
	 * allowing the caller to skip vector search for non-searchable queries.
	 *
	 * @param string|null $user_input Raw user input/message text.
	 *
	 * @return Message A Message object containing the refined search query text, or empty if no search intent.
	 * @throws Exception If AI call fails.
	 * @since 1.0.0
	 */
	public function refine_search_query( ?string $user_input ): ?Message {
		$messages = new Collection();

		// Build system message for search query refinement
		$system_message = implode( "\n", [
			"You are an expert search query optimizer.",
			"Your task is to extract and refine search keywords from user messages.",
			"",
			"IMPORTANT:",
			"- First, determine whether the user has a CLEAR and SPECIFIC SEARCH INTENT.",
			"- Generic browsing requests are NOT valid search intent.",
			"",
			"## VALID SEARCH INTENT:",
			"The user explicitly mentions a specific product, category, or identifiable attributes.",
			"Examples:",
			"- 'blue hoodie'",
			"- 'red dress size m'",
			"- 'running shoes under 100 dollars'",
			"- 'wireless headphones'",
			"",
			"## INVALID SEARCH INTENT (RETURN 'none'):",
			"- Generic or vague requests with no specific product",
			"- Requests that only contain catalog-level words",
			"",
			"INVALID examples:",
			"- 'products'",
			"- 'available products'",
			"- 'items'",
			"- 'show me products'",
			"- 'what do you sell'",
			"- 'can you share available products'",
			"- greetings, thanks, or uncertainty",
			"",
			"## STRICT RULES:",
			"1. If the input contains ONLY generic words like:",
			"   products, product, items, item, catalog, shop",
			"   → This is NOT a search query.",
			"2. NEVER return generic catalog words as a search query.",
			"3. If no specific product or attribute is mentioned → return EXACTLY: none",
			"",
			"## IF SEARCH INTENT EXISTS:",
			"- Identify the core product or category",
			"- Extract attributes (color, size, brand, feature, price, etc.)",
			"- Remove filler words, pronouns, and noise",
			"- Output a concise, search-ready phrase",
			"",
			"## OUTPUT FORMAT:",
			"- Return ONLY the refined search query text",
			"- OR return EXACTLY: none",
			"- No punctuation, no explanations, no extra text",
			"",
			"## EXAMPLES:",
			"Input: 'I want a blue hoodie for winter' → Output: blue hoodie winter",
			"Input: 'Do you have red shoes in size 10?' → Output: red shoes size 10",
			"Input: 'Show me shoes' → Output: shoes",
			"Input: 'Can you share available products?' → Output: none",
			"Input: 'Show me products' → Output: none",
			"Input: 'Thanks' → Output: none",
			"Input: 'Hi there' → Output: none",
			"Input: 'I don\\'t know what I want' → Output: none",
		] );

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );
		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $user_input ],
				]
			],
		] ) );
		// todo change this
		return $this->get_ai_provider_utility()->generate_action_success_message( $messages );
	}

	/**
	 * Lightweight query rewrite for clearer, more explicit retrieval queries.
	 *
	 * This is intentionally language-neutral:
	 * - Preserves the original language
	 * - Does not translate or add new intent
	 * - Focuses on spelling cleanup and clarity
	 *
	 * @param $payload
	 *
	 * @return string|null Rewritten query text or null on failure.
	 * @throws Exception
	 * @since 1.0.0
	 */
	public function rewrite_query(  $payload ): ?string {
		$messages = new Collection();
		$system_lines = [
			'You rewrite user queries for knowledge retrieval.',
			'',
			'Rewrite ONLY when necessary to resolve missing or unclear references.',
			'',
			'Rules:',
			'- Rewrite ONLY the "Current query".',
			'- If the query is already clear and self-contained, return it unchanged.',
			'',
			'',
			'- Use conversation ONLY if:',
			'  1) The current query contains a pronoun or unclear reference (e.g. "it", "one", "that", "this", "how?", "how to create one?"), OR',
			'  2) The current query is a short affirmative/negative or confirmation (e.g. "yes", "no", "okay", "please show", "sure") AND the previous assistant message contains an offer, explanation, or incomplete instruction.',
			'  3) A specific feature, concept, or named functionality was clearly mentioned earlier.',
			'',
			'- If condition 1, 2, or 3 is met, rewrite the query into a **complete, explicit, retrieval-ready question** that follows the user intent and reuses any terminology previously introduced by the assistant.',
			'',
			'- If there is no clearly established feature name, return the query unchanged.',
			'',
			'- Do NOT generalize.',
			'- Do NOT add abstract phrases like "in the context of..." or similar meta-language.',
			'- Do NOT introduce new feature names.',
			'- Do NOT assume a subject unless it was explicitly mentioned.',
			'- Do NOT change user intent.',
			'- Fix typos if needed.',
			'',
			'- Return only the rewritten query.',
			'- Keep the same language as the input.',
		];
		/**
		 * $system_lines = [
		 * 'You rewrite user queries for knowledge retrieval.',
		 * '',
		 * 'Your goal is to make the query retrieval-safe, explicit, and unambiguous.',
		 * '',
		 * 'Rules:',
		 * '- Rewrite ONLY the "Current query".',
		 * '- Use the conversation ONLY to resolve unclear references, missing subjects, or implicit intent.',
		 * '- If the current query is generic (e.g. "How to get API key?", "How to enable it?") and the dominant subject is clearly established in the conversation, explicitly include that subject in the rewritten query.',
		 * '- If the current query is a very short reply (e.g. "yes", "no", "ok", "sure"), convert it into a complete, explicit request that reflects what the user is responding to, including the relevant subject and topic from the conversation.',
		 * '- When expanding short replies, do NOT invent new goals or new information beyond what is clearly implied.',
		 * '- Do NOT guess subjects that are not clearly implied.',
		 * '- Do NOT change intent.',
		 * '- Do NOT add new constraints or unrelated information.',
		 * '- Fix typos if needed.',
		 * '- If already clear and search-ready, return unchanged.',
		 * '- Return only the rewritten query.',
		 * '- Keep the same language as the input.',
		 * ];
		 */
		$system_message = implode( "\n", $system_lines );

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				],
			],
		] ) );

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [
						'value' => $payload,
					],
				],
			],
		] ) );

		$response = $this->get_ai_provider_utility()->rerank( $messages );
		$rewritten = trim( $response->extract_text() );

		return $rewritten !== '' ? $rewritten : null;
	}

	/**
	 * Generates a message for search results on the first step of a searchable parameter.
	 *
	 * Creates a natural, conversational message informing the user about search results
	 * based on their initial query. The message is a statement, not a question.
	 *
	 * @param string $user_message The user's original message/query
	 * @param string $search_query The refined search query that was used
	 * @param bool   $has_results Whether search results were found
	 *
	 * @return Message The generated message
	 * @throws Exception If message generation fails
	 * @since 1.0.0
	 */
	public function generate_search_result_message( string $user_message, string $search_query, bool $has_results, $stream = false ): ?Message {
		$messages = new Collection();

		// Build system message
		$system_message = implode( "\n", [
			"You are a friendly AI assistant helping users with product searches.",
			"Your task is to generate a natural, conversational message about search results based on the user's query.",
			"",
			"## CONTEXT:",
			"- This is the FIRST step in a searchable parameter collection process",
			"- The user has just provided their initial search query",
			"- You need to inform them about the search results",
			"",
			"## GUIDELINES:",
			"- The message should be a STATEMENT, not a question",
			"- Make your message corresponding to whether results were found or not",
			"- Make your message corresponding to user message and tone",
			"- Make your message warm and helpful",
			"- Be natural and conversational, matching the user's tone",
			"- Reference the search query/keyword naturally in your message",
			"- If results were found: Express that you found items related to their query",
			"- If no results found: Politely inform them and ask them to refine their search",
			"- Keep it concise (1-3 sentences)",
			"- Be helpful and encouraging",
			"",
			"## OUTPUT FORMAT:",
			"Return ONLY the message text, without any extra commentary.",
			"",
			"## EXAMPLES:",
			"Found results:",
			"  User: 'I want a blue hoodie'",
			"  Output: 'Here's what I found related to \"*blue hoodie*\"'",
			"",
			"No results:",
			"  User: 'I want a blue hoodie'",
			"  Output: 'I couldn't find anything related to \"*blue hoodie*\", could you please refine your search?'",
			"",
			"No Keyword:",
			"  User: 'Can you share available products?'",
			"  Output: 'Here are some of our available products. You can also use the search below to quickly find the product you’re looking for.'",
			"",
		] );

		$system_message = apply_filters( 'lbaic_copilot_system_message', $system_message );

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		// Build user message with context
		$user_prompt = sprintf(
			"User's original message: %s\nSearch query used: %s\nResults found: %s\n\nGenerate an appropriate message about the search results.",
			$user_message,
			$search_query,
			$has_results ? 'Yes' : 'No'
		);

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $user_prompt ],
				]
			],
		] ) );

		// Delegate to AI provider utility
		return $this->get_ai_provider_utility()->generate_action_success_message( $messages, $stream );
	}

	/**
	 * Generate discovery response for keyword scenarios.
	 *
	 * This call is dedicated to relevance ranking + response text generation.
	 *
	 * @param  string  $payload
	 *
	 * @return array|null
	 * @throws Exception
	 */
	public function generate_product_discovery_rerank_message( $payload ): ?array {
		$system_message = implode("\n", [
			"You are a precise e-commerce product discovery assistant.",
			"Your task is to process query-based discovery context and return JSON only.",
			"",
			"TASK:",
			"1. Rank products by relevance to the search query.",
			"2. Include ALL relevant matches in ranked order.",
			"3. Generate a short, natural announcement message stating what was found.",
			"4. If no relevant products are found, provide a brief, friendly message.",
			"",
			"RULES:",
			"- Return ONLY valid JSON. No markdown, no extra text.",
			"- product_ids must include only IDs present in the provided products list.",
			"- Do not invent product attributes or availability.",
			"- Message must be a statement — NOT a question.",
			"- Include 1-2 relevant emojis naturally.",
			"- Keep the message to 1-2 sentences maximum.",
			"",
			"FORBIDDEN (never do these):",
			"- Do NOT end the message with a question.",
			"- Do NOT ask 'Would you like to see more...?' or any variation.",
			"- Do NOT suggest style alternatives, refinements, or other categories.",
			"- Do NOT say 'Let me know if you want...' or similar follow-up prompts.",
			"- The shopper decides next steps — your only job is to announce the results.",
			"",
			"OUTPUT JSON SHAPE:",
			"{",
			"  \"message\": \"Result announcement only...\",",
			"  \"product_ids\": [123, 456, 789]",
			"}",
		]);

		$messages = new Collection();

		$system_message = apply_filters( 'lbaic_copilot_system_message', $system_message );

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $payload ],
				]
			],
		] ) );
		error_log( 'messages are: ' . print_r( $messages, true ) );

		return $this->get_ai_provider_utility()->generate_json_message( $messages );	}

	/**
	 * Generate discovery response for direct search mode.
	 *
	 * @param  string  $payload
	 *
	 * @return Message|null
	 * @throws Exception
	 */
	public function generate_product_discovery_search_message( $payload ): ?Message {
		$messages       = new Collection();
		$system_message = implode("\n", [
			"You are a precise e-commerce product discovery assistant.",
			"Your task is to generate a single customer-facing message announcing the search results.",
			"",
			"TASK:",
			"Generate a concise, natural statement based on the provided query and products context.",
			"",
			"RULES:",
			"- Return ONLY plain message text (no JSON, markdown, or extra commentary).",
			"- Use only payload context. Never invent product names or details.",
			"- Include 1-2 relevant emojis naturally.",
			"- Keep it to 1-2 sentences. Statement only.",
			"",
			"FORBIDDEN (never do these):",
			"- Do NOT end the message with a question.",
			"- Do NOT ask 'Would you like to see more...?' or any variation.",
			"- Do NOT suggest style alternatives, refinements, or other categories.",
			"- Do NOT say 'Let me know if you want...' or similar follow-up prompts.",
			"- The shopper decides next steps — your only job is to announce the results.",
			"",
			"EXAMPLES:",
			"FOUND_ITEMS_COUNT = 5 → \"I found several products matching your criteria 🎉.\"",
			"FOUND_ITEMS_COUNT = 1 → \"There's 1 item that matches your search 👀.\"",
			"FOUND_ITEMS_COUNT = 0 → \"I didn't find any products matching that criteria 😔.\"",
		]);

		$system_message = apply_filters( 'lbaic_copilot_system_message', $system_message );

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $payload ],
				]
			],
		] ) );

		return $this->get_ai_provider_utility()->generate_action_success_message( $messages );
	}

	/**
	 * Generate a structured clarification question for recommendation narrowing.
	 *
	 * Expected output JSON:
	 * {
	 *   "question": "string",
	 *   "buttons": [{"label":"string","value":"string"}]
	 * }
	 *
	 * @param  array<string, mixed>|string  $payload
	 * @return array<string, mixed>|null
	 * @throws Exception
	 */
	public function generate_product_discovery_clarification_question( $payload ): ?array {
		$messages       = new Collection();
		$system_message = implode( "\n", [
			'You generate structured clarification questions for e-commerce product recommendations.',
			'',
			'TASK:',
			'- Write one short shopper-facing clarification question.',
			'- Propose concise button labels and values based only on the provided options.',
			'- Keep wording friendly and direct.',
			'',
			'RULES:',
			'- Return JSON only. No markdown.',
			'- Keep question under 18 words.',
			'- Keep button labels short (1-3 words).',
			'- Keep button values machine-friendly lower_snake_case when possible.',
			'- Do not invent options outside provided payload.',
			'',
			'OUTPUT JSON:',
			'{',
			'  "question": "string",',
			'  "buttons": [',
			'    {"label":"string","value":"string"}',
			'  ]',
			'}',
		] );

		$system_message = apply_filters( 'lbaic_copilot_system_message', $system_message );

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		$user_payload = is_string( $payload ) ? $payload : wp_json_encode( $payload );
		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => (string) $user_payload ],
				]
			],
		] ) );

		return $this->get_ai_provider_utility()->generate_json_message( $messages );
	}

	/**
	 * Generate a friendly error message for product discovery failures.
	 *
	 * @param  string  $error_message
	 * @param  string  $query
	 * @param  array   $filters
	 *
	 * @return Message|null
	 * @throws Exception
	 */
	public function generate_product_discovery_error_message(
		string $error_message,
		string $query = '',
		array $filters = []
	): ?Message {
		$messages       = new Collection();
		$filters_string = wp_json_encode( $filters );

		$system_message = implode( "\n", [
			"You are a friendly e-commerce assistant handling product discovery errors.",
			"Your task is to explain the issue in a calm, natural, and helpful way.",
			"",
			"GUIDELINES:",
			"- Keep it to 1-2 short sentences",
			"- Sound reassuring and conversational",
			"- Include 1-2 relevant emojis",
			"- Avoid technical jargon and stack traces",
			"- If possible, gently suggest what the user can try next",
			"",
			"OUTPUT FORMAT:",
			"Return ONLY the final message text (no JSON, no extra commentary).",
		] );

		$system_message = apply_filters( 'lbaic_copilot_system_message', $system_message );

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		$user_prompt = implode( "\n", [
			'Create a user-facing product discovery error message with this context:',
			'- Search query: ' . ( '' !== trim( $query ) ? $query : '[none]' ),
			'- Applied filters: ' . ( is_string( $filters_string ) && '' !== $filters_string ? $filters_string : '[none]' ),
			'- Internal error: ' . $error_message,
		] );

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $user_prompt ],
				]
			],
		] ) );

		return $this->get_ai_provider_utility()->generate_action_success_message( $messages );
	}

	/**
	 * Generate an add-to-cart success message via Copilot.
	 *
	 * @param  string  $payload  Product and cart information payload.
	 *
	 * @return Message|null The generated message.
	 * @since 1.0.0
	 */
	public function generate_add_to_cart_message( $payload ): ?Message {
		$messages = new Collection();

		$system_message = implode( "\n", [
			"You are a friendly e-commerce assistant celebrating a customer's purchase decision.",
			"Your job is to generate a warm, encouraging message confirming their product was added to cart.",
			"",
			"TONE:",
			"- Celebratory and warm",
			"- Enthusiastic about their choice",
			"- Concise (1-2 sentences max)",
			"- Personable, use natural language",
			"- Include 1-2 relevant emojis (✅, 🎉, ➡️, 🛒, etc.)",
			"",
			"EXAMPLES:",
			"✅ Amazing choice! Your product is in the cart. Ready to check out when you are!",
			"🎉 Perfect! I've added it to your cart. Check out whenever you're ready!",
			"➡️ Done! Your item is in the cart and waiting for you.",
			"",
			"GUIDELINES:",
			"- Acknowledge the product was successfully added",
			"- Optionally mention cart total or item count",
			"- Never use phrases like 'click here' or 'press button' - keep it conversational",
			"- Keep it brief and friendly",
		] );

		$system_message = apply_filters( 'lbaic_copilot_add_to_cart_message', $system_message );

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $payload ],
				]
			],
		] ) );

		// Delegate to AI provider utility
		return $this->get_ai_provider_utility()->generate_action_success_message( $messages );
	}

	/**
	 * Generate a friendly cancellation message when a user cancels an action/form.
	 *
	 * Uses AI to create a warm, reassuring message that acknowledges the cancellation
	 * and offers continued assistance. Falls back to a static message if AI is unavailable.
	 *
	 * @param  string  $action_context  The action context/purpose (e.g., "Contact Form")
	 * @param  bool  $stream  Whether to stream the response
	 *
	 * @return Message|null The generated cancellation message, or null on failure
	 * @throws Exception If message generation fails
	 * @since 1.0.0
	 */
	public function generate_action_cancellation_message( string $action_context = '', $stream = false ) {
		$messages = new Collection();

		$system_message = implode( "\n", array_filter( [
			"You are a friendly assistant embedded in a website chatbot.",
			"The user has just cancelled a form/action they were filling out.",
			"Your task is to generate a short, warm, understanding message that:",
			"",
			"## GUIDELINES:",
			"- Acknowledges the cancellation without making the user feel bad",
			"- Reassures them it's perfectly fine",
			"- Offers to help with something else",
			"- Keep it to ONE or TWO sentences maximum",
			"- Be conversational, warm, and supportive",
			"- Use 1-2 appropriate emojis to add warmth",
			"- No technical details or jargon",
			$action_context ? "- The form/action they cancelled: " . $action_context : "",
			"",
			"## OUTPUT FORMAT:",
			"Return ONLY the message text, without any formatting, quotes, or extra commentary.",
			"",
			"## EXAMPLES:",
			"Good: 'No worries at all! 😊 The form has been cancelled. How else can I help you?'",
			"Good: 'No problem! 👍 Form cancelled. Feel free to ask me anything else!'",
			"Good: 'All good — cancelled! 😊 Let me know if there's anything else I can do for you.'",
		] ) );

		$system_message = apply_filters( 'lbaic_copilot_system_message', $system_message );

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		$user_prompt = $action_context
			? sprintf( 'The user cancelled the "%s" form. Generate a friendly cancellation acknowledgment.', $action_context )
			: 'The user cancelled a form. Generate a friendly cancellation acknowledgment.';

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $user_prompt ],
				]
			],
		] ) );

		return $this->get_ai_provider_utility()->generate_action_success_message( $messages, $stream );
	}

	public function detect_language( string $text ) {
		$messages = new Collection();

		$system_message = implode( "\n", [
			'You analyze any input text and determine the language it is written in. Always respond with the language name only (e.g., “English”, “Spanish”, “German”).',
			'If the text contains multiple languages, return the dominant one.',
			'If you are uncertain, return the most likely language.',
			'Examples:',
			'Input: "Bonjour, comment allez-vous ?"',
			'Output: French',
			'Input: "¿Dónde está la estación?"',
			'Output: Spanish',
			'Input: "Guten Morgen!"',
			'Output: German',
		] );

		$messages->push_item( Message::make( [
			'role' => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );
		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $text ],
				]
			],
		] ) );
		// todo change this
		return $this->get_ai_provider_utility()->generate_action_success_message( $messages );
	}

	/**
	 * Checks if the given content appears to be AI instructions rather than an entity overview.
	 *
	 * Uses AI to determine if the content is instructions for an AI system rather than
	 * a descriptive overview of an entity (company, person, organization).
	 *
	 * @param  string  $content  The content to check.
	 *
	 * @return bool True if content appears to be instructions, false otherwise.
	 * @throws Exception If the AI request fails.
	 * @since 1.0.0
	 */
	public function is_instructions_content( string $content ): bool {
		$messages = new Collection();

		// Build system message for instruction detection
		$system_message = implode( "\n", [
			"You are a content classifier. Your task is to determine if the given text is AI instructions or an entity overview/description.",
			"",
			"## DEFINITIONS:",
			"- AI INSTRUCTIONS: Text that tells an AI system HOW to behave, what to do, or how to respond. Examples:",
			"  * 'You are a helpful assistant. Always be polite and professional.'",
			"  * 'When asked about products, provide detailed specifications.'",
			"  * 'Respond in a friendly tone and use emojis when appropriate.'",
			"  * 'If the user asks about pricing, redirect them to the contact page.'",
			"",
			"- ENTITY OVERVIEW: Text that DESCRIBES what an entity (company, person, organization) IS, what it does, or its background. Examples:",
			"  * 'We are a software company specializing in AI solutions.'",
			"  * 'Our company was founded in 2020 and provides cloud services.'",
			"  * 'We offer web design, development, and digital marketing services.'",
			"  * 'John is a software engineer with 10 years of experience in web development.'",
			"",
			"## YOUR TASK:",
			"Analyze the provided text and determine if it is AI INSTRUCTIONS or an ENTITY OVERVIEW.",
			"",
			"## OUTPUT FORMAT:",
			"Return ONLY 'yes' if the text contains AI instructions, or 'no' if it is an entity overview.",
			"Do not include any explanations, formatting, or additional text.",
			"",
			"## EXAMPLES:",
			"Input: 'You should always greet users warmly and provide helpful responses.'",
			"Output: yes",
			"",
			"Input: 'We are a technology company that develops mobile applications.'",
			"Output: no",
			"",
			"Input: 'When customers ask about shipping, tell them it takes 3-5 business days.'",
			"Output: yes",
			"",
			"Input: 'Our company specializes in e-commerce solutions and has been serving clients since 2015.'",
			"Output: no",
		] );
		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );
		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $content ],
				]
			],
		] ) );
		// Make request through AI provider utility
		$ai_provider_utility = $this->get_ai_provider_utility();
		$response            = $ai_provider_utility->generate_action_success_message( $messages );
		// Extract response text
		$response_text = '';
		if ( $response instanceof Message ) {
			$response_text = trim( strtolower( $response->extract_text() ) );
		}

		// Check if response indicates instructions
		return str_starts_with( $response_text, 'yes' ) || str_contains( $response_text, 'yes' );
	}

//	/**
//	 * Reranks candidate knowledge retrieval results based on relevance to the latest user question.
//	 *
//	 * Uses AI to analyze the latest user question, recent conversation context, and a list of candidate
//	 * knowledge snippets. The AI selects the most relevant candidates that can help answer the question,
//	 * following strict relevance rules and returning only the IDs of the selected candidates.
//	 *
//	 * @param string $payload A JSON-serializable array containing:
//	 *                       - "latest_question": string – the current user question.
//	 *                       - "conversation": string|null – short recent transcript, or null.
//	 *                       - "candidates": array of { "id": number, "text": string }.
//	 *
//	 * @return Message|null An array of selected candidate IDs, ordered from most relevant to least relevant.
//	 * @throws Exception If the AI request fails or returns invalid output.
//	 * @since 1.0.0
//	 */
//	public function rerank( $payload ) {
//		// Explicit JSON contract for the model.
//		$system_message = implode( "\n", [
//			'You are a KNOWLEDGE RETRIEVAL RE-RANKER.',
//			'',
//			'You will receive a JSON object with:',
//			'- "latest_question": string – the current user question.',
//			'- "conversation": string|null – short recent transcript, or null.',
//			'- "candidates": array of { "id": number, "text": string }.',
//			'',
//			'TASK:',
//			'- Select the MOST RELEVANT candidate IDs to answer "latest_question".',
//			'',
//			'RULES:',
//			'1. SELECT a candidate if it clearly explains how to answer the question.',
//			'2. If the question allows MULTIPLE METHODS (e.g., manual setup and wizard setup), INCLUDE ALL candidates that describe different valid methods.',
//			'3. If a candidate partly answers the question, INCLUDE it.',
//			'4. If relevance is weak or unclear, EXCLUDE it.',
//			'5. Prefer precision over recall.',
//			'6. Return at most 3 IDs.',
//			'7. If no candidate clearly answers the question, return [].',
//			'',
//			'IMPORTANT:',
//			'- Do NOT exclude a valid method just because another method exists.',
//			'- If multiple valid approaches clearly answer the question, include multiple IDs.',
//			'',
//			'STRICT OUTPUT FORMAT:',
//			'- Return ONLY a JSON array of candidate IDs, e.g. [1734, 1773].',
//			'- Each ID MUST be taken from candidates[i].id (the exact numeric value).',
//			'- NEVER return list positions like 1, 2, 3 – only real IDs.',
//			'- Order IDs from most relevant to least relevant.',
//			'- Exclude irrelevant candidates entirely.',
//			'- Do NOT include explanations, keys, or extra text.',
//			'- Do NOT repeat or echo the input JSON payload.',
//			'- Do NOT wrap the array in markdown, code fences, or backticks.',
//			'- The first non-whitespace character of your reply MUST be "[" and the last MUST be "]".',
//			'- Your ENTIRE reply must be just the JSON array, nothing else.',
//		] );
//		$messages = new Collection();
//
//		$messages->push_item( Message::make( [
//			'role'    => 'system',
//			'content' => [
//				[
//					'type' => 'text',
//					'text' => [ 'value' => $system_message ],
//				],
//			],
//		] ) );
//
//		$messages->push_item( Message::make( [
//			'role'    => 'user',
//			'content' => [
//				[
//					'type' => 'text',
//					'text' => [ 'value' => $payload ],
//				],
//			],
//		] ) );
//
//		return $this->get_ai_provider_utility()->rerank( $messages );
//	}

	public function generate_confirmation_message( string $payload ) {
		$messages = new Collection();

		$system_message = implode( "\n", [
			'You generate confirmation UI copy for a WooCommerce shopping assistant.',
			'You are NOT a conversational assistant. You output structured JSON only.',
			'',
			'CONTEXT',
			'The user has already shown interest in a product and the system has automatically',
			'triggered this confirmation step. The UI already shows the product card and',
			'confirm/cancel buttons - so the message text must NOT ask "Would you like to add',
			'it to your cart?" or any equivalent question. That would be redundant and awkward.',
			'',
			'--------------------------------',
			'DECISION LOGIC',
			'',
			'Determine product type from Product Information.',
			'',
			'If product type is "Simple":',
			'- Write a brief, natural acknowledgment of the user\'s choice.',
			'- Tone: warm affirmation, like a helpful friend confirming a good pick.',
			'- Do NOT ask whether they want to add it - the buttons handle that.',
			'- Example: "Nice choice! Hit Confirm and it\'s yours." or "Looks great - just confirm below."',
			'',
			'If product type is "Variable":',
			'- Acknowledge the choice, then guide the user to pick their options.',
			'- Mention ALL required attributes (from the payload).',
			'- Use collaborative phrasing.',
			'- BAD: "Please select Size and Color." GOOD: "Just choose your Size and Color, and you\'re all set."',
			'',
			'--------------------------------',
			'TONE RULES',
			'',
			'- Warm, natural, conversational.',
			'- Never robotic, never abrupt, never a system-validation-error tone.',
			'- Emojis optional but subtle (max 1).',
			'- Reflect the user\'s sentiment from the conversation where appropriate.',
			'',
			'--------------------------------',
			'RESPONSE FORMAT (STRICT JSON ONLY)',
			'',
			'Return valid JSON. No markdown. No explanations. No extra text.',
			'',
			'{',
			'  "ui_type": "simple_confirmation" | "variation_selection",',
			'  "message": "max 25 words, smooth and conversational, no redundant questions",',
			'  "primary_action": {',
			'    "label": "1-3 words, action-oriented"',
			'  },',
			'  "secondary_action": {',
			'    "label": "1-3 words"',
			'  }',
			'}',
			'',
			'--------------------------------',
			'FIELD RULES',
			'',
			'message:',
			'- Maximum 25 words.',
			'- Must feel connected to the conversation.',
			'- NEVER ask "Would you like to add it to your cart?" or similar.',
			'- NEVER begin with "Please select..."',
			'- For variable products: mention ALL required attributes.',
			'',
			'primary_action.label:',
			'- 1-3 words. Clear, confident next step.',
			'- Simple product: "Add to Cart" or "Confirm".',
			'- Variable product: "Choose Options".',
			'',
			'secondary_action.label:',
			'- 1-3 words. Cancel / Back / Not Now.',
			'',
			'--------------------------------',
			'NEVER:',
			'- ASK WHETHER THE USER WANTS TO ADD THE PRODUCT',
			'- START WITH "Please select..."',
			'- SOUND LIKE A VALIDATION ERROR',
			'- INVENT PRODUCT DETAILS',
			'- EXCEED 25 WORDS',
			'- INCLUDE MARKDOWN OR EXTRA TEXT',
			'- BREAK JSON FORMAT',
			'',
			'GOOD SIMPLE EXAMPLES:',
			'"Nice choice! Hit confirm and it\'s on its way to your cart."',
			'"Looks great - just confirm below to add it."',
			'',
			'GOOD VARIABLE EXAMPLE:',
			'"Nice pick! Just choose your Size and Color, and you\'re all set."',
			'',
			'--------------------------------',
			'OUTPUT ONLY VALID JSON.'
		]);

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $payload ],
				]
			],
		] ) );

		return $this->get_ai_provider_utility()->generate_json_message( $messages );
	}

	/**
	 * Format message history as a context summary string.
	 *
	 * Converts message history array into a formatted string for inclusion in prompts.
	 * Separates the last user message from history for clarity.
	 *
	 * @param  array  $message_history  Array of Message objects.
	 * @param  string  $current_user_message  The current user message (will be excluded from history).
	 *
	 * @return string Formatted history summary, or empty string if no history.
	 * @since 1.0.0
	 */
	private function format_message_history_context( array $message_history, string $current_user_message = '' ): string {
		if ( empty( $message_history ) ) {
			return '';
		}

		// Get last 6 messages, excluding the current user message
		$history_messages = array_slice( $message_history, -6 );
		$history_summary  = [];

		foreach ( $history_messages as $history_message ) {
			if ( ! $history_message instanceof Message ) {
				continue;
			}

			$message_text = $history_message->extract_text();
			if ( empty( $message_text ) ) {
				continue;
			}

			// Skip if this is the current user message
			if ( $message_text === $current_user_message && $history_message->get_role() === Message::ROLE_USER ) {
				continue;
			}

			$role_label = $history_message->get_role() === Message::ROLE_USER ? 'User' : 'Assistant';
			$history_summary[] = sprintf( '%s: %s', $role_label, $message_text );
		}

		if ( empty( $history_summary ) ) {
			return '';
		}

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

	/**
	 * Analyze user intent with optional message history context.
	 *
	 * @param  string  $user_prompt
	 * @param  string  $history  formatting of Message objects for context (up to 6 messages).
	 *
	 * @return Message The intent analysis result.
	 * @throws Exception
	 * @since 1.0.0
	 */
	public function analyze_intent( string $user_prompt, string $history ) {
		$messages = new Collection();
		// Build system message with history context
		$system_message = implode( "\n", [
			'You are an intent analyzer for an e-commerce chatbot.',
			'Your task:',
			'Classify the user\'s CURRENT message into one intent using conversation context.',
			'--------------------------------',
			'INTENT OPTIONS',
			'add_to_cart',
			'- User clearly confirms they want the product added.',
			'- Strong purchase confirmation.',
			'- Examples: "yes add it", "add to cart", "buy it", "I\'ll take it", "do it".',
			'more_info',
			'- User asks questions, requests details, wants clarification, or intent is unclear.',
			'- Includes hesitation, follow-up questions, or mixed responses.',
			'- Default choice if unsure.',
			'reject',
			'- User clearly declines the product or stops purchase.',
			'- Examples: "no", "not interested", "too expensive", "never mind".',
			'--------------------------------',
			'DECISION RULES (STRICT)',
			'- Analyze ONLY the current user message.',
			'- Use conversation history only for context.',
			'- If intent is ambiguous → choose "more_info".',
			'- Do NOT classify add_to_cart if user asks any question.',
			'- Do NOT classify add_to_cart if user shows hesitation.',
			'- Short confirmations like "yes" mean add_to_cart ONLY if they clearly respond to a purchase confirmation.',
			'- Requests for details → more_info.',
			'- Requests for alternatives → more_info.',
			'- Changing topic → more_info.',
			'--------------------------------',
			'OUTPUT FORMAT',
			'Return ONLY one word:',
			'add_to_cart',
			'more_info',
			'reject',
			'',
			'No explanations.',
			'',
			"\n\nCONVERSATION HISTORY:\n" . $history
		] );


		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $user_prompt ],
				]
			],
		] ) );
		error_log('messages are : '. print_r($messages, true));

		return $this->get_ai_provider_utility()->generate_action_success_message( $messages );
	}

	/**
	 * Generate a natural variation question for variable products.
	 *
	 * Uses LLM to create a conversational question asking the user to select
	 * their preferred product variations (color, size, etc.).
	 *
	 * @param  string  $payload  JSON payload containing product and variation data.
	 *
	 * @return null|Message The generated variation question message.
	 * @throws Exception
	 * @since 1.0.0
	 */
	public function generate_variation_question( string $payload ): ?Message {
		$messages = new Collection();

		$system_message = <<<'SYSTEM'
You are a friendly and helpful e-commerce assistant. Your job is to generate natural, conversational questions asking users to select product variations.

Guidelines:
- Keep it brief and conversational (1-2 sentences)
- Be enthusiastic and positive about the product
- Use natural language and 1-2 relevant emojis if appropriate
- Acknowledge the user's interest in the product
- Present available variations naturally and clearly
- Ask which specific variant they prefer

CRITICAL RULES:
- Always include the available options from the payload (colors, sizes, etc.)
- Format options in a readable way (e.g., "Red, Blue, or Black" or "sizes S, M, L, XL")
- Make it feel like a natural conversation, not a form
- Use warm, friendly tone
- Don't overwhelm with too many details - keep it simple

Tone examples:
- "Great choice! 👕 This hoodie comes in Red, Blue, or Black, and sizes S, M, L, XL. Which color and size would you like?"
- "Perfect! 🎯 This product is available in several options. What's your preferred color and size?"
- "Awesome! ✨ Which variation would you prefer? We have [options] available."

Remember: Your goal is to make the user feel excited about selecting their perfect variant, not to overwhelm them with choices.
SYSTEM;

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $payload ],
				]
			],
		] ) );

		return $this->get_ai_provider_utility()->generate_action_success_message( $messages );
	}

	/**
	 * Detect variation selection from user message.
	 *
	 * Analyzes user message to determine if it matches any variation from the provided list.
	 * Returns the variation ID if a match is found, otherwise returns null.
	 *
	 * @param  string  $user_message  The user's message text.
	 * @param  string  $payload  JSON payload containing product and variation data.
	 * @param  array  $message_history  Optional array of Message objects for context (up to 6 messages).
	 *
	 * @return int|null The variation ID if matched, or null if no match.
	 * @since 1.0.0
	 */
	public function detect_variation_selection( string $user_message, string $payload, array $message_history = [] ): ?int {
		$messages = new Collection();

		// Format history as context summary
		$history_context = $this->format_message_history_context( $message_history, $user_message );

		// Build production-level system message
		$system_message = <<<'SYSTEM'
You are a variation selection matcher for an e-commerce chatbot.

TASK: Determine if the user is SELECTING a variation or asking an INFORMATIONAL question, then match to variation_id if selecting.

CRITICAL DECISION RULES:
1. If the user is asking an informational question (about price, size, stock, details, comparisons), return: null
2. If the user is clearly SELECTING a variation (choosing one to add to cart), match it to a variation_id
3. Return ONLY the variation_id (a number) or "null" - nothing else
4. Never explain or add text

INFORMATIONAL QUESTIONS (return null):
- Questions about price, cost, pricing, "how much"
- Questions about size, dimensions, measurements, "what size"
- Questions about stock, availability, inventory
- Questions asking for details, information, descriptions, comparisons
- Any question that seeks information rather than making a selection

SELECTION INTENT (match to variation_id):
- User says a size/color/attribute name: "medium", "large", "red"
- User confirms selection: "yes, medium", "I want medium", "medium please"
- User makes a clear choice: "I'll take the medium one", "medium works for me"
- Short, direct responses indicating choice

MATCHING RULES:
- Match attributes exactly (case-insensitive)
- When user says "medium", find the variation with size=Medium
- When user says "medium red", find variation with BOTH size=Medium AND color=Red
- If unsure whether it's informational or selection, return: null (let agent-service handle it)

CONTEXT INSTRUCTIONS:
- You will receive conversation history (if available) and the current user message
- Use the history to understand the conversation flow and context
- The current user message is the LAST message - analyze this one specifically
- Consider the conversation context when determining if it's informational or selection

OUTPUT FORMAT:
Return ONLY the variation_id (a number) or "null", nothing else.
Examples: 123 or 456 or null
SYSTEM;

		// Add history context to system message if available
		if ( ! empty( $history_context ) ) {
			$system_message .= "\n\nCONVERSATION HISTORY:\n" . $history_context;
		}

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		// Build structured user prompt with current message and variation data
		$user_prompt = <<<PROMPT
CURRENT USER MESSAGE (analyze this):
"{$user_message}"

AVAILABLE VARIATIONS:
{$payload}
PROMPT;

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $user_prompt ],
				]
			],
		] ) );

		try {
			$response = $this->get_ai_provider_utility()->generate_action_success_message( $messages );

			if ( $response instanceof Message ) {
				$response_text = $response->extract_text();
				if ( empty( $response_text ) ) {
					return null;
				}

				// Extract variation ID from response
				$response_text = trim( strtolower( $response_text ) );

				// Check if response is "null"
				if ( $response_text === 'null' || empty( $response_text ) ) {
					return null;
				}

				// Try to extract number from response
				if ( preg_match( '/\d+/', $response_text, $matches ) ) {
					$variation_id = (int) $matches[0];
					return $variation_id > 0 ? $variation_id : null;
				}

				return null;
			}

			return null;
		} catch ( \Exception $e ) {
			return null;
		}
	}

	/**
	 * Extract selected variation attributes while state is awaiting variations.
	 *
	 * @param  string  $payload
	 *
	 * @return array<string,string>
	 * @throws Exception
	 */
	public function extract_waiting_variation_attributes( string $payload ): array {
		$messages = new Collection();

		$system_message = <<<'SYSTEM'
You are a strict attribute extractor for a WooCommerce variation workflow.

TASK:
Read the provided payload and extract ONLY valid variation attribute selections from the current user message.

RULES:
- Use only attribute taxonomies and option slugs from the payload.
- Extract from the CURRENT user message only. Do not carry old choices from conversation history.
- If a value is invalid or not clearly selected, do not include it.
- If no valid attributes are selected, return an empty object.
- Never invent keys or values.
- Handle messy language and map to the closest valid option only when clear.
- If user message is a pure confirmation (e.g. "yes", "ok", "sure"), resolve at MOST one attribute.
- On pure confirmation, prefer the attribute currently being asked (if clear from context); never fill multiple attributes.

OUTPUT (STRICT JSON ONLY):
{
  "selected_attributes": {
    "<attribute_taxonomy>": "<option_slug>"
  }
}

If nothing is selected:
{
  "selected_attributes": {}
}
SYSTEM;

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $payload ],
				]
			],
		] ) );

		$result = $this->get_ai_provider_utility()->generate_json_message( $messages );
		if ( is_array( $result ) && isset( $result['selected_attributes'] ) && is_array( $result['selected_attributes'] ) ) {
			return $result['selected_attributes'];
		}

		return [];
	}

	/**
	 * Classify non-attribute message in waiting-for-variations state.
	 *
	 * @param  string  $payload
	 *
	 * @return string|null
	 * @throws Exception
	 */
	public function analyze_waiting_variation_intent( string $payload ): ?string {
		$messages = new Collection();

		$system_message = <<<'SYSTEM'
You classify a user's message when the assistant is in WAITING_FOR_VARIATIONS.

Your output must be EXACTLY one label:
- reject
- new_topic
- question_related
- question_unrelated
- vague
- confirm
- continue

PRIORITY:
1) reject -> explicit cancellation or decline
2) new_topic -> explicit switch to a different shopping intent/request
3) question_related -> informational question about the current product/attributes (size, color, material, recommendation, etc.)
4) question_unrelated -> informational question not about the current product flow
5) vague -> short unclear replies like "okay", "hmm", "maybe"
6) confirm -> explicit confirmation to proceed
7) continue -> everything else that should keep the same flow

CONTEXT:
- The payload includes formatted conversation history.
- Use that conversation context to decide whether this message is related to the current product flow or unrelated.
- If uncertain between related/unrelated question, choose question_related.

RULES:
- Return one label only, lowercase.
- No explanation.
SYSTEM;

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $payload ],
				]
			],
		] ) );

		$response = $this->get_ai_provider_utility()->generate_action_success_message( $messages );
		if ( ! $response instanceof Message ) {
			return null;
		}

		$intent = strtolower( trim( (string) $response->extract_text() ) );
		if ( in_array( $intent, [ 'reject', 'new_topic', 'question', 'question_related', 'question_unrelated', 'vague', 'confirm', 'continue' ], true ) ) {
			return $intent;
		}

		return null;
	}

	/**
	 * Generate add to cart success message.
	 *
	 * Creates a natural, conversational success message when a product is added to cart.
	 *
	 * @param  string  $payload  JSON payload containing product information.
	 *
	 * @return Message The generated success message.
	 * @throws Exception
	 * @since 1.0.0
	 */
	public function generate_add_to_cart_success_message( string $payload ): ?Message {
		$messages = new Collection();

		$system_message = <<<'SYSTEM'
You are a friendly and helpful e-commerce assistant. Your job is to generate natural, enthusiastic success messages when products are added to the cart.

Guidelines:
- Keep it brief and conversational (1-2 sentences)
- Be enthusiastic and positive
- Use natural language and 1-2 relevant emojis if appropriate
- Acknowledge the product that was added
- Optionally mention the price
- Suggest next steps (continue shopping or checkout)

CRITICAL RULES:
- Always acknowledge the product name from the payload
- Include price information if available
- Be warm and friendly
- Make the user feel good about their purchase
- Keep it concise - don't overwhelm

Tone examples:
- "✅ Perfect! I've added [Product Name] to your cart. [Price] | Would you like to continue shopping or proceed to checkout?"
- "Awesome! 🛒 [Product Name] is now in your cart. [Price] | Ready to checkout or browse more?"
- "Great choice! ✨ I've added [Product Name] to your cart. [Price] | What would you like to do next?"

Remember: Make the user feel excited about their purchase and guide them to the next step naturally.
SYSTEM;

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $payload ],
				]
			],
		] ) );

		return $this->get_ai_provider_utility()->generate_action_success_message( $messages );
	}

	public function rerank_products( string $payload ) {
		$messages= new Collection();
		$system_message = <<<'SYSTEM'
YOU ARE AN E-COMMERCE PRODUCT SEARCH AND RERANKING ASSISTANT.

INPUT:
- User search query
- Recent conversation
- List of products (name, id, categories, description, variations)

TASK:
1. Select products that clearly match the user query or conversation context.
2. Rank them by relevance (best first).
3. Exclude any product without clear evidence of relevance.

MATCHING RULES:
- Use product name, categories, description, and variations.
- Prefer exact keyword or product type matches.
- Use semantic meaning only if strongly related.
- If the query specifies attributes (type, brand, color, size, etc.), prioritize those.
- NEVER include a product if the query intent is not supported by the product data.

RANKING PRIORITY:
1. Exact product type match
2. Keyword match in name or category
3. Matching attributes or variations
4. General semantic relevance

MESSAGE RULES:
- One short friendly sentence.
- Maximum 12 words.
- Do NOT mention product names.
- Phrase the message as suggestions, not the full catalog.
- Use wording like "some options", "a few matches", or "some suggestions".

OUTPUT FORMAT (STRICT):

Return ONLY valid JSON:

{
  "ids": [id1, id2, id3],
  "message": "short response"
}

OUTPUT RULES:
- ids must contain ONLY product IDs
- order by relevance
- no duplicates
- no text outside JSON
- return all the matching ids

If no products match:
{
  "ids": [],
  "message": "{Friendly user message based on conversation that no product found, and suggestion about refining the search.}"
}
SYSTEM;
		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				]
			],
		] ) );

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $payload ],
				]
			],
		] ) );

		return $this->get_ai_provider_utility()->generate_json_message_for_rerank( $messages );
	}

	/**
	 * Reranks candidate knowledge retrieval results based on relevance to the latest user question.
	 *
	 * Uses AI to analyze the latest user question, recent conversation context, and a list of candidate
	 * knowledge snippets. The AI selects the most relevant candidates that can help answer the question,
	 * following strict relevance rules and returning only the IDs of the selected candidates.
	 *
	 * @param string $payload A JSON-serializable array containing:
	 *                       - "latest_question": string – the current user question.
	 *                       - "conversation": string|null – short recent transcript, or null.
	 *                       - "candidates": array of { "id": number, "text": string }.
	 *
	 * @return Message|null An array of selected candidate IDs, ordered from most relevant to least relevant.
	 * @throws Exception If the AI request fails or returns invalid output.
	 * @since 1.0.0
	 */
	public function rerank( $payload ) {
		// Explicit JSON contract for the model.
		$system_message = implode("\n", [
			'You are a KNOWLEDGE RETRIEVAL RE-RANKER.',
			'',
			'You will receive a JSON object with:',
			'- "question": string – the current user question.',
			'- "candidates": array of { "id": number, "text": string }.',
			'',
			'TASK:',
			'- Select the candidate IDs that most directly help answer the specific task or question described in "question".',
			'',
			'EVALUATION RULES:',
			'1. A candidate is relevant it explains or describes how to perform the SAME task, feature, or concept as the question.',
			'2. Prefer candidates that contain actionable instructions, explanations, or steps matching the user’s intent.',
			'3. Exclude candidates that only partially overlap in wording but describe a different task or broader/unrelated topic.',
			'4. If multiple candidates describe different valid approaches to the SAME task, include them.',
			'5. Prefer precision over recall.',
			'6. Return at most 3 IDs.',
			'7. If no candidate clearly answers the question, return [].',
			'',
			'STRICT OUTPUT FORMAT:',
			'- Return ONLY a JSON array of candidate IDs, e.g. [1734, 1773].',
			'- Each ID MUST be taken from candidates[i].id.',
			'- NEVER return list positions like 1, 2, 3 – only real IDs.',
			'- Order IDs from most relevant to least relevant.',
			'- Exclude irrelevant candidates entirely.',
			'- Do NOT include explanations or extra text.',
			'- The first non-whitespace character MUST be "[" and the last MUST be "]".',
			'- Candidate texts may be truncated. Do your best to infer relevance from the visible content.',
		]);
		$messages = new Collection();

		$messages->push_item( Message::make( [
			'role'    => 'system',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $system_message ],
				],
			],
		] ) );

		$messages->push_item( Message::make( [
			'role'    => 'user',
			'content' => [
				[
					'type' => 'text',
					'text' => [ 'value' => $payload ],
				],
			],
		] ) );

		return $this->get_ai_provider_utility()->rerank( $messages );
	}
}