<?php
/**
 * AJAX chat handler for Chatbot AI.
 *
 * @package Replyan_AI
 */

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

final class Replyan_AI_Ajax {
	public function register_hooks(): void {
		add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) );
	}

	public function register_rest_routes(): void {
		register_rest_route(
			'replyan/v1',
			'/chat',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'handle_chat_request' ),
				'permission_callback' => '__return_true',
			)
		);

		register_rest_route(
			'replyan/v1',
			'/poll',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'poll_messages' ),
				'permission_callback' => '__return_true',
			)
		);

		register_rest_route(
			'replyan/v1',
			'/upload',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'handle_upload_request' ),
				'permission_callback' => '__return_true',
			)
		);
	}

	public function handle_chat_request( WP_REST_Request $request ) {
		$nonce = $request->get_param( 'nonce' );
		if ( ! wp_verify_nonce( $nonce, Replyan_AI_Settings::NONCE_ACTION ) ) {
			return new WP_Error( 'rest_forbidden', __( 'Invalid nonce.', 'replyan-ai' ), array( 'status' => 403 ) );
		}

		$settings = Replyan_AI_Settings::get();

		if ( '1' !== $settings['enabled'] ) {
			return new WP_Error( 'disabled', __( 'Chatbot is disabled.', 'replyan-ai' ), array( 'status' => 403 ) );
		}

		$message    = sanitize_textarea_field( wp_unslash( $request->get_param( 'message' ) ?? '' ) );
		$session_id = sanitize_text_field( wp_unslash( $request->get_param( 'session_id' ) ?? '' ) );
		$source     = sanitize_text_field( wp_unslash( $request->get_param( 'source' ) ?? 'default' ) );
		$page_title = sanitize_text_field( wp_unslash( $request->get_param( 'page_title' ) ?? '' ) );
		$page_url   = esc_url_raw( wp_unslash( $request->get_param( 'page_url' ) ?? '' ) );
		$history    = $this->sanitize_history( $request->get_param( 'history' ) ?? array() );

		if ( ! $this->verify_session_ownership( $session_id, true ) ) {
			return new WP_Error( 'rest_forbidden', __( 'Unauthorized session.', 'replyan-ai' ), array( 'status' => 403 ) );
		}

		// Generate ownership token for new guest sessions.
		$ownership_token_hash = '';
		if ( ! is_user_logged_in() ) {
			$cookie_name = 'replyan_ot_' . md5( $session_id );
			if ( ! isset( $_COOKIE[ $cookie_name ] ) && ! headers_sent() ) {
				$raw_token            = bin2hex( random_bytes( 32 ) );
				$ownership_token_hash = hash( 'sha256', $raw_token );
				setcookie( $cookie_name, $raw_token, time() + 30 * DAY_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
				// Make cookie available immediately for the current request.
				$_COOKIE[ $cookie_name ] = $raw_token;
			}
		}

		if ( '' === trim( $message ) ) {
			return new WP_Error( 'empty_message', __( 'Message cannot be empty.', 'replyan-ai' ), array( 'status' => 400 ) );
		}

		$agent_id = (int) ( $request->get_param( 'agent_id' ) ?? 0 );

		if ( empty( $settings['skip_agent_fetch'] ) ) {
			global $wpdb;
			$table = $wpdb->prefix . 'replyan_ai_agents';
			$default_agent_id = ! empty( $settings['default_agent_id'] ) ? (int) $settings['default_agent_id'] : 0;
			
			$agent_id_to_fetch = $agent_id ?: $default_agent_id;
			$agent = null;

			if ( $agent_id_to_fetch ) {
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$agent = $wpdb->get_row( $wpdb->prepare( "SELECT id FROM {$table} WHERE id = %d", $agent_id_to_fetch ), ARRAY_A );
			}
			
			if ( ! $agent ) {
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$agent = $wpdb->get_row( "SELECT id FROM {$table} ORDER BY created_at DESC LIMIT 1", ARRAY_A );
			}
			
			if ( $agent ) {
				$agent_id = (int) $agent['id'];
			}
		}

		// Ensure session and save user message immediately
		Replyan_AI_DB::ensure_session( $session_id, $source, $page_title, $page_url, $ownership_token_hash, $agent_id );
		Replyan_AI_DB::save_message( $session_id, 'user', $message );
		Replyan_AI_DB::increment_unread( $session_id, 'admin' );

		// Allow premium agent takeover module to intercept
		$is_intercepted = apply_filters( 'replyan_ai_intercept_message', false, $session_id );

		if ( $is_intercepted ) {
			return rest_ensure_response( array(
				'success' => true,
				'data'    => array( 'reply' => '', 'waiting' => true, 'is_manual' => true ),
			) );
		}

		$skip_ai = rest_sanitize_boolean( $request->get_param( 'skip_ai' ) ?? false );
		if ( $skip_ai ) {
			return rest_ensure_response( array(
				'success' => true,
				'data'    => array( 'reply' => '', 'skip_ai' => true ),
			) );
		}

		$response = $this->send_to_provider( $settings, $message, $history, $session_id, $agent_id );

		if ( is_wp_error( $response ) ) {
			return rest_ensure_response(
				array(
					'success' => false,
					'data'    => array( 'message' => $response->get_error_message() ),
				)
			);
		}

		$context = array(
			'settings' => $settings,
			'message'  => $message,
			'history'  => $history,
		);

		return $this->send_provider_response( $response, $session_id, $context );
	}

	public function send_to_provider( array &$settings, string $message, array $history = array(), string $session_id = '', int $agent_id = 0 ) {
		if ( empty( $settings['skip_agent_fetch'] ) ) {
			global $wpdb;
			$table = $wpdb->prefix . 'replyan_ai_agents';
			$default_agent_id = ! empty( $settings['default_agent_id'] ) ? (int) $settings['default_agent_id'] : 0;
			
			$agent_id_to_fetch = $agent_id ?: $default_agent_id;
			$agent = null;

			if ( $agent_id_to_fetch ) {
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$agent = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE id = %d", $agent_id_to_fetch ), ARRAY_A );
			}
			
			if ( ! $agent ) {
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$agent = $wpdb->get_row( "SELECT * FROM {$table} ORDER BY created_at DESC LIMIT 1", ARRAY_A );
			}

			if ( $agent ) {
				$settings['model'] = $agent['model'];
				if ( empty( $settings['skip_agent_prompt'] ) ) {
					$settings['system_prompt'] = $agent['instruction'];
					$settings['temperature']   = $agent['temperature'];
					$settings['max_tokens']    = $agent['max_tokens'];
				}
			}
		}

		if ( ! isset( $settings['system_prompt'] ) ) {
			$settings['system_prompt'] = '';
		}
		
		$actual_agent_id = isset( $agent ) && $agent ? $agent['id'] : 0;
		
		$provider_settings = $settings;
		$provider_settings['system_prompt'] = apply_filters( 'replyan_ai_system_prompt', $provider_settings['system_prompt'], $session_id, $actual_agent_id, $message );

		// Replyan_AI_Logs::log( 'Prompt: ' . $provider_settings['system_prompt'] );
		$provider      = 'openai';
		$key_field     = 'api_key';
		$enabled_field = 'api_key_enabled';

		if ( strpos( $provider_settings['model'], 'openrouter/' ) === 0 ) {
			$provider      = 'openrouter';
			$key_field     = 'openrouter_api_key';
			$enabled_field = 'openrouter_api_key_enabled';
			$provider_settings['model'] = substr( $provider_settings['model'], 11 );
		} elseif ( strpos( $provider_settings['model'], 'claude' ) === 0 ) {
			$provider      = 'anthropic';
			$key_field     = 'anthropic_api_key';
			$enabled_field = 'anthropic_api_key_enabled';
		} elseif ( strpos( $provider_settings['model'], 'gemini' ) === 0 ) {
			$provider      = 'gemini';
			$key_field     = 'gemini_api_key';
			$enabled_field = 'gemini_api_key_enabled';
		}

		if ( empty( $provider_settings[ $key_field ] ) ) {
			/* translators: %s: AI provider name (e.g., OpenAI, Gemini). */
			return new WP_Error( 'missing_api_key', sprintf( __( 'The %s API key is not configured yet.', 'replyan-ai' ), ucfirst( $provider ) ) );
		}

		if ( '1' !== ( $provider_settings[ $enabled_field ] ?? '1' ) ) {
			/* translators: %s: AI provider name (e.g., OpenAI, Gemini). */
			return new WP_Error( 'disabled_api_key', sprintf( __( 'The %s API key is disabled.', 'replyan-ai' ), ucfirst( $provider ) ) );
		}

		if ( 'openrouter' === $provider ) {
			return Replyan_AI_OpenRouter::send_request( $provider_settings, $message, $history );
		}

		if ( 'anthropic' === $provider ) {
			return Replyan_AI_Anthropic::send_request( $provider_settings, $message, $history );
		}

		if ( 'gemini' === $provider ) {
			return Replyan_AI_Gemini::send_request( $provider_settings, $message, $history );
		}

		return Replyan_AI_OpenAI::send_request( $provider_settings, $message, $history );
	}

	private function send_provider_response( array $response, string $session_id = '', array $context = array() ) {
		$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'] ?? __( 'The AI service returned an error.', 'replyan-ai' );
			return new WP_Error( 'api_error', sanitize_text_field( $error_message ), array( 'status' => $status_code ) );
		}

		$reply = '';
		if ( isset( $body['choices'][0]['message']['content'] ) ) {
			// OpenAI response format.
			$reply = $body['choices'][0]['message']['content'];
		} elseif ( isset( $body['content'][0]['text'] ) ) {
			// Anthropic response format.
			$reply = $body['content'][0]['text'];
		} elseif ( isset( $body['candidates'][0]['content']['parts'][0]['text'] ) ) {
			// Gemini response format.
			$reply = $body['candidates'][0]['content']['parts'][0]['text'];
		}

		if ( '' === trim( $reply ) ) {
			return new WP_Error( 'empty_response', __( 'The AI service returned an empty response.', 'replyan-ai' ), array( 'status' => 500 ) );
		}

		$reply = apply_filters( 'replyan_ai_parse_response', $reply, $session_id, $context );

		// Persist assistant message to database.
		if ( '' !== $session_id ) {
			Replyan_AI_DB::save_message( $session_id, 'assistant', $reply );
			// Note: We don't increment user unread count for AI replies, as they are real-time on screen.

			// Trigger background conversation analysis (premium).
			do_action( 'replyan_ai_after_response', $session_id );
		}

		return rest_ensure_response( array(
			'success' => true,
			'data'    => array(
				'reply'     => wp_kses_post( $reply ),
				'is_manual' => false,
			)
		) );
	}

	/**
	 * Poll for new messages for the frontend widget.
	 */
	public function poll_messages( WP_REST_Request $request ) {
		$nonce = $request->get_param( 'nonce' );
		if ( ! wp_verify_nonce( $nonce, Replyan_AI_Settings::NONCE_ACTION ) ) {
			return new WP_Error( 'rest_forbidden', __( 'Invalid nonce.', 'replyan-ai' ), array( 'status' => 403 ) );
		}

		$session_id = sanitize_text_field( wp_unslash( $request->get_param( 'session_id' ) ?? '' ) );

		if ( ! $session_id ) {
			return new WP_Error( 'missing_session', 'Missing session', array( 'status' => 400 ) );
		}

		if ( ! $this->verify_session_ownership( $session_id, false ) ) {
			return new WP_Error( 'rest_forbidden', __( 'Unauthorized session.', 'replyan-ai' ), array( 'status' => 403 ) );
		}

		// Simple polling MVP: return all messages and unread count.
		$messages = Replyan_AI_DB::get_messages( $session_id );
		$session  = Replyan_AI_DB::get_session_by_id( $session_id );
		
		$unread_count = $session ? (int) $session->unread_count_user : 0;
		$is_manual    = $session && (int) $session->is_manual_mode === 1;

		// Clear user unread count since they are polling with the chat open
		if ( $unread_count > 0 ) {
			Replyan_AI_DB::reset_unread( $session_id, 'user' );
		}

		return rest_ensure_response( array(
			'success' => true,
			'data'    => array( 'messages' => $messages, 'is_manual' => $is_manual )
		) );
	}

	public function handle_upload_request( WP_REST_Request $request ) {
		$nonce = $request->get_param( 'nonce' );
		if ( ! wp_verify_nonce( $nonce, Replyan_AI_Settings::NONCE_ACTION ) ) {
			return new WP_Error( 'rest_forbidden', __( 'Invalid nonce.', 'replyan-ai' ), array( 'status' => 403 ) );
		}

		$session_id = sanitize_text_field( $request->get_param( 'session_id' ) );
		if ( ! $this->verify_session_ownership( $session_id, false ) ) {
			return new WP_Error( 'rest_forbidden', __( 'Unauthorized session.', 'replyan-ai' ), array( 'status' => 403 ) );
		}

		if ( empty( $_FILES['image'] ) ) {
			return new WP_Error( 'no_file', __( 'No file uploaded.', 'replyan-ai' ), array( 'status' => 400 ) );
		}

		$upload_url = Replyan_AI_Upload::handle_image_upload( $_FILES['image'] );

		if ( is_wp_error( $upload_url ) ) {
			return new WP_Error(
				$upload_url->get_error_code(),
				$upload_url->get_error_message(),
				array( 'status' => 400 )
			);
		}

		return rest_ensure_response( array(
			'success' => true,
			'data'    => array( 'url' => $upload_url )
		) );
	}

	private function sanitize_history( $history ): array {
		if ( is_string( $history ) ) {
			$decoded = json_decode( wp_unslash( $history ), true );
			$history = is_array( $decoded ) ? $decoded : array();
		}

		if ( ! is_array( $history ) ) {
			return array();
		}

		$clean = array();
		$total_chars = 0;
		$max_chars = 3000;
		$max_single = 1000;
		$max_messages = 8;
		
		// Start from the newest message (end of array)
		$history = array_reverse( $history );

		foreach ( $history as $item ) {
			if ( count( $clean ) >= $max_messages ) {
				break;
			}

			if ( ! is_array( $item ) ) {
				continue;
			}

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

			$content = sanitize_textarea_field( $item['content'] ?? '' );
			if ( '' === trim( $content ) ) {
				continue;
			}
			
			// Truncate any single message that exceeds 1,000 characters
			if ( mb_strlen( $content ) > $max_single ) {
				$content = mb_substr( $content, 0, $max_single ) . '...';
			}
			
			$content_len = mb_strlen( $content );
			
			// Stop if we exceed the total character limit
			if ( $total_chars + $content_len > $max_chars ) {
				break;
			}

			$total_chars += $content_len;

			// Add to the beginning of the array so chronological order is preserved
			array_unshift( $clean, array(
				'role'    => $role,
				'content' => $content,
			) );
		}

		return $clean;
	}

	private function verify_session_ownership( string $session_id, bool $is_new_allowed = false ): bool {
		if ( empty( $session_id ) ) {
			return false;
		}

		// Logged-in admins can access any session (for the admin panel).
		if ( current_user_can( 'manage_options' ) ) {
			return true;
		}

		$session = Replyan_AI_DB::get_session_by_id( $session_id );
		
		if ( ! $session ) {
			return $is_new_allowed;
		}

		// Logged-in user: verify by user_id.
		if ( (int) $session->user_id > 0 ) {
			return (int) $session->user_id === get_current_user_id();
		}

		// Guest user: verify ownership token from HttpOnly cookie against DB hash.
		$cookie_name = 'replyan_ot_' . md5( $session_id );
		if ( ! isset( $_COOKIE[ $cookie_name ] ) || empty( $session->ownership_token_hash ) ) {
			return false;
		}

		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
		$submitted_hash = hash( 'sha256', (string) $_COOKIE[ $cookie_name ] );
		return hash_equals( $session->ownership_token_hash, $submitted_hash );
	}
}

