<?php

namespace Limb_Chatbot\Includes\Integrations\Telegram\Services;

use Limb_Chatbot\Includes\Data_Objects\Chat;
use Limb_Chatbot\Includes\Data_Objects\Chat_Meta;
use Limb_Chatbot\Includes\Data_Objects\Chatbot;
use Limb_Chatbot\Includes\Data_Objects\Chatbot_User;
use Limb_Chatbot\Includes\Data_Objects\Config;
use Limb_Chatbot\Includes\Data_Objects\Message;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;
use Limb_Chatbot\Includes\Integrations\Telegram\Utilities\Messages_Utility;
use Limb_Chatbot\Includes\Interfaces\Live_Agent_Interface;
use Limb_Chatbot\Includes\Services\Data_Object_Collection;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\Live_Agent_Service;
use Limb_Chatbot\Includes\Utilities\Chatbot_Utility;

/**
 * Telegram Live Agent Service
 *
 * Handles Telegram-specific live agent connection logic:
 * - Selects available agents based on active conversations
 * - Sends messages to agents' private Telegram chats
 * - Logs chat history to agents
 * - Manages agent availability and workload distribution
 *
 * @package Limb_Chatbot\Includes\Integrations\Telegram\Services
 * @since 1.0.11
 */
class Telegram_Live_Agent_Service implements Live_Agent_Interface {

	/**
	 * Meta key for storing Telegram agent ID (Chatbot_User ID).
	 */
	const META_TELEGRAM_AGENT_ID = 'telegram_agent_id';

	/**
	 * Meta key for storing Telegram chat ID (private chat with agent).
	 */
	const META_TELEGRAM_CHAT_ID = 'telegram_chat_id';

	/**
	 * Meta key for storing Telegram topic ID (for Supergroups).
	 */
	const META_TELEGRAM_TOPIC_ID = 'telegram_topic_id';

	/**
	 * Maximum number of message blocks to show in history.
	 */
	const MAX_BLOCKS_COUNT = 5;


	/**
	 * Connect chat to Telegram live agents.
	 *
	 * Selects an available agent (one without active conversations) and sends
	 * a message to their private Telegram chat.
	 *
	 * @param Chat            $chat    Chat instance.
	 * @param Chatbot_Utility $utility Chatbot utility.
	 * @param Config          $config  Telegram configuration.
	 * @param array           $args    Connection arguments including reason.
	 *
	 * @return array Connection result.
	 * @throws Exception
	 * @since 1.0.11
	 */
	public function connect( Chat $chat, Chatbot_Utility $utility, Config $config, array $args ): array {
		$params = $config->get_params();

		// Validate bot token
		if ( empty( $params['bot_token'] ) ) {
			throw new Exception(
				Error_Codes::AUTHENTICATION_API_KEY_MISSING,
				__( 'Telegram bot token is missing or empty.', 'limb-chatbot' ),
				null,
				400
			);
		}

		// Get configured agent IDs
		$agent_ids = $utility->agent_ids ?? [];

		if ( empty( $agent_ids ) ) {
			throw new Exception(
				Error_Codes::NOT_FOUND,
				__( 'No Telegram agents configured for this chatbot.', 'limb-chatbot' ),
				null,
				404
			);
		}

		$available_agent = $this->find_available_agent( $agent_ids );

		if ( ! $available_agent ) {
			throw new Exception(
				Error_Codes::NOT_FOUND,
				__( 'No available Telegram agents found. All agents are currently busy.', 'limb-chatbot' ),
				null,
				503 // Service Unavailable
			);
		}

		// Get Telegram chat ID for the agent
		$telegram_user_id = $available_agent->get_meta( 'telegram_user_id' );

		if ( empty( $telegram_user_id ) ) {
			throw new Exception(
				Error_Codes::TECHNICAL_ERROR,
				__( 'Agent Telegram user ID not found.', 'limb-chatbot' ),
				null,
				500
			);
		}

		// Store agent information in chat meta
		$chat->update_meta( self::META_TELEGRAM_AGENT_ID, $available_agent->get_id() );
		$chat->update_meta( self::META_TELEGRAM_CHAT_ID, $telegram_user_id );
		$chat->update_meta( 'telegram_live_agent_started_at', time() );

		try {
			$this->send_initial_notification( $chat, $config, $telegram_user_id, $available_agent, $args );
		} catch ( \Exception $e ) {
			throw $e;
		}

		$this->send_chat_history( $chat, $config, $telegram_user_id );

		$result = [
			'agent_id'       => $available_agent->get_id(),
			'telegram_id'    => $telegram_user_id,
			'integration'    => 'telegram',
		];

		return $result;
	}

	/**
	 * Find an available Telegram agent.
	 *
	 * Searches for an agent who:
	 * 1. Is in the configured agent list
	 * 2. Does NOT have any active live agent conversations
	 *
	 * @param array $agent_ids Array of Chatbot_User IDs.
	 *
	 * @return Chatbot_User|null Available agent or null if none found.
	 * @since 1.0.11
	 */
	private function find_available_agent( array $agent_ids ): ?Chatbot_User {
		if ( empty( $agent_ids ) ) {
			return null;
		}

		// Get all configured agents
		$agents = Chatbot_User::where( [
			'id'   => $agent_ids,
			'type' => Chatbot_User::TYPE_AGENT,
		] )->get();

		if ( empty( $agents ) ) {
			return null;
		}

		// Check each agent for availability
		foreach ( $agents as $agent ) {
			if ( $this->is_agent_available( $agent ) ) {
				return $agent;
			}
		}

		return null;
	}

	/**
	 * Check if an agent is available (has no active conversations).
	 *
	 * Queries all chats to find if this agent has any active live agent sessions.
	 *
	 * @param Chatbot_User $agent Agent to check.
	 *
	 * @return bool True if agent is available, false if busy.
	 * @since 1.0.11
	 */
	private function is_agent_available( Chatbot_User $agent ): bool {
		$agent_id = $agent->get_id();

		// Query all chats where this agent is assigned
		$assigned_metas = Chat_Meta::where( [
			'meta_key'   => self::META_TELEGRAM_AGENT_ID,
			'meta_value' => $agent_id,
		] )->get();

		if ( empty( $assigned_metas ) ) {
			return true;
		}

		$live_agent_service = new Live_Agent_Service();

		foreach ( $assigned_metas as $meta ) {
			$chat = Chat::find( $meta->get_chat_id() );

			// Check if chat is active
			if ( ! $chat instanceof Chat || $chat->get_status() !== Chat::STATUS_ACTIVE ) {
				continue;
			}

			// Check if live agent session is active
			if ( $live_agent_service->is_live_agent_active( $chat ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Send initial notification to the agent's private chat.
	 *
	 * @param Chat         $chat         Chat instance.
	 * @param Config       $config       Telegram configuration.
	 * @param string       $telegram_id  Agent's Telegram user ID.
	 * @param Chatbot_User $agent        Agent Chatbot_User object.
	 * @param array        $args         Connection arguments.
	 *
	 * @return void
	 * @throws Exception
	 * @since 1.0.11
	 */
	private function send_initial_notification(
		Chat $chat,
		Config $config,
		string $telegram_id,
		Chatbot_User $agent,
		array $args
	): void {

		$utility         = new Messages_Utility( $config );
		$message_builder = new Telegram_Bot_Message_Builder();
		$message         = $message_builder->build_initial_message( $chat, $args['reason'] ?? '' );

		try {
			// Send to agent's private chat (no message_thread_id for private chats)
			$utility->send_message( $telegram_id, $message, null, 'HTML' );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ . ' - Failed to send initial notification to Telegram agent' );
			throw new Exception(
				Error_Codes::TECHNICAL_ERROR,
				__( 'Failed to notify Telegram agent.', 'limb-chatbot' ),
				null,
				500
			);
		}
	}

	/**
	 * Send chat history to the agent's private chat.
	 *
	 * @param Chat   $chat        Chat instance.
	 * @param Config $config      Telegram configuration.
	 * @param string $telegram_id Agent's Telegram user ID.
	 *
	 * @return void
	 * @since 1.0.11
	 */
	private function send_chat_history( Chat $chat, Config $config, string $telegram_id ): void {
		$messages = Message::where( [ 'chat_uuid' => $chat->get_uuid() ] )->get();
		if ( empty( $messages ) ) {
			return;
		}

		if ( count( $messages ) > self::MAX_BLOCKS_COUNT ) {
			$messages = array_slice( $messages, - self::MAX_BLOCKS_COUNT, self::MAX_BLOCKS_COUNT, true );
		}

		$utility         = new Messages_Utility( $config );
		$message_builder = new Telegram_Bot_Message_Builder();
		$history_text    = $message_builder->build_history_message( $messages );

		try {
			$utility->send_message( $telegram_id, $history_text, null, 'HTML' );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ . ' - Failed to send chat history to Telegram agent' );
		}
	}


	/**
	 * Send a message to the Telegram agent's private chat.
	 *
	 * @param Chat    $chat    Chat instance.
	 * @param Message $message Message to send.
	 * @param Config  $config  Telegram configuration.
	 *
	 * @return bool True if sent successfully.
	 * @since 1.0.11
	 */
	public function send( Chat $chat, Message $message, Config $config ): bool {
		$telegram_id = $chat->get_meta( self::META_TELEGRAM_CHAT_ID );

		if ( ! $telegram_id ) {
			return false;
		}

		try {
			$utility         = new Messages_Utility( $config );
			$message_builder = new Telegram_Bot_Message_Builder();
			$text            = $message_builder->build_chat_message( $message );

			// Send to agent's private chat (no message_thread_id)
			$utility->send_message( $telegram_id, $text, null, 'HTML' );

			return true;
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ . ' - Failed to send message to Telegram agent' );

			return false;
		}
	}

	/**
	 * Disconnect live agent from a chat.
	 *
	 * Posts a disconnection message to the agent's private Telegram chat.
	 *
	 * @param Chat            $chat    Chat instance.
	 * @param Chatbot_Utility $utility Chatbot utility.
	 * @param Config          $config  Telegram configuration.
	 *
	 * @return bool True if disconnection logged successfully.
	 * @since 1.0.11
	 */
	public function disconnect( Chat $chat, Chatbot_Utility $utility, Config $config ): bool {
		$telegram_id = $chat->get_meta( self::META_TELEGRAM_CHAT_ID );

		if ( empty( $telegram_id ) ) {
			return false;
		}

		try {
			$messages_utility = new Messages_Utility( $config );
			$message_builder  = new Telegram_Bot_Message_Builder();
			$message          = $message_builder->build_session_ended_message( $chat );

			// Send to agent's private chat (no message_thread_id)
			$messages_utility->send_message( $telegram_id, $message, null, 'HTML' );

			return true;
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ . ' - Failed to log disconnection to Telegram agent' );

			return false;
		}
	}

	/**
	 * Fetch new messages from Telegram.
	 *
	 * Syncs messages from the Telegram topic to the local database.
	 * When using event_subscription (webhook) mode, queries database directly
	 * since Telegram disables getUpdates API when a webhook is set.
	 *
	 * @param Chatbot|null $chatbot Chatbot instance.
	 * @param Chat         $chat    Chat instance.
	 * @param mixed        $since   Timestamp to fetch messages since.
	 * @param Config       $config  Telegram configuration.
	 *
	 * @return Data_Object_Collection Collection of messages.
	 * @since 1.0.11
	 */
	public function fetch_new_messages( ?Chatbot $chatbot, Chat $chat, $since, Config $config ) {
		// Check if using event subscription (webhook) mode
		// When webhook is set, Telegram disables getUpdates API, so we must query DB directly
		$fetch_method = $chatbot ? $chatbot->get_parameter( 'telegram_fetch_method' ) : null;

		if ( $fetch_method === 'event_subscription' ) {
			// Query database for messages saved by webhook
			$query_args = [
				'chat_uuid'   => $chat->get_uuid(),
				'role'        => Message::ROLE_ASSISTANT,
				'created_at>' => $since,
			];

			$messages = Message::where( $query_args );
		} else {
			// Use polling to sync messages
			$sync_service = new Telegram_Message_Sync_Service();
			$messages     = $sync_service->sync_messages( $chat, $config );
		}

		return $messages->with( 'agent' );
	}
}

