<?php
/**
 * AI Conversation Background Analyzer (Premium Module)
 *
 * @package Replyan_AI
 */

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

final class Replyan_Conversation_Analyzer {

	private static $scheduled_sessions = array();

	public function register_hooks(): void {
		add_action( 'replyan_ai_after_response', array( __CLASS__, 'schedule_analysis' ) );
	}

	/**
	 * Schedule the analysis to run on shutdown for a given session.
	 *
	 * @param string $session_id The unique session identifier.
	 */
	public static function schedule_analysis( string $session_id ): void {
		if ( empty( $session_id ) || isset( self::$scheduled_sessions[ $session_id ] ) ) {
			return;
		}

		self::$scheduled_sessions[ $session_id ] = true;

		add_action( 'shutdown', function () use ( $session_id ) {
			// Try to flush buffers and close the connection early
			if ( function_exists( 'fastcgi_finish_request' ) ) {
				fastcgi_finish_request();
			}

			self::run_analysis( $session_id );
		} );
	}

	/**
	 * Main orchestrator for the background analysis.
	 *
	 * @param string $session_id The session identifier.
	 */
	private static function run_analysis( string $session_id ): void {
		try {
			$messages = self::load_conversation( $session_id );
			if ( empty( $messages ) ) {
				return;
			}

			$categories = Replyan_Conversation_Insights::get_enabled_categories();
			if ( empty( $categories ) ) {
				return;
			}

			$prompt_data = self::build_analysis_prompt( $messages, $categories );
			
			$settings = Replyan_AI_Settings::get();
			$response = self::call_ai( $prompt_data['system'], $prompt_data['user'], $settings );

			if ( empty( $response ) ) {
				return;
			}

			$parsed_data = self::parse_and_validate( $response );
			if ( ! $parsed_data ) {
				return;
			}

			Replyan_Conversation_Insights::save_or_update( $session_id, $parsed_data );

		} catch ( Exception $e ) {
			self::log_error( 'Analysis error: ' . $e->getMessage() );
		}
	}

	/**
	 * Load the last 20 messages for the session.
	 *
	 * @param string $session_id The session identifier.
	 * @return array
	 */
	private static function load_conversation( string $session_id ): array {
		global $wpdb;
		$table = $wpdb->prefix . 'replyan_ai_messages';
		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$results = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT role, content FROM {$table} WHERE session_id = %s ORDER BY created_at ASC LIMIT 20",
				$session_id
			),
			ARRAY_A
		);
		// phpcs:enable

		return $results ?: array();
	}

	/**
	 * Construct the system prompt and user message for the analysis.
	 *
	 * @param array $messages   The conversation messages.
	 * @param array $categories The enabled AI categories.
	 * @return array{system: string, user: string}
	 */
	private static function build_analysis_prompt( array $messages, array $categories ): array {
		$cat_descriptions = array();
		foreach ( $categories as $cat ) {
			$desc = ! empty( $cat['description'] ) ? ': ' . $cat['description'] : '';
			$cat_descriptions[] = '- ' . $cat['name'] . $desc;
		}

		$categories_list = implode( "\n", $cat_descriptions );

		$system = "You are an AI conversation analyst. Analyze the following customer support conversation and produce a structured JSON analysis.

You MUST respond with ONLY valid JSON. No markdown, no explanation, no code fences.

Available categories (pick the best match):
{$categories_list}

## Field Definitions

- category: The single best-matching category from the list above.
- purpose: A short sentence describing the customer's primary goal or reason for the conversation. Answer: What does the customer want? Examples: Customer wants product pricing. Customer is requesting a refund.
- sentiment: The overall emotional tone of the conversation. One of: positive, neutral, negative, mixed.
- sentiment_score: Your confidence level (0.00 to 1.00) in the detected sentiment. This is NOT the polarity. The sentiment label already represents polarity. Example: sentiment=negative, sentiment_score=0.94 means you are 94% confident the sentiment is negative.
- emotion: The dominant emotion expressed. One of: satisfied, frustrated, confused, curious, angry, grateful, neutral.
- priority: The urgency level. One of: low, medium, high, urgent.
- summary: A 2-3 sentence overview of what happened during the conversation. Answer: What happened during the conversation? Do NOT repeat the purpose. Focus on the flow of events and outcome.

Required JSON format:
{
  \"category\": \"Sales\",
  \"purpose\": \"Customer wants product pricing.\",
  \"sentiment\": \"positive\",
  \"sentiment_score\": 0.91,
  \"emotion\": \"curious\",
  \"priority\": \"medium\",
  \"summary\": \"Customer asked about pricing for the Premium package and was provided with detailed plan options. They are considering purchasing and requested a follow-up.\"
}

Rules:
- sentiment_score must be a number between 0.00 and 1.00 representing confidence
- category MUST be one of the listed categories above
- If no category fits well, use the closest match
- purpose and summary MUST contain different information. Do not duplicate content between them
- Do not wrap the JSON in markdown code blocks";

		$user_msgs = array();
		foreach ( $messages as $msg ) {
			$role = 'user' === $msg['role'] ? '[User]' : '[Assistant]';
			$user_msgs[] = "{$role}: {$msg['content']}";
		}

		$user = "Analyze this conversation:\n\n" . implode( "\n", $user_msgs );

		return array(
			'system' => $system,
			'user'   => $user,
		);
	}

	/**
	 * Send the request to the AI provider.
	 *
	 * @param string $system_prompt The system prompt.
	 * @param string $user_message  The user message.
	 * @param array  $settings      The plugin settings.
	 * @return string|null The raw response text, or null on error.
	 */
	private static function call_ai( string $system_prompt, string $user_message, array $settings ): ?string {
		// Override settings for analysis
		$settings['system_prompt']    = $system_prompt;
		$settings['temperature']      = '0.1'; // Low creativity
		$settings['max_tokens']       = '500'; // Short output
		$settings['skip_agent_prompt']= true; // Don't overwrite the prompt, but DO fetch the model

		$ajax     = new Replyan_AI_Ajax();
		$response = $ajax->send_to_provider( $settings, $user_message, array() );

		if ( is_wp_error( $response ) ) {
			self::log_error( 'Provider error: ' . $response->get_error_message() );
			return null;
		}

		$status_code = wp_remote_retrieve_response_code( $response );
		$body        = json_decode( wp_remote_retrieve_body( $response ), true );

		if ( 200 > $status_code || 299 < $status_code ) {
			$error_message = $body['error']['message'] ?? 'API Error';
			self::log_error( 'API error: ' . $error_message );
			return null;
		}

		$reply = '';
		if ( isset( $body['choices'][0]['message']['content'] ) ) {
			$reply = $body['choices'][0]['message']['content'];
		} elseif ( isset( $body['content'][0]['text'] ) ) {
			$reply = $body['content'][0]['text'];
		} elseif ( isset( $body['candidates'][0]['content']['parts'][0]['text'] ) ) {
			$reply = $body['candidates'][0]['content']['parts'][0]['text'];
		}

		return $reply;
	}

	/**
	 * Parse and validate the AI's JSON output.
	 *
	 * @param string $raw The raw AI response.
	 * @return array|null Validated associative array, or null if invalid.
	 */
	private static function parse_and_validate( string $raw ): ?array {
		// Strip markdown fences
		$raw = trim( $raw );
		if ( preg_match( '/^```(?:json)?\s*(.*?)\s*```$/is', $raw, $matches ) ) {
			$raw = $matches[1];
		}

		// Extract first JSON object
		if ( preg_match( '/\{.*\}/s', $raw, $matches ) ) {
			$raw = $matches[0];
		}

		$data = json_decode( $raw, true );
		if ( ! is_array( $data ) ) {
			self::log_error( 'Invalid JSON returned: ' . $raw );
			return null;
		}

		$required = array( 'category', 'purpose', 'sentiment', 'sentiment_score', 'emotion', 'priority', 'summary' );
		foreach ( $required as $key ) {
			if ( ! isset( $data[ $key ] ) ) {
				self::log_error( "Missing required key: {$key}" );
				return null;
			}
		}

		// Validate enums and ranges
		$valid_sentiments = array( 'positive', 'neutral', 'negative', 'mixed' );
		if ( ! in_array( $data['sentiment'], $valid_sentiments, true ) ) {
			$data['sentiment'] = 'neutral';
		}

		$valid_priorities = array( 'low', 'medium', 'high', 'urgent' );
		if ( ! in_array( $data['priority'], $valid_priorities, true ) ) {
			$data['priority'] = 'medium';
		}

		$score = (float) $data['sentiment_score'];
		if ( $score < 0.0 ) {
			$data['sentiment_score'] = abs( $score ); // Convert negative to positive (legacy fallback)
		}
		if ( $data['sentiment_score'] > 1.0 ) {
			$data['sentiment_score'] = 1.0;
		}

		return $data;
	}

	/**
	 * Log errors only if debugging is enabled.
	 *
	 * @param string $message The message to log.
	 */
	private static function log_error( string $message ): void {
		// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
		error_log( 'Replyan Conversation Analysis: ' . $message ); // Force log
	}
}
