<?php

namespace Limb_Chatbot\Includes\Integrations\Slack\Services;

use Limb_Chatbot\Includes\Data_Objects\Chat;
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\Slack\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\Message_Service;
use Limb_Chatbot\Includes\Utilities\Chatbot_Utility;
use Limb_Chatbot\Includes\Utilities\Slack_Messages_Utility;

/**
 * Slack Live Agent Service
 *
 * Handles Slack-specific live agent connection logic:
 * - Creates or uses existing channels
 * - Adds agents to channels
 * - Logs chat history
 * - Sends messages to Slack
 *
 * @package Limb_Chatbot\Includes\Integrations\Slack\Services
 * @since 1.0.0
 */
class Slack_Live_Agent_Service implements Live_Agent_Interface {
	const META_LIVE_AGENT_CHANNEL_ID = 'slack_channel_id';
	const MAX_BLOCKS_COUNT = 5;

	/**
	 * Connect chat to Slack live agents.
	 *
	 * @param  Chat  $chat  Chat instance.
	 * @param  Chatbot_Utility  $utility  Chatbot utility.
	 * @param  Config  $config  Slack configuration.
	 * @param  array  $args  Connection reason.
	 *
	 * @return array Connection result.
	 * @throws Exception
	 * @since 1.0.0
	 */
	public function connect( Chat $chat, Chatbot_Utility $utility, Config $config, array $args ): array {
		// Get agents
		$agents = $this->get_agents( $utility );
		if ( empty( $agents ) ) {
			throw new Exception( Error_Codes::NOT_FOUND, __( 'No active live agents found.', 'limb-chatbot' ) );
		}
		// Check if new channel should be created
		if ( ! $channel_id = $chat->get_meta( 'slack_channel_id' ) ) {
			$channel_id = $this->create_slack_channel( $utility, $chat, $config, $agents, $args );
		}
		$this->log_chat_history_to_slack( $chat, $config, $channel_id );
		$chat->update_meta( self::META_LIVE_AGENT_CHANNEL_ID, $channel_id );

		return [
			'channel_id'   => $channel_id,
			'agents_count' => count( $agents ),
			'integration'  => 'slack',
		];
	}

	/**
	 * Get all agent chatbot users.
	 *
	 * @return array Array of Chatbot_User objects.
	 * @since 1.0.0
	 */
	private function get_agents( Chatbot_Utility $utility ): array {
		$agents = $utility->agent_ids;

		return Chatbot_User::where( [ 'id' => $agents ] )->get();
	}

	/**
	 * Create a new Slack channel for the live agent session.
	 *
	 * @param  Chat  $chat  Chat instance.
	 * @param  Config  $config  Slack configuration.
	 * @param  array  $agents  Array of agent Chatbot_User objects.
	 * @param  array  $args  Connection arguments.
	 *
	 * @return string Created channel ID.
	 * @throws Exception
	 * @since 1.0.0
	 */
	private function create_slack_channel( Chatbot_Utility $utility, Chat $chat, Config $config, array $agents, array $args ): string {
		$channel_name = $this->generate_channel_name( $utility, $chat );

		// Create global utility
		$global_utility = new Slack_Messages_Utility( $config );
		$utility        = new Messages_Utility( $global_utility );

		// Create channel
		$channel    = $utility->create_channel( $channel_name, false );
		$channel_id = $channel['id'] ?? '';

		if ( empty( $channel_id ) ) {
			throw new Exception(
				Error_Codes::TECHNICAL_ERROR,
				__( 'Failed to create Slack channel.', 'limb-chatbot' ),
				null,
				500
			);
		}
		$chat->update_meta( self::META_LIVE_AGENT_CHANNEL_ID, $channel_id );

		// Invite agents to channel
		$this->invite_agents_to_channel( $utility, $channel_id, $agents );

		// Post initial message
		$this->post_initial_message( $utility, $channel_id, $chat, $args['reason'] ?? '' );

		return $channel_id;
	}

	/**
	 * Generate a unique channel name for the chat.
	 *
	 * @param  Chat  $chat  Chat instance.
	 *
	 * @return string Channel name.
	 * @since 1.0.0
	 */
	private function generate_channel_name( Chatbot_Utility $utility, Chat $chat ): string {
		$base_name = strtolower( $utility->title ) . '-' . substr( $chat->get_uuid(), 0, 8 );

		return sanitize_title( $base_name );
	}

	/**
	 * Invite agents to Slack channel.
	 *
	 * @param  Messages_Utility  $utility  Messages utility instance.
	 * @param  string  $channel_id  Channel ID.
	 * @param  array  $agents  Array of agent users.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	private function invite_agents_to_channel( Messages_Utility $utility, string $channel_id, array $agents ): void {
		$slack_user_ids = [];
		foreach ( $agents as $agent ) {
			$slack_id = $agent->get_meta( 'slack_user_id' );
			if ( $slack_id ) {
				$slack_user_ids[] = $slack_id;
			}
		}

		if ( empty( $slack_user_ids ) ) {
			return;
		}

		$utility->invite_users( $channel_id, $slack_user_ids );
	}

	/**
	 * Post initial message to Slack channel.
	 *
	 * @param  Messages_Utility  $utility  Messages utility instance.
	 * @param  string  $channel_id  Channel ID.
	 * @param  Chat  $chat  Chat instance.
	 * @param  string  $reason  Connection reason.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	private function post_initial_message(
		Messages_Utility $utility,
		string $channel_id,
		Chat $chat,
		string $reason
	): void {
		$participant = $chat->get_user_participant();
		$user_name   = $participant instanceof Chatbot_User ? $participant->name() : 'Guest User';
		$user_email  = $participant instanceof Chatbot_User ? $participant->email() : 'N/A';
		$user_ip     = $participant instanceof Chatbot_User ? $participant->get_ip() : 'N/A';
		$chat_id     = $chat->get_uuid();
		$timestamp   = date( 'F j, Y \a\t g:i A' );

		$blocks = [
			// Header section with icon
			[
				'type' => 'header',
				'text' => [
					'type'  => 'plain_text',
					'text'  => '🔔 New Live Agent Request',
					'emoji' => true
				]
			],
			// Divider
			[
				'type' => 'divider'
			],
			// User information section
			[
				'type'   => 'section',
				'fields' => [
					[
						'type' => 'mrkdwn',
						'text' => "*👤 Customer*\n" . $user_name
					],
					[
						'type' => 'mrkdwn',
						'text' => "*📧 Email*\n" . $user_email
					],
					[
						'type' => 'mrkdwn',
						'text' => "*🌐 IP Address*\n" . $user_ip
					],
					[
						'type' => 'mrkdwn',
						'text' => "*🆔 Chat ID*\n`" . $chat_id . "`"
					]
				]
			],
			// Reason section
			[
				'type' => 'section',
				'text' => [
					'type' => 'mrkdwn',
					'text' => "*💬 Reason*\n" . ( $reason ?: _x( 'Customer requested human assistance',
							'Slack message',
							'limb-chatbot' ) )
				]
			],
			// Divider
			[
				'type' => 'divider'
			],
			// Context/footer
			[
				'type'     => 'context',
				'elements' => [
					[
						'type' => 'mrkdwn',
						'text' => '🕐 ' . $timestamp
					]
				]
			],
			// Command instructions
			[
				'type'     => 'context',
				'elements' => [
					[
						'type' => 'mrkdwn',
						'text' => '💡 _Type `#close`, `#end`, or `#done` to close this session_'
					]
				]
			]
		];

		$utility->post_message(
			$channel_id,
			'🔔 New Live Agent Request',
			$blocks
		);
	}

	/**
	 * Log chat history to Slack channel.
	 *
	 * @param  Chat  $chat  Chat instance.
	 * @param  Config  $config  Slack configuration.
	 * @param  string  $channel_id  Channel ID.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	private function log_chat_history_to_slack( Chat $chat, Config $config, string $channel_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 );
		}

		// Create global utility
		$global_utility = new Slack_Messages_Utility( $config );
		$utility        = new Messages_Utility( $global_utility );

		$blocks = [
			// Header
			[
				'type' => 'header',
				'text' => [
					'type'  => 'plain_text',
					'text'  => '📚 Conversation History',
					'emoji' => true
				]
			],
			[
				'type' => 'divider'
			]
		];

		// Add each message as a section
		foreach ( $messages as $index => $message ) {
			$role        = $message->get_role();
			$role_icon   = $role === 'user' ? '👤' : '🤖';
			$role_label  = $role === 'user' ? 'Customer' : 'AI Assistant';
			$content     = $this->truncate_text( $message->extract_text(), 1000 );
			$timestamp   = $message->get_created_at();
			$time_string = $timestamp ? date( 'g:i A', strtotime( $timestamp ) ) : '';

			// Message block
			$blocks[] = [
				'type' => 'section',
				'text' => [
					'type' => 'mrkdwn',
					'text' => "*{$role_icon} {$role_label}*\n{$content}"
				]
			];

			// Add context with timestamp
			if ( $time_string ) {
				$blocks[] = [
					'type'     => 'context',
					'elements' => [
						[
							'type' => 'mrkdwn',
							'text' => '🕐 ' . $time_string
						]
					]
				];
			}

			// Add divider between messages (except for the last one)
			if ( $index < count( $messages ) - 1 ) {
				$blocks[] = [
					'type' => 'divider'
				];
			}
		}

		$utility->post_message(
			$channel_id,
			'📚 Conversation History',
			$blocks
		);
	}

	/**
	 * Truncate text to specified length.
	 *
	 * @param  string  $text  Text to truncate.
	 * @param  int  $length  Maximum length.
	 *
	 * @return string Truncated text.
	 * @since 1.0.0
	 */
	private function truncate_text( string $text, int $length = 500 ): string {
		if ( strlen( $text ) <= $length ) {
			return $text;
		}

		return substr( $text, 0, $length ) . '...';
	}

	/**
	 * Send a message to Slack channel.
	 *
	 * @param  Chat  $chat  Chat instance.
	 * @param  Message  $message  Message to send.
	 * @param  Config  $config  Slack configuration.
	 *
	 * @return bool True if sent successfully.
	 * @since 1.0.0
	 */
	public function send( Chat $chat, Message $message, Config $config ): bool {
		$channel_id = $chat->get_meta( self::META_LIVE_AGENT_CHANNEL_ID );
		if ( ! $channel_id ) {
			return false;
		}

		try {
			// Create global utility
			$global_utility = new Slack_Messages_Utility( $config );
			$utility        = new Messages_Utility( $global_utility );

			$role       = $message->get_role();
			$role_icon  = $role === 'user' ? '👤' : '🤖';
			$role_label = $role === 'user' ? 'Customer' : 'AI Assistant';
			$content    = $this->truncate_text( $message->extract_text(), 3000 );
			$timestamp  = date( 'g:i A' );

			// Create beautiful blocks for the message
			$blocks = [
				[
					'type' => 'section',
					'text' => [
						'type' => 'mrkdwn',
						'text' => "{$content}"
					]
				],
			];

			$utility->post_message(
				$channel_id,
				"{$role_icon} {$role_label}: {$content}",
				$blocks
			);

			return true;
		} catch ( \Exception $e ) {
			return false;
		}
	}

	/**
	 * Disconnect live agent from a chat.
	 *
	 * Posts a beautiful disconnection message to the Slack channel
	 * informing agents that the user has disconnected.
	 *
	 * @param  Chat  $chat  Chat instance.
	 * @param  Chatbot_Utility  $utility  Chatbot utility.
	 * @param  Config  $config  Slack configuration.
	 *
	 * @return bool True if disconnection logged successfully.
	 * @since 1.0.0
	 */
	public function disconnect( Chat $chat, Chatbot_Utility $utility, Config $config ): bool {
		$channel_id = $chat->get_meta( self::META_LIVE_AGENT_CHANNEL_ID );
		if ( empty( $channel_id ) ) {
			return false;
		}

		try {
			// Create global utility
			$global_utility = new Slack_Messages_Utility( $config );
			$utility_msg    = new Messages_Utility( $global_utility );

			$participant = $chat->get_user_participant();
			$user_name   = $participant instanceof Chatbot_User ? $participant->name() : 'Guest User';
			$timestamp   = date( 'F j, Y \a\t g:i A' );

			// Create beautiful disconnection blocks
			$blocks = [
				// Header section
				[
					'type' => 'header',
					'text' => [
						'type'  => 'plain_text',
						'text'  => '👋 Live Agent Session Ended',
						'emoji' => true
					]
				],
				// Divider
				[
					'type' => 'divider'
				],
				// User info section
				[
					'type'   => 'section',
					'fields' => [
						[
							'type' => 'mrkdwn',
							'text' => "*👤 Customer*\n" . $user_name
						],
						[
							'type' => 'mrkdwn',
							'text' => "*🆔 Chat ID*\n`" . $chat->get_uuid() . "`"
						]
					]
				],
				// Message section
				[
					'type' => 'section',
					'text' => [
						'type' => 'mrkdwn',
						'text' => "✅ *Session Concluded*\n Thank you for your assistance!"
					]
				],
				// Divider
				[
					'type' => 'divider'
				],
				// Context/footer
				[
					'type'     => 'context',
					'elements' => [
						[
							'type' => 'mrkdwn',
							'text' => '🕐 ' . $timestamp
						]
					]
				]
			];

			$utility_msg->post_message(
				$channel_id,
				'👋 Live Agent Session Ended',
				$blocks
			);

			return true;

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

	/**
	 * Fetch new messages
	 *
	 * @param  Chatbot|null  $chatbot
	 * @param  Chat  $chat
	 * @param $since
	 * @param  Config  $config
	 *
	 * @return Data_Object_Collection
	 */
	public function fetch_new_messages( ?Chatbot $chatbot, Chat $chat, $since, Config $config ) {
		if ( Helper::is_local() || $chatbot->get_parameter( 'slack_fetch_method' ) == 'polling' ) {
			$sync_service = new Slack_Message_Sync_Service();
			$messages     = $sync_service->sync_messages( $chat, $config );
		} else {
			$query_args = [
				'chat_uuid'   => $chat->get_uuid(),
				'role'        => Message::ROLE_ASSISTANT,
				'created_at>' => $since
			];

			$messages = Message::where( $query_args );
		}

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

