<?php
/**
 * Message Service for DeepSeek AI Provider.
 *
 * Prepares message payloads for sending to DeepSeek's Chat Completion API
 * by formatting mixed content types according to the API requirements.
 *
 * @package Limb_Chatbot\Includes\AI_Providers\Deep_Seek\Services
 * @since  1.0.9
 */

namespace Limb_Chatbot\Includes\AI_Providers\Deep_Seek\Services;

use Limb_Chatbot\Includes\Data_Objects\Message;
use Limb_Chatbot\Includes\Services\Helper;

/**
 * Class Message_Service
 *
 * Transforms Message objects into the format required by DeepSeek's API.
 * DeepSeek API is OpenAI-compatible but text-only (no image/audio support).
 *
 * @package Limb_Chatbot\Includes\AI_Providers\Deep_Seek\Services
 * @since  1.0.9
 */
class Message_Service {

	/**
	 * Transforms a message object into a structured array suitable for DeepSeek API requests.
	 *
	 * DeepSeek API only supports text content, so images and attachments are skipped.
	 * The method extracts text from various content types and formats them appropriately.
	 *
	 * @param Message $message The message to prepare.
	 *
	 * @return array Returns an array with the message `role` and `content` prepared for DeepSeek.
	 * @since  1.0.0
	 */
	public function prepare_message( Message $message ): array {
		$content = Helper::maybe_json_decode( $message->get_content() );

		if ( is_string( $content ) ) {
			return array(
				'role'    => $message->get_role(),
				'content' => $content,
			);
		}

		if ( is_array( $content ) ) {
			$text_parts          = $this->extract_text_from_content( $content );
			$content             = ! empty( $text_parts['text'] ) ? implode( "\n", $text_parts['text'] ) : '';
		}

		return array(
			'role'    => $message->get_role(),
			'content' => $content,
		);
	}

	/**
	 * Extracts text content from a mixed content array.
	 *
	 * Processes various content types and converts them to plain text.
	 * WooCommerce content types are processed via apply_filters so they
	 * can produce model-facing context text. When WC context text is present,
	 * only WC context text is returned (non-WC parts are dropped).
	 *
	 * @param array $content The content array to process.
	 *
	 * @return array Array with 'text' and 'wc' keys containing extracted text strings.
	 * @since  1.0.0
	 */
	private function extract_text_from_content( array $content ): array {
		$text_parts          = array();
		$wc_text_parts       = array();
		$has_wc_context_text = false;

		foreach ( $content as $part ) {
			if ( ! is_array( $part ) || ! isset( $part['type'] ) ) {
				continue;
			}

			$type = $part['type'];

			switch ( $type ) {
				case Message::CONTENT_TYPE_PARAMETER_VALUE:
				case Message::CONTENT_TYPE_PARAMETER:
					$filtered_text = apply_filters(
						"limb_chatbot_message_content_part_{$type}",
						$part['text']['value'] ?? '',
						$part
					);
					$filtered_text = is_string( $filtered_text ) ? trim( $filtered_text ) : '';
					if ( '' !== $filtered_text ) {
						$wc_text_parts[]     = $filtered_text;
						$has_wc_context_text = true;
					}
					break;

				default:
					$extracted = $this->extract_text_from_part( $part );
					if ( ! empty( $extracted ) ) {
						$text_parts[] = $extracted;
					}
					break;
			}
		}

		return array(
			'text' => $has_wc_context_text ? $wc_text_parts : $text_parts,
		);
	}

	/**
	 * Extracts text from a single content part based on its type.
	 *
	 * @param array $part The content part to process.
	 *
	 * @return string|null The extracted text or null if not applicable.
	 * @since  1.0.0
	 */
	private function extract_text_from_part( array $part ): ?string {
		switch ( $part['type'] ) {
			case 'text':
				return $part['text']['value'] ?? null;

			case Message::CONTENT_TYPE_ACTION_SUBMISSION:
				return $part[ Message::CONTENT_TYPE_ACTION_SUBMISSION ]['message'] ?? __( 'Action submitted', 'limb-chatbot' );

			case Message::CONTENT_TYPE_ACTION_CANCELLATION:
				return ! empty( $part[ Message::CONTENT_TYPE_ACTION_CANCELLATION ]['text'] )
					? $part[ Message::CONTENT_TYPE_ACTION_CANCELLATION ]['text']
					: __( 'Cancel submission of this action', 'limb-chatbot' );

			default:
				return null;
		}
	}
}
