/**
 * Copyright 2022-2025 Theai, Inc. dba Inworld AI
 */

#pragma once

#include "Conversation/InworldConversationGroup.h"
#include "CoreMinimal.h"

#include "InworldConversationGroupComponent.generated.h"

class USpeakerRuleDirector;

/** Fired after a batch of characters has been successfully added to this group via AddCharacters. */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCharactersAdded, const TArray<UInworldCharacterComponent*>&, AddedCharacters);

/** Fired after a batch of characters has been successfully removed from this group via RemoveCharacters. */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCharactersRemoved, const TArray<UInworldCharacterComponent*>&, RemovedCharacters);

/**
 * @class UInworldConversationGroupComponent
 * @brief Component wrapper for managing multi-character conversation groups.
 *
 * This component provides an actor component interface to UInworldConversationGroup,
 * allowing easy integration of multi-character conversations into actors. It manages
 * participant addition/removal, speaker selection, and conversation state while
 * implementing the conversation target interface.
 *
 * Key Features:
 * - Multi-character conversation management
 * - Speaker selection graph integration
 * - Participant management (add/remove characters)
 * - Event history tracking
 * - Blueprint accessible conversation control
 */
UCLASS(ClassGroup = (Inworld), BlueprintType, Blueprintable, Meta = (BlueprintSpawnableComponent))
class INWORLDCHARACTER_API UInworldConversationGroupComponent : public UInworldConversationTargetComponent
{
	GENERATED_BODY()

public:
	/**
	 * Adds a character to the conversation group.
	 *
	 * The character will receive messages sent to the group and can participate
	 * in speaker selection for responses.
	 *
	 * @param CharacterComponent The character component to add to the conversation
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld")
	virtual void AddCharacter(UInworldCharacterComponent* CharacterComponent);

	/**
	 * Removes a character from the conversation group.
	 *
	 * @param CharacterId The unique ID of the character to remove
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld")
	virtual void RemoveCharacter(FString CharacterId);

	/**
	 * Adds multiple characters to the conversation group in a single batch.
	 * Will fire OnBatchCharactersAdded delegate when complete.
	 *
	 * @param CharactersToAdd The array of characters to add to this group.
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld")
	void AddCharacters(const TArray<UInworldCharacterComponent*>& CharactersToAdd);

	/**
	 * Removes multiple characters from the conversation group in a single batch.
	 * Will fire OnBatchCharactersRemoved delegate when complete.
	 *
	 * @param CharactersToRemove The array of characters to remove from this group.
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld")
	void RemoveCharacters(const TArray<UInworldCharacterComponent*>& CharactersToRemove);

	/**
	 * Manually invokes the next character response in the conversation.
	 *
	 * @param CharacterId Optional specific character ID to respond. If empty, uses speaker selection.
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld")
	virtual void InvokeNextResponse(const FString& CharacterId = "");

	/**
	 * Gets all character participants in the conversation.
	 *
	 * @return Array of character components participating in the conversation
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld")
	TArray<UInworldCharacterComponent*> GetParticipants();

	/**
	 * Gets the current player target for the conversation.
	 *
	 * @return The player component currently interacting with this conversation
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld")
	UInworldBasePlayerComponent* GetPlayerTarget();

	/**
	 * Gets the current state of the conversation.
	 *
	 * @return The conversation state data
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld")
	FInworldConversationState GetConversationState();

	/**
	 * Clears the group's event history data, resetting all stored interactions.
	 *
	 * This method ensures that the event history runtime data is wiped clean,
	 * removing all previously recorded conversation or event entries. It logs an
	 * error message if the event history data is null or invalid.
	 *
	 * @param IncludeCharacters (optional) If true (false by default), clear the history of all characters in the conversation.
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld|Event History")
	void ClearHistory(bool IncludeCharacters = false) const;

protected:
	virtual void BeginPlay() override;

	/**
	 * Cancels the current message, and interrupts all characters in the group.
	 */
	virtual void Interrupt_Implementation() override;

	/**
	 * Sends a message to the conversation group.
	 *
	 * The message contains data that should be processed by the
	 * conversation target's AI system to generate appropriate text or speech responses.
	 *
	 * @param Message A data handle holding the message
	 * @return An FString identifier for the sent message
	 */
	virtual FString SendMessage_Implementation(const FInworldDataHandle& Message) override;

	/**
	 * Sends a message to this conversation group with player information.
	 *
	 * The message contains data that should be processed by the
	 * conversation target's AI system to generate appropriate text or speech responses.
	 *
	 * @param Message A data handle holding the message
	 * @param PlayerProfile Runtime data that holds information about the player that sent the message
	 * @return An FString identifier for the sent message
	 */
	virtual FString SendPlayerMessage_Implementation(
		const FInworldDataHandle& Message, const FInworldPlayerProfile& PlayerProfile) override;

	/**
	 * Sends a message to the conversation group with runtime data.
	 *
	 * The message contains data that should be processed by the
	 * conversation target's AI system to generate appropriate text or speech responses.
	 *
	 * @param Message A data handle holding the message
	 * @param RuntimeData The runtime data
	 * @return An FString identifier for the sent message
	 */
	virtual FString SendDataMessage_Implementation(
		const FInworldDataHandle& Message, const TMap<FName, FInworldDataHandle>& RuntimeData) override;

public:
	/** Event fired when a character is added to the conversation */
	UPROPERTY(BlueprintAssignable, Category = "Inworld")
	FOnInworldCharacterAddedToConversation OnCharacterAdded;
	FOnInworldCharacterAddedToConversationNative OnCharacterAddedNative;

	/** Event fired when a character is removed from the conversation */
	UPROPERTY(BlueprintAssignable, Category = "Inworld")
	FOnInworldCharacterRemovedFromConversation OnCharacterRemoved;
	FOnInworldCharacterRemovedFromConversationNative OnCharacterRemovedNative;

	/** Event fired after a batch of characters has been successfully added to this group */
	UPROPERTY(BlueprintAssignable, Category = "Inworld")
	FOnCharactersAdded OnCharactersAdded;

	/** Event fired after a batch of characters is successfully removed from this group */
	UPROPERTY(BlueprintAssignable, Category = "Inworld")
	FOnCharactersRemoved OnCharactersRemoved;

protected:
	/** Internal conversation group object managing the actual multi-character conversation */
	UPROPERTY(Meta = (ToolTip = "Internal conversation group object managing the actual multi-character conversation"))
	TObjectPtr<UInworldConversationGroup> ConversationGroup;

	/** Inworld graph for intelligent speaker selection in multi-character conversations */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Graph",
		Meta = (ToolTip = "Inworld graph for intelligent speaker selection in multi-character conversations"))
	TObjectPtr<UInworldGraphAsset> SpeakerSelectionGraphAsset;

	/**
	 * Optional Director object to manage rule-based, deterministic speaker selection.
	 * If assigned, this will be checked *before* the SpeakerSelectionGraph.
	 */
	UPROPERTY(EditAnywhere, Instanced, BlueprintReadOnly, Category = "Inworld")
	TObjectPtr<USpeakerRuleDirector> RuleDirector;

	/** Internal callback function. Bound to a character's OnConversationTargetReadyNative delegate. */
	UFUNCTION()
	void OnCharacterFromBatchReady(UInworldConversationTargetComponent* ReadyComponent);

	/** Stores the original list of characters from the AddCharacters call, to be broadcast in the delegate when all are ready.
	 */
	UPROPERTY(Transient)
	TArray<TObjectPtr<UInworldCharacterComponent>> CurrentBatchBeingAdded;

	/** Counter for tracking how many characters from the current batch are still pending (not ready). */
	UPROPERTY(Transient)
	int32 PendingReadyCharacterCount;
};
