<?php

namespace Limb_Chatbot\Includes\Integrations\Slack\Services;

use Limb_Chatbot\Includes\Data_Objects\Chat;
use Limb_Chatbot\Includes\Data_Objects\Chat_Meta;
use Limb_Chatbot\Includes\Data_Objects\Chatbot_User_Meta;
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\Services\Helper;
use Limb_Chatbot\Includes\Services\Live_Agent_Service;

/**
 * Slack Event Handler Service
 *
 * Processes Slack Events API callbacks and synchronizes messages
 * from live agents to the chat system.
 *
 * @package Limb_Chatbot\Includes\Integrations\Slack\Services
 * @since 1.0.0
 */
class Slack_Event_Handler_Service {

	/**
	 * Handle incoming Slack event.
	 *
	 * @param array $event Event data from Slack.
	 *
	 * @return bool True if handled successfully.
	 * @since 1.0.0
	 */
	public function handle_event( array $event, $config ): bool {
		$event_type = $event['type'] ?? '';

		switch ( $event_type ) {
			case 'message':
				return $this->handle_message_event( $event, $config );
			
			default:
				// Ignore other event types
				return true;
		}
	}

	/**
	 * Handle message event from Slack.
	 *
	 * Saves agent messages to the database for display in the chat.
	 * Also detects special closure commands.
	 *
	 * @param array $event Message event data.
	 *
	 * @return bool True if handled successfully.
	 * @since 1.0.0
	 */
	private function handle_message_event( array $event, Config $config): bool {
		// Ignore bot messages and message changes/deletions
		if ( isset( $event['subtype'] ) || isset( $event['bot_id'] ) ) {
			return true;
		}

		$channel_id = $event['channel'] ?? '';
		$text       = $event['text'] ?? '';
		$slack_ts   = $event['ts'] ?? '';
		$user_id    = $event['user'] ?? '';

		if ( empty( $channel_id ) || empty( $text ) || empty( $slack_ts ) ) {
			return false;
		}

		// Find chat by Slack channel ID
		$chat = $this->find_chat_by_channel( $channel_id );
		if ( ! $chat ) {
			return false;
		}

		// Check for special closure commands (alternative to button)
		if ( $this->is_closure_command( $text ) ) {
			$this->handle_closure_command( $chat, $user_id );
			return true;
		}

		// Check if message already exists
		if ( $this->message_exists( $chat, $slack_ts ) ) {
			return true; // Already processed
		}

		// Create and save agent message
		$this->save_agent_message( $chat, $text, $slack_ts, $user_id );

		return true;
	}

	/**
	 * Find chat by Slack channel ID.
	 *
	 * @param string $channel_id Slack channel ID.
	 *
	 * @return Chat|null Chat object or null if not found.
	 * @since 1.0.0
	 */
	private function find_chat_by_channel( string $channel_id ): ?Chat {
		$chat = null;
		$chat_meta = Chat_Meta::where([
			'meta_key'   => Slack_Live_Agent_Service::META_LIVE_AGENT_CHANNEL_ID,
			'meta_value' => $channel_id,
		]);
		if (!$chat_meta->is_empty()){
			$chat = Chat::find($chat_meta->first()->get_chat_id());
		}

		return $chat;
	}

	/**
	 * Check if message already exists.
	 *
	 * @param Chat   $chat     Chat object.
	 * @param string $slack_ts Slack message timestamp.
	 *
	 * @return bool True if message exists.
	 * @since 1.0.0
	 */
	private function message_exists( Chat $chat, string $slack_ts ): bool {
		$existing = Message_Meta::where( [
			'meta_key'   => 'slack_ts',
			'meta_value' => $slack_ts
		] );
		if ( ! $existing->is_empty() ) {
			foreach ( $existing->get() as $item ) {
				$message = $item->message();
				if ( $message instanceof Message && $message->get_chat_uuid() === $chat->get_uuid() ) {
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Save agent message to database.
	 *
	 * @param Chat   $chat     Chat object.
	 * @param string $text     Message text.
	 * @param string $slack_ts Slack timestamp.
	 * @param string $user_id  Slack user ID.
	 *
	 * @return Message|null Saved message or null on failure.
	 * @since 1.0.0
	 */
	private function save_agent_message( Chat $chat, string $text, string $slack_ts, string $user_id ): ?Message {
		try {
			$message = Message::make( [
				'chat_uuid' => $chat->get_uuid(),
				'role'      => Message::ROLE_ASSISTANT,
				'content'   => [
					[
						'type' => 'text',
						'text' => [ 'value' => $text ]
					]
				]
			] );

			$message->save();

			// Store Slack metadata for ordering and deduplication
			$message->update_meta( 'slack_ts', $slack_ts );
			$message->update_meta( 'slack_user_id', $user_id );
			$message->update_meta( 'source', 'slack_agent' );

			$chatbot_user_meta = Chatbot_User_Meta::where( [
				'meta_key' => 'slack_user_id',
				'meta_value' => $user_id,
			] );
			if ( ! $chatbot_user_meta->is_empty() ) {
				$chatbot_user_meta->each( function ( Chatbot_User_Meta $meta ) use ( $message ) {
					$message->update_meta( 'agent_id', $meta->get_chatbot_user_id() );
				} );
			}

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

	/**
	 * Check if message is a closure command.
	 *
	 * Detects special commands that agents can type to close sessions:
	 * - #close
	 * - #end
	 * - #disconnect
	 *
	 * @param string $text Message text.
	 *
	 * @return bool True if message is a closure command.
	 * @since 1.0.0
	 */
	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 $user_id Slack user ID who sent command.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	private function handle_closure_command( Chat $chat, string $user_id ): void {
		try {
			$live_agent_service = new Live_Agent_Service();
			
			// Check if session is active
			if ( ! $live_agent_service->is_live_agent_active( $chat ) ) {
				return; // Already closed
			}

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

			// Disconnect and send message to user
			$message = $live_agent_service->disconnect_live_agent( $chat );
			$message->set_content(
				array_merge( $message->get_content(), array(
					array('type' => Message::CONTENT_TYPE_LIVE_AGENT_DISCONNECTION)
				) )
			);
			$message->update_meta( 'source', 'slack_agent' );
			$message->update_meta( 'slack_user_id', $user_id );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ . ' - Failed to handle closure command' );
		}
	}
}