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

#pragma once

#include "CoreMinimal.h"
#include "Engine/DataTable.h"
#include "Graph/InworldCharacterData.h"
#include "Graph/InworldGraphRuntimeData.h"
#include "InworldCharacterTypes.h"
#include "UObject/Interface.h"

#include "InworldGraphRuntimeData_Goals.generated.h"

/**
 * @class UInworldGraphRuntimeData_Goals
 * @brief Runtime data component for managing character goals and intent-driven behaviors.
 *
 * This runtime data component stores and manages a character's goals, which define
 * intent-based triggers and responses. Goals can be activated through dialogue intents
 * or explicit triggers, enabling dynamic character behaviors based on conversation context.
 * The component also supports dynamic parameter resolution through the IInworldGoalParameterInterface.
 */
UCLASS(BlueprintType, meta = (DisplayName = "Goals Data"))
class INWORLDCHARACTER_API UInworldGraphRuntimeData_Goals : public UInworldGraphRuntimeData
{
	GENERATED_BODY()

public:
	/**
	 * @brief Default constructor for the goals runtime data.
	 */
	UInworldGraphRuntimeData_Goals();

	/**
	 * @brief Retrieves the goals data as a data handle for graph processing.
	 * @return Data handle containing the goals collection.
	 */
	virtual FInworldDataHandle GetData() override;

	/**
	 * @brief Initializes the runtime data by loading goals from the configured data table.
	 */
	virtual void Initialize_Implementation() override;

	/**
	 * @brief Sets the goals data table and loads its contents into the goals map.
	 * @param InGoalTable The data table containing goal definitions.
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld|Goals")
	void Set(UDataTable* InGoalTable);

	/**
	 * Returns reference to an Inworld Goal by name.
	 *
	 * @param GoalName Name of the goal to retrieve.
	 * @param GoalOut Reference to store the retrieved goal in.
	 *
	 * @return True if a goal exists with the given name, false if not.
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld|Goals")
	bool TryGetGoal(FName GoalName, FInworldGoal& GoalOut) const;

	/**
	 * Returns reference to the first Inworld Goal in the goals map which has the specified intent.
	 *
	 * @param IntentName Name of the intent.
	 * @param GoalOut Reference to store the retrieved goal in.
	 *
	 * @return True if a goal exists with the given intent, false if not.
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld|Goals")
	bool TryGetGoalFromIntent(FName IntentName, FInworldGoal& GoalOut) const;

	/**
	 * Removes a goal from the goals map if it is not repeatable.
	 *
	 * @param GoalName The name of the goal to complete.
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld|Goals")
	void CompleteGoal(FName GoalName);

	/**
	 * Removes a goal from the goals map.
	 *
	 * @param GoalName The name of the goal to remove.
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld|Goals")
	void RemoveGoal(FName GoalName);

	/**
	 * Removes all goals from the goals map.
	 *
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld|Goals")
	void ClearAllGoals();

	/**
	 * Adds a goal to the goals map.
	 *
	 * @param Name The name of the goal to add.
	 * @param Goal The goal to add to the goals map.
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld|Goals")
	void AddGoal(FName Name, const FInworldGoal& Goal);

	/**
	 * Returns a pointer to the data table containing the goals of the agent.
	 *
	 * @return A pointer to the data table containing the goals of the agent.
	 */
	UDataTable* GetGoalsDataTable() const
	{
		return GoalTable;
	}

	/**
	 * Register a UObject that implements IInworldGoalParameterInterface as the parameter source. When a goal is "text"
	 * triggered (meaning triggered by FInworldData_Text and NOT FInworldData_Trigger), the source object will be
	 * queried for parameter data.
	 *
	 * @param Source The object to register as the parameter source. It must implement the
	 * IInworldGoalParameterInterface interface to be accepted.
	 */
	UFUNCTION(BlueprintCallable, Category = "Inworld|Goals")
	void SetParameterSourceObject(UObject* Source);

	/**
	 * Returns a pointer to the parameter source object.
	 * @return A pointer to the parameter source object.
	 */
	IInworldGoalParameterInterface* GetParameterSourceObject() const;

	/**
	 * @brief Retrieves all goal names currently stored in the goals map.
	 * @return Array of goal names (keys from the goals map).
	 */
	UFUNCTION(BlueprintPure, Category = "Inworld|Knowledge")
	TArray<FName> GetGoalsMapKeys() const;

protected:
	/**
	 * Data table defining the goals of the character.
	 * Must be a data table with FInworldGoal as the row structure.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Goal Runtime Data",
		Meta = (RequiredAssetDataTags = "RowStructure=/Script/InworldCharacter.InworldGoal",
			ToolTip =
				"Data table defining the goals of the character. Must be a data table with FInworldGoal as the row structure"))
	UDataTable* GoalTable;

	/**
	 * Weak pointer to the parameter source object implementing IInworldGoalParameterInterface.
	 * Used to generate dynamic parameters for text-triggered goals at runtime.
	 */
	UPROPERTY(Transient,
		Meta = (ToolTip =
					"Weak pointer to the parameter source object implementing IInworldGoalParameterInterface. Used to generate dynamic parameters for text-triggered goals at runtime"))
	TWeakObjectPtr<UObject> ParameterSourceObject;

	/** Runtime map of all loaded goals indexed by their unique names */
	TMap<FName, FInworldGoal> GoalsMap;
};
