<?php

namespace Limb_Chatbot\Includes\Services;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Chat;
use Limb_Chatbot\Includes\Data_Objects\Chatbot;
use Limb_Chatbot\Includes\Data_Objects\Message;
use Limb_Chatbot\Includes\Factories\Chatbot_Service_Factory;
use Limb_Chatbot\Includes\Repositories\Chat_Meta_Repository;
use Limb_Chatbot\Includes\Repositories\Chat_Repository;
use Limb_Chatbot\Includes\Repositories\Message_Repository;
use Limb_Chatbot\Includes\Services\Notifications\Notification_Bootstrap;
use Limb_Chatbot\Includes\Services\Notifications\Types\Chat_Created_Notification_Type;


/**
 * Service class to manage Chat operations like creation, running, clearing, and deleting chats.
 *
 * @since 1.0.0
 */
class Chat_Service {

	/**
	 * The chatbot instance associated with this service.
	 *
	 * @var Chatbot
	 * @since 1.0.0
	 */
	public Chatbot $chatbot;

	/**
	 * Repository for managing chat data persistence.
	 *
	 * @var Chat_Repository|null
	 * @since 1.0.0
	 */
	public ?Chat_Repository $chat_repository;

	/**
	 * Repository for managing chat meta data.
	 *
	 * @var Chat_Meta_Repository|null
	 * @since 1.0.0
	 */
	public ?Chat_Meta_Repository $chat_meta_repository;

	/**
	 * Factory to create chatbot-related services.
	 *
	 * @var Chatbot_Service_Factory
	 * @since 1.0.0
	 */
	public Chatbot_Service_Factory $chatbot_service_factory;

	/**
	 * Chat_Service constructor.
	 *
	 * @param  Chatbot  $chatbot  Chatbot instance to associate.
	 * @param  Chat_Repository|null  $chat_repository  Optional chat repository instance.
	 * @param  Chat_Meta_Repository|null  $chat_meta_repository  Optional chat meta repository instance.
	 *
	 * @since 1.0.0
	 */
	public function __construct( Chatbot $chatbot, ?Chat_Repository $chat_repository = null, ?Chat_Meta_Repository $chat_meta_repository = null ) {
		$this->chatbot              = $chatbot;
		$this->chat_repository      = $chat_repository;
		$this->chat_meta_repository = $chat_meta_repository;
		$this->chatbot_service_factory = new Chatbot_Service_Factory();
	}

	/**
	 * Create a new chat.
	 *
	 * @param  array  $data  Chat data for creation.
	 *
	 * @return Chat|null The created chat object or null on failure.
	 * @throws Exception
	 * @since 1.0.0
	 */
	public function create( array $data ): ?Chat {
		$chatbot_user = User_Manager::instance()->get_current_user();
		$chat         = $this->chat_repository->create( $data, $this->chatbot );
		if ( $chat->add_participant( $chatbot_user ) ) {
			$chatbot_user->increment_new_chat_usage();
		}
		$admin_users = User_Manager::instance()->get_admins();
		$current_id  = $chatbot_user->get_id();
		if ( $current_id !== null ) {
			$admin_users = array_values( array_filter( $admin_users, function ( $admin ) use ( $current_id ) {
				return $admin->get_id() !== $current_id;
			} ) );
		}
		if ( ! empty( $admin_users ) ) {
			Notification_Bootstrap::get_service()->queue(
				$chat->get_chatbot(),
				$admin_users,
				Chat_Created_Notification_Type::TYPE_KEY,
				[ 'chat_uuid' => $chat->get_uuid() ]
			);
		}

		return $chat;
	}

	/**
	 * Run chat interaction to generate a reply.
	 *
	 * @param  Chat  $chat  Chat instance to run.
	 * @param  bool  $preview  Whether to run in preview mode (admin only).
	 *
	 * @return Message Generated reply data.
	 * @throws \Limb_Chatbot\Includes\Exceptions\Exception
	 * @since 1.0.0
	 *
	 */
	public function run( Chat $chat, bool $preview = false, $reload = false ) {
		if ( $preview && User_Manager::instance()->current_user->wp_can( 'manage_options' ) ) {
			$this->chatbot->set_preview( true );
		}
		if ( $reload ) {
			$last_message = $chat->get_last_message();
			if ( $last_message->get_role() === Message::ROLE_ASSISTANT ) {
				Message::delete( [ 'id' => $last_message->get_id() ] );
			}
		}

		return $this->chatbot_service_factory->make( $this->chatbot )->generate_reply( $chat );
	}

	/**
	 * Clear all messages from a chat.
	 *
	 * @since 1.0.0
	 *
	 * @param Chat $chat Chat instance whose messages to clear.
	 * @return bool True on success, false otherwise.
	 */
	public function clear( Chat $chat ): bool {
		return ( new Message_Service( $chat, new Message_Repository() ) )->delete( [ 'chat_uuid' => $chat->get_uuid() ] );
	}

	/**
	 * Delete a chat and associated data including meta and messages.
	 *
	 * @since 1.0.0
	 *
	 * @param Chat $chat Chat instance to delete.
	 * @return bool True on successful deletion, false otherwise.
	 */
	public function delete( Chat $chat ): bool {
		$this->chat_meta_repository->delete( [ 'chat_id' => $chat->get_id() ] );
		( new Message_Service( $chat, new Message_Repository() ) )->delete( [ 'chat_uuid' => $chat->get_uuid() ] );

		return Chat::delete( [ 'id' => $chat->get_id() ] );
	}
}