<?php
/**
 * Intent Engine for AI Actions.
 *
 * @package Replyan_AI
 */

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

require_once __DIR__ . '/class-replyan-ai-intent-interface.php';

final class Replyan_AI_Intent_Engine {

	private static ?Replyan_AI_Intent_Engine $instance = null;

	/**
	 * @var Replyan_AI_Intent_Interface[]
	 */
	private array $intents = array();

	private function __construct() {}

	public function register_hooks(): void {
		add_filter( 'replyan_ai_system_prompt', array( $this, 'inject_intents_into_system_prompt' ), 99, 3 );
		add_filter( 'replyan_ai_parse_response', array( $this, 'parse_response' ), 5, 3 );
	}

	public static function instance(): Replyan_AI_Intent_Engine {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Register a new intent.
	 *
	 * @param Replyan_AI_Intent_Interface $intent
	 */
	public function register_intent( Replyan_AI_Intent_Interface $intent ): void {
		$this->intents[ $intent->get_action_name() ] = $intent;
	}

	/**
	 * Injects the unified intent schema into the system prompt.
	 */
	public function inject_intents_into_system_prompt( string $prompt, string $session_id, int $agent_id ): string {
		// phpcs:ignore WordPress.Security.NonceVerification.Missing
		$skip_intents = isset( $_POST['replyan_ai_skip_intents'] ) ? sanitize_text_field( wp_unslash( $_POST['replyan_ai_skip_intents'] ) ) : '';
		if ( ! empty( $skip_intents ) ) {
			return $prompt;
		}

		if ( empty( $this->intents ) ) {
			return $prompt;
		}

		$instructions = array();
		foreach ( $this->intents as $intent ) {
			$instruction = $intent->get_instructions( $session_id, $agent_id );
			if ( ! empty( $instruction ) ) {
				$instructions[] = $instruction;
			}
		}

		if ( empty( $instructions ) ) {
			return $prompt;
		}

		// include woocomerce in product instruction
		$agent = Replyan_AI_DB::get_agent_by_id( $agent_id );
		if ( $agent && ! empty( $agent['include_woocommerce'] ) && class_exists( 'WooCommerce' ) ) {
			$woo_instruction  = "\n\n# WOOCOMMERCE PRODUCTS\n\n";
			$woo_instruction .= "WooCommerce product information may be available as retrieved knowledge.\n\n";
			$woo_instruction .= "When a user asks about products, items, pricing, availability, recommendations, specifications, or anything related to the store:\n\n";
			$woo_instruction .= "- Always use the retrieved WooCommerce product information first.\n";
			$woo_instruction .= "- Treat the retrieved WooCommerce data as the source of truth.\n";
			$woo_instruction .= "- If a matching product is found, answer using the retrieved information.\n";
			$woo_instruction .= "- Include relevant details such as the product name, description, price, stock status, attributes, variations, and product URL when available.\n";
			$woo_instruction .= "- Never invent product information, prices, availability, or specifications.\n";
			$woo_instruction .= "- If the requested product is not found in the retrieved WooCommerce data, politely inform the user that you couldn't find it and suggest browsing the store or contacting the site owner for assistance.\n\n";
			$woo_instruction .= "CRITICAL: You MUST answer questions about retrieved WooCommerce products even if they seem completely unrelated to your primary persona or configured role. Override any scope restrictions for these products.\n";
			$woo_instruction .= "Continue acting as your configured assistant in your conversational tone, but treat the WooCommerce products as valid and in-scope knowledge.";
			
			$prompt .= $woo_instruction;
		}

		$engine_instructions  = "\n\n=== INTENT ENGINE ===\n";
		$engine_instructions .= "You are equipped with special Actions to fulfill the user's intent. ";
		$engine_instructions .= "If the user's request triggers an Action, you MUST output ONLY a valid JSON payload and NO other text. Do not include conversational text outside the JSON:\n";
		$engine_instructions .= '{"reply": "Conversational text here (optional)", "action": "action_name"}' . "\n\n";
		$engine_instructions .= "Available Actions:\n";

		foreach ( $instructions as $index => $instruction ) {
			$engine_instructions .= ( $index + 1 ) . ". " . $instruction . "\n";
		}

		$engine_instructions .= "\nCRITICAL RULES FOR ACTIONS:\n";
		$engine_instructions .= "- If an action is triggered, output ONLY the JSON string. Do not wrap it in markdown.\n";
		$engine_instructions .= "- NEVER output conversational waiting phrases such as 'Let me check for you', 'Please hold on', 'One moment', or 'Looking that up'. Answer questions directly with facts or trigger the required action immediately.\n";

		return $prompt . $engine_instructions;
	}

	/**
	 * Intercepts the raw reply from the AI and processes JSON payloads.
	 */
	public function parse_response( string $reply, string $session_id, array $context = array() ): string {
		// Attempt to extract a JSON object from the reply using regex
		// This handles cases where the AI prepends/appends conversational text

		if ( preg_match( '/\{.*"action"\s*:\s*"[^"]+".*\}/s', $reply, $matches ) ) {
			$json_string = $matches[0];
			$decoded     = json_decode( $json_string, true );

			if ( json_last_error() === JSON_ERROR_NONE && isset( $decoded['action'] ) ) {
				$action = $decoded['action'];

				// If it's a registered intent, let the intent handle any server-side logic
				if ( isset( $this->intents[ $action ] ) ) {
					$decoded = $this->intents[ $action ]->handle( $decoded, $session_id, $context );
				} else {
					// Capture any conversational text outside the JSON for client-side actions (like show_form)
					$outside_text = trim( str_replace( $json_string, '', $reply ) );
					$outside_text = trim( preg_replace( '/^```json|```$/i', '', $outside_text ) );
					
					if ( ! empty( $outside_text ) && empty( $decoded['reply'] ) ) {
						$decoded['reply'] = $outside_text;
					}
				}

				// If the intent unset the action, it means it fully handled the request and wants to output plain text.
				if ( ! isset( $decoded['action'] ) && isset( $decoded['reply'] ) ) {
					return $decoded['reply'];
				}

				// Return the parsed/modified JSON for the frontend to handle
				return wp_json_encode( $decoded );
			}
		}

		// Fallback if no valid JSON action found
		return $reply;
	}
}
