<?php

namespace Limb_Chatbot\Includes\AI_Providers\Open_Ai\Endpoints\Chat_Completion;

use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\Stream_Event_Service;

/**
 * Class Stream_Parser
 *
 * Parses streamed chunks received from OpenAI's Chat Completion API.
 *
 * Handles reconstruction of incomplete JSON lines, identification of request metadata,
 * and manages stream state including streamed content output.
 *
 * @since 1.0.0
 */
class Stream_Parser {

	/**
	 * Collected JSON strings that were parsed completely from stream.
	 *
	 * @var array
	 * @since 1.0.0
	 */
	public array $complete_jsons = [];

	/**
	 * Flag to indicate if the first data chunk has started.
	 *
	 * @var bool
	 * @since 1.0.0
	 */
	public bool $data_chunk_started = false;

	/**
	 * Buffer for incomplete (half) JSON lines across multiple stream chunks.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	public string $half_lines = '';

	/**
	 * Parsed error information from the stream, if any.
	 *
	 * @var array|null
	 * @since 1.0.0
	 */
	public ?array $error = null;

	/**
	 * Parses an individual stream chunk.
	 *
	 * Handles:
	 * - JSON line collection (including partial JSON across chunks)
	 * - Extraction of x-request-id for debugging
	 * - Identification of special stream events
	 * - Stream content printing
	 *
	 * @param  string  $chunk  A single raw chunk received from the streaming API.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function parser( $chunk ): void {
		$lines = explode( "\n", $chunk );
		foreach ( $lines as $line ) {
			if ( empty( $line ) ) {
				continue;
			}
			// Handle request ID logging
			if ( str_contains( $line, 'x-request-id:' ) ) {
				$request_id = trim( explode( ':', $line, 2 )[1] ?? '' );
				if ( ! empty( $request_id ) ) {
					Helper::log( 'x-request-id: ' . $request_id );
				}
				continue;
			}
			// Remove known prefixes
			$line = trim( $line );
			if ( str_starts_with( $line, 'event: ' ) || $line === 'data: [DONE]' ) {
				continue;
			}
			if ( str_starts_with( $line, 'data: ' ) ) {
				$line = substr( $line, 6 );
			}
			// Append to buffer
			$this->half_lines .= $line;
			// Try to decode buffer if it looks like JSON
			if ( Helper::is_probable_json( $this->half_lines ) ) {
				$decoded = json_decode( $this->half_lines );
				if ( isset( $decoded->error ) ) {
					$this->error      = (array) $decoded->error;
					$this->half_lines = ''; // Clear after error

					return;
				}
				$this->complete_jsons[] = $decoded;
				$this->print( $decoded );
				// Reset buffer after successful parse
				$this->half_lines = '';
			}
		}
	}

	/**
	 * Prints streamed content (if present) to output buffer and flushes it.
	 *
	 * @param  object  $parsed_data  A single parsed stream chunk.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	/**
	 * Emits a structured SSE delta event to the frontend.
	 *
	 * @param object $parsed_data
	 * @return void
	 */
	public function print( $parsed_data ): void {
		$content = $parsed_data->choices[0]->delta->content ?? '';

		if ( $content === '' ) {
			return;
		}

		// Send the chunk via the central service
		Stream_Event_Service::text( $content );
	}
}