<?php

namespace Limb_Chatbot\Includes\Services;

use Limb_Chatbot\Includes\Data_Objects\Chat;
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\Token_Usage;
use Limb_Chatbot\Includes\Interfaces\Usage_Service_Interface;

/**
 * Class User_Usage_Service
 *
 * Handles usage limits checking and updating for users.
 *
 * @since 1.0.0
 */
class User_Usage_Service implements Usage_Service_Interface {

	/**
	 * Checks if the given limit has been exceeded for the user.
	 *
	 * @param Limit $limit
	 * @param Message|null $message
	 *
	 * @return Limit|null Returns the limit if exceeded, null otherwise.
	 * @since 1.0.0
	 */
	public function check( Limit $limit, ?Message $message = null ) {
		if ( $this->reset_limits_if_needed( $limit, $message ) ) {
			return null;
		}
		$current_usage = $this->get_current_usage( $limit, $message );
		if ( $current_usage === null || $current_usage === false ) {
			return null;
		}

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

	/**
	 * Resets limits if the reset period has elapsed.
	 *
	 * @param Limit $limit
	 * @param Message $message
	 *
	 * @return bool True if limits were reset, false otherwise.
	 * @since 1.0.0
	 */
	private function reset_limits_if_needed( Limit $limit, Message $message ): bool {
		if ( $limit->get_period() <= 0 ) {
			// This means no period was selected
			return false;
		}
		$chatbot_user         = $limit->get_chatbot_user();
		$current_time         = current_time( 'timestamp' );
		$last_reset_meta_key  = $limit->get_usage_key() . '_last_reset_timestamp';
		$object               = $limit->get_unit() == Limit::UNIT_MESSAGE_PER_CHAT ? Chat::find_by_uuid( $message->get_chat_uuid() ) : $chatbot_user;
		$last_reset_timestamp = $object->get_meta( $last_reset_meta_key );
		if ( empty( $last_reset_timestamp ) || ( $current_time - $last_reset_timestamp ) >= $limit->get_period() ) {
			$object->update_meta( $limit->get_usage_key(), 0 );
			$object->update_meta( $last_reset_meta_key, $current_time );

			return true;
		}

		return false;
	}

	/**
	 * Retrieves current usage value for a limit.
	 *
	 * @param Limit $limit
	 * @param Message|null $message
	 *
	 * @return mixed Current usage value or null.
	 * @since 1.0.0
	 */
	private function get_current_usage( Limit $limit, ?Message $message ) {
		if ( $limit->get_unit() == Limit::UNIT_MESSAGE_PER_CHAT ) {
			$chat = $message->get_chat();

			return $chat->get_meta( $limit->get_usage_key() );
		}

		return $limit->get_chatbot_user()->get_meta( $limit->get_usage_key() );
	}

	/**
	 * Updates usage statistics for the given limit based on token usage.
	 *
	 * @param Limit $limit
	 * @param Token_Usage $usage
	 * @param AI_Model $model
	 * @param Chat|null $chat
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function update_usage( $limit, Token_Usage $usage, AI_Model $model, ?Chat $chat = null ) {
		$key           = $limit->get_usage_key();
		$chatbot_user  = $limit->get_chatbot_user();
		if ( $limit->get_unit() == Limit::UNIT_TOKEN ) {
			$total_tokens = $usage->get_input_tokens() + $usage->get_output_tokens();
			$chatbot_user->update_meta( $key, $chatbot_user->get_meta( $key ) + $total_tokens );
		} elseif ( $limit->get_unit() == Limit::UNIT_COST ) {
			$model_service = ( new AI_Model_Service() );
			$input_cost    = $model_service->get_input_cost( $model, $usage->get_input_tokens() );
			$output_cost   = $model_service->get_output_cost( $model, $usage->get_output_tokens() );
			$total_cost    = $input_cost + $output_cost;
			$chatbot_user->update_meta( $key, $chatbot_user->get_meta( $key ) + $total_cost );
		} elseif ( $limit->get_unit() == Limit::UNIT_MESSAGE_PER_CHAT && ! empty( $chat ) ) {
			$chat->update_meta( $key, $chat->get_meta( $limit->get_usage_key() ) + 1 );
		}
	}
}