<?php

namespace Limb_Chatbot\Includes\Services;

use Limb_Chatbot\Includes\Data_Objects\Embeddable_Message;
use Limb_Chatbot\Includes\Data_Objects\Limit;
use Limb_Chatbot\Includes\Data_Objects\Message;
use Limb_Chatbot\Includes\Data_Objects\AI_Model;
use Limb_Chatbot\Includes\Data_Objects\Chat;
use Limb_Chatbot\Includes\Data_Objects\AI_Model_Meta;
use Limb_Chatbot\Includes\Data_Objects\Token_Usage;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;
use Limb_Chatbot\Includes\Factories\Token_Calculator_Factory;
use Limb_Chatbot\Includes\Interfaces\Usage_Service_Interface;
use Limb_Chatbot\Includes\Repositories\Vector_Repository;

/**
 * Handles usage checking and cost validation for input messages based on limits.
 *
 * @since 1.0.0
 */
class Input_Usage_Service implements Usage_Service_Interface {

	/**
	 * Check if a message exceeds the given input limit (token or cost).
	 *
	 * @param  Limit  $limit  The limit to check against.
	 * @param  Message|null  $message  Optional message object.
	 *
	 * @return Limit|null   Returns the limit if exceeded, or null if within range.
	 * @throws Exception
	 * @since 1.0.0
	 */
	public function check( Limit $limit, Message $message = null ) {
		if ( $message instanceof Embeddable_Message ) {
			return null;
		}
		if ( empty( $message ) || empty( $message->get_content() ) ) {
			return null;
		}

		// Stateless word-count check — bypass token calculator entirely.
		if ( $limit->get_unit() === Limit::UNIT_WORD ) {
			$word_count = $this->count_words_in_message( $message );
			return $word_count > $limit->get_value() ? $limit : null;
		}

		if ( ! ( $tokens = ( new Token_Calculator_Factory() )->make( $message, $limit )->calculate() ) ) {
			return null;
		}

		return $tokens > $limit->get_value() ? $limit : null;
	}


	/**
	 * Count words in a message's text content.
	 *
	 * Supports both plain-string and structured-array content (e.g. multi-part
	 * messages where each part carries a 'text' key).
	 *
	 * @param  Message  $message  The message to inspect.
	 *
	 * @return int  Word count.
	 * @since 1.2.0
	 */
	private function count_words_in_message( Message $message ): int {
		$content = $message->get_content();
		// Content is stored as a JSON-encoded string via prepare_content() — decode it.
		if ( is_string( $content ) ) {
			$decoded = json_decode( $content, true );
			if ( is_array( $decoded ) ) {
				$content = $decoded;
			}
		}
		if ( is_array( $content ) ) {
			$text = '';
			array_walk_recursive(
				$content,
				function ( $val, $key ) use ( &$text ) {
					if ( is_string( $val ) && in_array( $key, [ 'value', 'text' ], true ) ) {
						$text .= ' ' . $val;
					}
				}
			);
		} else {
			$text = (string) $content;
		}

		$trimmed = wp_strip_all_tags( trim( $text ) );
		if ( '' === $trimmed ) {
			return 0;
		}

		return count( preg_split( '/\s+/u', $trimmed, -1, PREG_SPLIT_NO_EMPTY ) );
	}

	/**
	 * Updates usage statistics — for this service, no usage update is performed.
	 *
	 * @since 1.0.0
	 *
	 * @param Limit         $limit The current limit object.
	 * @param Token_Usage   $usage Token usage data.
	 * @param AI_Model      $model AI model used.
	 * @param Chat|null     $chat  Optional chat reference.
	 * @return void
	 */
	public function update_usage( Limit $limit, Token_Usage $usage, AI_Model $model, ?Chat $chat = null ) {
		// There is nothing to update for message
	}
}