<?php
/**
 * Chat Completion Response Handler for DeepSeek AI Provider.
 *
 * Handles parsing and message extraction for DeepSeek chat completions.
 * Extends the OpenAI handler since DeepSeek API is OpenAI-compatible.
 *
 * @package Limb_Chatbot\Includes\AI_Providers\Deep_Seek\Endpoints\Chat_Completion\Handlers
 * @since   1.0.0
 */

namespace Limb_Chatbot\Includes\AI_Providers\Deep_Seek\Endpoints\Chat_Completion\Handlers;

use Limb_Chatbot\Includes\AI_Providers\Deep_Seek\Endpoints\Chat_Completion\Tool_Calls_Message;
use Limb_Chatbot\Includes\AI_Providers\Open_Ai\Endpoints\Chat_Completion\Handlers\Chat_Completion_Response_Handler as Handler;
use Limb_Chatbot\Includes\Chatbot_Tools\Actions\Action_Chatbot_Tool;
use Limb_Chatbot\Includes\Chatbot_Tools\Chatbot_Tools;
use Limb_Chatbot\Includes\Data_Objects\Action_Tool_Calls_Message;
use Limb_Chatbot\Includes\Data_Objects\Message;
use Limb_Chatbot\Includes\Data_Objects\Token_Usage;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;

/**
 * Class Chat_Completion_Response_Handler
 *
 * Extends the OpenAI Chat Completion Response Handler for DeepSeek provider.
 * Overrides message creation to use DeepSeek-specific Tool_Calls_Message class.
 *
 * @package Limb_Chatbot\Includes\AI_Providers\Deep_Seek\Endpoints\Chat_Completion\Handlers
 * @since   1.0.0
 */
class Chat_Completion_Response_Handler extends Handler {

	/**
	 * Returns the parsed message object (text or tool-call based).
	 *
	 * Overrides parent to use DeepSeek's Tool_Calls_Message class.
	 *
	 * @return Message|Tool_Calls_Message|null
	 * @throws Exception
	 * @since 1.0.9
	 */
	public function get_message() {
		return $this->deepseek_message_factory();
	}

	/**
	 * Creates message from DeepSeek API response.
	 *
	 * Uses DeepSeek's Tool_Calls_Message for tool call responses.
	 *
	 * @return Message|Tool_Calls_Message|null
	 * @throws Exception If parsing fails.
	 * @since 1.0.9
	 */
	private function deepseek_message_factory() {
		if ( $this->is_stream ) {
			return $this->deepseek_stream_message_factory();
		}

		$message = ! empty( $this->get_body()->choices[0]->message ) ? $this->get_body()->choices[0]->message : null;

		if ( ! empty( $message->tool_calls ) ) {
			// Check if this is an action tool call.
			if ( ! empty( $message->tool_calls[0]->function->name ) && Chatbot_Tools::instance()->get_tool( $message->tool_calls[0]->function->name ) instanceof Action_Chatbot_Tool ) {
				return Action_Tool_Calls_Message::make( [
					'action_name' => $message->tool_calls[0]->function->name,
					'role'        => Message::ROLE_ASSISTANT,
					'parts'       => $message->tool_calls,
				] );
			}

			// Use DeepSeek's Tool_Calls_Message.
			return Tool_Calls_Message::make( [
				'role'       => $message->role,
				'tool_calls' => $message->tool_calls,
			] );
		} elseif ( ! empty( $message->content ) ) {
			return Message::make( [
				'role'    => $message->role,
				'content' => [
					[
						'type' => 'text',
						'text' => [ 'value' => $message->content ],
					],
				],
				'usage'   => Token_Usage::make( [
					'input_tokens'  => $this->get_body()->usage->prompt_tokens ?? 0,
					'output_tokens' => $this->get_body()->usage->completion_tokens ?? 0,
				] ),
			] );
		}

		return null;
	}

	/**
	 * Creates message from streamed DeepSeek API response.
	 *
	 * Merges multiple stream chunks and uses DeepSeek's Tool_Calls_Message.
	 *
	 * @return Message|Tool_Calls_Message
	 * @throws Exception If stream parsing fails.
	 * @since 1.0.9
	 */
	private function deepseek_stream_message_factory() {
		$content    = '';
		$tool_calls = [];

		foreach ( $this->stream_chunks as $stream_chunk ) {
			if ( empty( $stream_chunk->choices ) ) {
				continue;
			}

			$delta = $stream_chunk->choices[0]->delta;

			if ( ! empty( $delta->content ) ) {
				$content .= $delta->content;
			} elseif ( ! empty( $delta->tool_calls[0] ) ) {
				$tool_call    = $delta->tool_calls[0];
				$active_index = $tool_call->index;

				if ( ! isset( $tool_calls[ $active_index ] ) ) {
					$tool_calls[ $active_index ] = $tool_call;
					continue;
				}

				// Collect function-call arguments.
				if ( isset( $tool_calls[ $active_index ]->{$tool_calls[ $active_index ]->type}->arguments ) ) {
					$tool_calls[ $active_index ]->{$tool_calls[ $active_index ]->type}->arguments .= $tool_call->{$tool_calls[ $active_index ]->type}->arguments;
				}
			}
		}

		// Get usage from last chunk.
		$usage = null;
		$chunk_with_usage = $this->stream_chunks[ count( $this->stream_chunks ) - 1 ] ?? null;
		if ( ! empty( $chunk_with_usage->usage ) ) {
			$usage = Token_Usage::make( [
				'input_tokens'  => $chunk_with_usage->usage->prompt_tokens,
				'output_tokens' => $chunk_with_usage->usage->completion_tokens,
			] );
		}

		// Create appropriate message type.
		if ( ! empty( $tool_calls ) ) {
			if ( ! empty( $tool_calls[0]->function->name ) && Chatbot_Tools::instance()->get_tool( $tool_calls[0]->function->name ) instanceof Action_Chatbot_Tool ) {
				return Action_Tool_Calls_Message::make( [
					'action_name' => $tool_calls[0]->function->name,
					'role'        => Message::ROLE_ASSISTANT,
					'parts'       => $tool_calls,
				] );
			}
			if ( empty( $message ) ) {
				// Use DeepSeek's Tool_Calls_Message.
				$message = Tool_Calls_Message::make( [
					'role'       => Message::ROLE_ASSISTANT,
					'tool_calls' => $tool_calls,
					'usage'      => $usage,
				] );
			}
		} elseif ( ! empty( $content ) ) {
			$message = Message::make( [
				'role'     => Message::ROLE_ASSISTANT,
				'content'  => [ [ 'type' => 'text', 'text' => [ 'value' => $content ] ] ],
				'usage'    => $usage,
				'streamed' => true,
			] );
		}
		if ( empty( $message ) ) {
			throw new Exception( Error_Codes::TECHNICAL_ERROR, __( 'Error while parsing the streamed message', 'limb-chatbot' ) );
		}

		return $message;
	}
}