<?php
/**
 * Server-Sent Events Handler for real-time chat functionality.
 *
 * This class provides real-time communication using SSE (Server-Sent Events)
 * which works entirely within WordPress without requiring separate servers.
 *
 * @package Askany
 * @since 1.0.0
 */
namespace Askany;

/**
 * SSE Handler class for real-time chat communication
 *
 * @package Askany
 * @since 1.0.0
 */
class SSEHandler {
	/**
	 * Database instance for managing database operations.
	 *
	 * @var Database
	 */
	private $database;

	/**
	 * Session Manager instance for managing chat sessions.
	 *
	 * @var SessionManager
	 */
	private $session_manager;

	/**
	 * Constructor for SSEHandler.
	 *
	 * @param Database $database Database instance.
	 */
	public function __construct( $database ) {
		$this->database = $database;
		$this->session_manager = new SessionManager( $database );
	}

	/**
	 * Initialize the SSE Handler.
	 *
	 * @return void
	 */
	public function init() {
		// Register SSE endpoint
		add_action( 'wp_ajax_askany_sse_stream', array( $this, 'handle_sse_stream' ) );
		add_action( 'wp_ajax_nopriv_askany_sse_stream', array( $this, 'handle_sse_stream' ) );

		// Register message broadcast endpoint
		add_action( 'wp_ajax_askany_broadcast_message', array( $this, 'broadcast_message' ) );
		add_action( 'wp_ajax_nopriv_askany_broadcast_message', array( $this, 'broadcast_message' ) );

		// Hook into message saving to trigger broadcasts
		add_action( 'askany_message_saved', array( $this, 'on_message_saved' ), 10, 3 );
	}

	/**
	 * Handle SSE stream connection.
	 *
	 * @return void
	 */
	public function handle_sse_stream() {
		// Verify nonce for security
		if ( ! wp_verify_nonce( $_GET['nonce'] ?? '', 'askany_chat_nonce' ) ) {
			wp_die( 'Security check failed', 'Unauthorized', array( 'response' => 401 ) );
		}

		$session_id = sanitize_text_field( $_GET['session_id'] ?? '' );
		$last_message_time = sanitize_text_field( $_GET['last_message_time'] ?? '' );

		if ( empty( $session_id ) ) {
			wp_die( 'Session ID required', 'Bad Request', array( 'response' => 400 ) );
		}

		// Set SSE headers
		$this->set_sse_headers();

		// Send initial connection confirmation
		$this->send_sse_data( array(
			'type' => 'connected',
			'session_id' => $session_id,
			'timestamp' => current_time( 'mysql' )
		) );

		// Keep connection alive and send updates
		$this->stream_updates( $session_id, $last_message_time );
	}

	/**
	 * Set proper SSE headers.
	 *
	 * @return void
	 */
	private function set_sse_headers() {
		// Prevent any output buffering
		if ( ob_get_level() ) {
			ob_end_clean();
		}

		// Set SSE headers
		header( 'Content-Type: text/event-stream' );
		header( 'Cache-Control: no-cache' );
		header( 'Connection: keep-alive' );
		header( 'Access-Control-Allow-Origin: *' );
		header( 'Access-Control-Allow-Headers: Cache-Control' );

		// Disable PHP output buffering
		ini_set( 'output_buffering', 'off' );
		ini_set( 'zlib.output_compression', false );

		// Flush any existing output
		if ( function_exists( 'apache_setenv' ) ) {
			apache_setenv( 'no-gzip', '1' );
		}
	}

	/**
	 * Stream real-time updates to client.
	 *
	 * @param string $session_id Session ID to monitor.
	 * @param string $last_message_time Last message timestamp.
	 * @return void
	 */
	private function stream_updates( $session_id, $last_message_time ) {
		$start_time = time();
		$max_execution_time = 300; // 5 minutes max connection time
		$check_interval = 1; // Check every 1 second

		while ( ( time() - $start_time ) < $max_execution_time ) {
			// Check for connection
			if ( connection_aborted() ) {
				break;
			}

			// Get updates from database
			$updates = $this->database->get_realtime_updates( $session_id, $last_message_time );

			if ( ! empty( $updates['messages'] ) ) {
				// Send new messages
				foreach ( $updates['messages'] as $message ) {
					$this->send_sse_data( array(
						'type' => 'message',
						'data' => $message
					) );

					// Update last message time
					$last_message_time = $message['created_at'];
				}
			}

			// Send typing indicators if any
			if ( ! empty( $updates['typing_indicators'] ) ) {
				$this->send_sse_data( array(
					'type' => 'typing',
					'data' => $updates['typing_indicators']
				) );
			}

			// Send heartbeat every 30 seconds
			if ( ( time() - $start_time ) % 30 === 0 ) {
				$this->send_sse_data( array(
					'type' => 'heartbeat',
					'timestamp' => current_time( 'mysql' )
				) );
			}

			// Sleep before next check
			sleep( $check_interval );

			// Flush output to client
			if ( ob_get_level() ) {
				ob_flush();
			}
			flush();
		}

		// Connection timeout or ended
		$this->send_sse_data( array(
			'type' => 'disconnect',
			'reason' => 'timeout'
		) );
	}

	/**
	 * Send data via SSE format.
	 *
	 * @param array $data Data to send.
	 * @return void
	 */
	private function send_sse_data( $data ) {
		$json_data = wp_json_encode( $data );
		echo "data: {$json_data}\n\n";

		// Flush immediately
		if ( ob_get_level() ) {
			ob_flush();
		}
		flush();
	}

	/**
	 * Broadcast message to all connected clients.
	 *
	 * @return void
	 */
	public function broadcast_message() {
		// Verify nonce
		if ( ! wp_verify_nonce( $_POST['nonce'] ?? '', 'askany_chat_nonce' ) ) {
			wp_send_json_error( 'Security check failed' );
		}

		$session_id = sanitize_text_field( $_POST['session_id'] ?? '' );
		$message_data = $_POST['message_data'] ?? array();

		if ( empty( $session_id ) || empty( $message_data ) ) {
			wp_send_json_error( 'Missing required data' );
		}

		// Store message for SSE pickup
		$this->store_broadcast_message( $session_id, $message_data );

		wp_send_json_success( array( 'broadcasted' => true ) );
	}

	/**
	 * Store broadcast message in transient for SSE pickup.
	 *
	 * @param string $session_id Session ID.
	 * @param array  $message_data Message data.
	 * @return void
	 */
	private function store_broadcast_message( $session_id, $message_data ) {
		$transient_key = "askany_broadcast_{$session_id}";
		$existing_messages = get_transient( $transient_key ) ?: array();

		$existing_messages[] = array(
			'data' => $message_data,
			'timestamp' => current_time( 'mysql' )
		);

		// Store for 5 minutes
		set_transient( $transient_key, $existing_messages, 300 );
	}

	/**
	 * Hook function called when a message is saved.
	 *
	 * @param string $session_id Session ID.
	 * @param string $sender_type Sender type (user/ai/agent).
	 * @param array  $message_data Message data.
	 * @return void
	 */
	public function on_message_saved( $session_id, $sender_type, $message_data ) {
		// Broadcast to SSE clients
		$this->store_broadcast_message( $session_id, array(
			'type' => 'new_message',
			'sender_type' => $sender_type,
			'message' => $message_data,
			'session_id' => $session_id
		) );
	}

	/**
	 * Get SSE endpoint URL.
	 *
	 * @param string $session_id Session ID.
	 * @param string $last_message_time Last message timestamp.
	 * @return string SSE endpoint URL.
	 */
	public static function get_sse_url( $session_id, $last_message_time = '' ) {
		$params = array(
			'action' => 'askany_sse_stream',
			'session_id' => $session_id,
			'nonce' => wp_create_nonce( 'askany_chat_nonce' )
		);

		if ( ! empty( $last_message_time ) ) {
			$params['last_message_time'] = $last_message_time;
		}

		return add_query_arg( $params, admin_url( 'admin-ajax.php' ) );
	}

	/**
	 * Check if SSE is supported by the server.
	 *
	 * @return bool True if SSE is supported.
	 */
	public static function is_sse_supported() {
		// Check if required functions are available
		return function_exists( 'header' ) && 
			   function_exists( 'flush' ) && 
			   ! headers_sent();
	}
}