<?php

namespace Limb_Chatbot\Includes\Integrations\Telegram\Services;

use Limb_Chatbot\Includes\Data_Objects\Chat;
use Limb_Chatbot\Includes\Data_Objects\Config;
use Limb_Chatbot\Includes\Data_Objects\Message;
use Limb_Chatbot\Includes\Data_Objects\Message_Meta;
use Limb_Chatbot\Includes\Integrations\Telegram\Utilities\Messages_Utility;
use Limb_Chatbot\Includes\Services\Collection;
use Limb_Chatbot\Includes\Services\Data_Object_Collection;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\Live_Agent_Service;

/**
 * Telegram Message Sync Service
 *
 * Handles polling-based message synchronization from Telegram topics.
 * Fetches new messages using getUpdates API.
 *
 * @package Limb_Chatbot\Includes\Integrations\Telegram\Services
 * @since 1.0.11
 */
class Telegram_Message_Sync_Service {

	/**
	 * Sync messages from Telegram private chat to database.
	 *
	 * Works with direct messages between the bot and agents in private chats.
	 *
	 * @param Chat   $chat   Chat object.
	 * @param Config $config Telegram config.
	 *
	 * @return Collection Collection of new messages.
	 * @since 1.0.11
	 */
	public function sync_messages( Chat $chat, Config $config ): Collection {
		$collection = new Data_Object_Collection();

		try {
			// Get Telegram agent's chat ID (private chat with agent)
			$telegram_chat_id = $chat->get_meta( Telegram_Live_Agent_Service::META_TELEGRAM_CHAT_ID );
			$started_at       = (int) $chat->get_meta( 'telegram_live_agent_started_at' );

			if ( empty( $telegram_chat_id ) ) {
				return $collection;
			}

			// Get last synced update ID
			$last_update_id = $this->get_last_update_id( $chat );

			// Fetch updates from Telegram
			$updates = $this->fetch_telegram_updates( $config, $last_update_id );

			// Filter updates for this specific private chat
			$chat_messages = $this->filter_private_chat_messages( $updates, $telegram_chat_id, $started_at );

			// Sort by update_id
			usort( $chat_messages, function ( $a, $b ) {
				return ( $a['update_id'] ?? 0 ) <=> ( $b['update_id'] ?? 0 );
			} );

			// Save new messages to database
			foreach ( $chat_messages as $update ) {
				if ( $last_message = $this->save_telegram_message( $chat, $update, $config ) ) {
					$collection->push_item( $last_message );
				}

				// Update last processed update ID
				$update_id = $update['update_id'] ?? null;
				if ( $update_id ) {
					$chat->update_meta( 'telegram_last_update_id', $update_id );
				}
			}

			return $collection;
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return $collection;
		}
	}

	/**
	 * Get last synced update ID.
	 *
	 * @param Chat $chat Chat object.
	 *
	 * @return int|null Last update ID or null.
	 * @since 1.0.11
	 */
	private function get_last_update_id( Chat $chat ): ?int {
		$last_id = $chat->get_meta( 'telegram_last_update_id' );

		return $last_id ? (int) $last_id : null;
	}

	/**
	 * Fetch updates from Telegram.
	 *
	 * @param Config   $config  Telegram config.
	 * @param int|null $offset  Offset (last update_id + 1).
	 *
	 * @return array Array of update objects.
	 * @since 1.0.11
	 */
	private function fetch_telegram_updates( Config $config, ?int $offset = null ): array {
		try {
			$utility = new Messages_Utility( $config );

			// If we have an offset, add 1 to get only new updates
			$fetch_offset = $offset !== null ? $offset + 1 : null;

			return $utility->get_updates( $fetch_offset, 100, 0 );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return [];
		}
	}

	/**
	 * Filter updates to get only messages from a specific private chat.
	 *
	 * @param array      $updates          Array of update objects.
	 * @param string|int $telegram_chat_id Telegram chat ID (agent's private chat).
	 * @param int        $started_at       Timestamp when live agent session started.
	 *
	 * @return array Filtered updates.
	 * @since 1.0.11
	 */
	private function filter_private_chat_messages( array $updates, $telegram_chat_id, int $started_at = 0 ): array {
		$filtered = [];

		foreach ( $updates as $update ) {
			$message = $update['message'] ?? null;
			if ( ! $message ) {
				continue;
			}

			// Check date to ensure message is from current session
			$date = $message['date'] ?? 0;
			if ( $started_at > 0 && $date < $started_at ) {
				continue;
			}

			// Check if message is from our private chat
			$chat_id = $message['chat']['id'] ?? null;
			if ( (string) $chat_id !== (string) $telegram_chat_id ) {
				continue;
			}

			// Must be a private chat (not group or channel)
			$chat_type = $message['chat']['type'] ?? null;
			if ( $chat_type !== 'private' ) {
				continue;
			}

			// Skip bot messages (our own messages)
			$from = $message['from'] ?? [];
			if ( ! empty( $from['is_bot'] ) ) {
				continue;
			}

			// Must have text content
			if ( empty( $message['text'] ) ) {
				continue;
			}

			$filtered[] = $update;
		}

		return $filtered;
	}

	/**
	 * Save Telegram message to database.
	 *
	 * @param Chat   $chat   Chat object.
	 * @param array  $update Telegram update data.
	 * @param Config $config Telegram config.
	 *
	 * @return Message|null Saved message or null.
	 * @since 1.0.11
	 */
	private function save_telegram_message( Chat $chat, array $update, Config $config ): ?Message {
		$message_data = $update['message'] ?? [];
		$message_id   = $message_data['message_id'] ?? '';
		$text         = $message_data['text'] ?? '';
		$from         = $message_data['from'] ?? [];
		$user_id      = $from['id'] ?? '';

		if ( empty( $message_id ) || empty( $text ) ) {
			return null;
		}

		// Check for closure commands
		if ( $this->is_closure_command( $text ) ) {
			return $this->handle_closure_command( $chat, $user_id, $message_id );
		}

		$existing = Message_Meta::where( [
			'meta_key'   => 'telegram_message_id',
			'meta_value' => $message_id,
		] );

		if ( ! $existing->is_empty() ) {
			foreach ( $existing->get() as $item ) {
				$msg = $item->message();
				if ( $msg instanceof Message && $msg->get_chat_uuid() === $chat->get_uuid() ) {
					return null;
				}
			}
		}

		try {
			$message = Message::make( [
				'chat_uuid' => $chat->get_uuid(),
				'role'      => Message::ROLE_ASSISTANT,
				'content'   => [
					[
						'type' => 'text',
						'text' => [ 'value' => $text ],
					],
				],
			] );
			$message->save();

			// Store Telegram metadata
			$message->update_meta( 'telegram_user_id', $user_id );
			$message->update_meta( 'telegram_message_id', $message_id );
			$message->update_meta( 'source', 'telegram_agent' );

			// Store agent info from Telegram
			$agent_name = trim( ( $from['first_name'] ?? '' ) . ' ' . ( $from['last_name'] ?? '' ) );
			if ( $agent_name ) {
				$message->update_meta( 'telegram_agent_name', $agent_name );
			}
			if ( ! empty( $from['username'] ) ) {
				$message->update_meta( 'telegram_agent_username', $from['username'] );
			}

			// Get or create agent and link message to Chatbot_User (similar to Slack integration)
			$agent_service = new Telegram_Agent_Service();
			$chatbot_user  = $agent_service->get_or_create_agent_from_telegram_user( $from, $config );

			if ( $chatbot_user ) {
				$message->update_meta( 'agent_id', $chatbot_user->get_id() );

				// Get cached avatar from WordPress user
				$wp_user_id = $chatbot_user->get_wp_user_id();

				if ( $wp_user_id ) {
					$avatar_url = get_user_meta( $wp_user_id, 'telegram_avatar_url', true );

					// If no avatar cached, fetch it now
					if ( empty( $avatar_url ) && ! empty( $user_id ) ) {
						$avatar_url = $agent_service->fetch_and_save_avatar( $wp_user_id, (int) $user_id, $config );
					}

					if ( $avatar_url ) {
						$message->update_meta( 'telegram_agent_avatar', $avatar_url );
					}
				}
			}

			return $message;
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return null;
		}
	}

	/**
	 * Get user avatar URL from Telegram.
	 *
	 * Caches avatar URLs in chat meta to reduce API calls.
	 *
	 * @param Chat $chat    Chat object.
	 * @param int  $user_id Telegram user ID.
	 *
	 * @return string|null Avatar URL or null.
	 * @since 1.0.11
	 */
	private function get_user_avatar_url( Chat $chat, int $user_id ): ?string {
		// Check cache first
		$cache_key     = 'telegram_avatar_' . $user_id;
		$cached_avatar = $chat->get_meta( $cache_key );

		if ( $cached_avatar ) {
			return $cached_avatar;
		}

		try {
			// Get config from chat meta
			$config_id = $chat->get_meta( Live_Agent_Service::META_LIVE_AGENT_CONFIG_ID );
			if ( ! $config_id ) {
				return null;
			}

			$config = Config::find( $config_id );
			if ( ! $config ) {
				return null;
			}

			$utility    = new Messages_Utility( $config );
			$avatar_url = $utility->get_user_avatar_url( $user_id );

			if ( $avatar_url ) {
				// Cache the avatar URL
				$chat->update_meta( $cache_key, $avatar_url );
			}

			return $avatar_url;
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ . ' - Failed to fetch avatar' );

			return null;
		}
	}

	/**
	 * Check if message is a closure command.
	 *
	 * @param string $text Message text.
	 *
	 * @return bool True if message is a closure command.
	 * @since 1.0.11
	 */
	private function is_closure_command( string $text ): bool {
		$text             = trim( strtolower( $text ) );
		$closure_commands = [ '#close', '#end', '#disconnect', '#done' ];

		return in_array( $text, $closure_commands, true );
	}

	/**
	 * Handle closure command from agent.
	 *
	 * @param Chat       $chat       Chat object.
	 * @param string|int $user_id    Telegram user ID.
	 * @param string|int $message_id Telegram message ID.
	 *
	 * @return Message|null
	 * @since 1.0.11
	 */
	private function handle_closure_command( Chat $chat, $user_id, $message_id ): ?Message {
		try {
			// Check if this command was already processed
			$processed_commands = $chat->get_meta( 'live_agent_processed_commands' ) ?: [];
			if ( in_array( (string) $message_id, $processed_commands, true ) ) {
				return null;
			}

			$live_agent_service = new Live_Agent_Service();

			// Check if session is active
			if ( ! $live_agent_service->is_live_agent_active( $chat ) ) {
				return null;
			}

			// Mark command as processed
			$processed_commands[] = (string) $message_id;
			$chat->update_meta( 'live_agent_processed_commands', $processed_commands );

			// Store who closed the session
			$chat->update_meta( 'live_agent_closed_by_telegram_id', $user_id );
			$chat->update_meta( 'live_agent_closed_at', current_time( 'mysql' ) );
			$chat->update_meta( 'live_agent_closed_via', 'telegram_polling' );

			// Disconnect and send message to user
			$message = $live_agent_service->disconnect_live_agent( $chat );

			$message->set_content(
				array_merge( $message->get_content(), [
					[ 'type' => Message::CONTENT_TYPE_LIVE_AGENT_DISCONNECTION ],
				] )
			);
			$message->update_meta( 'source', 'telegram_agent' );
			$message->update_meta( 'telegram_user_id', $user_id );

			return $message->save();
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ . ' - Failed to handle closure command' );

			return null;
		}
	}
}

