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

#include "CoreMinimal.h"
#include "Graph/InworldNode.h"
#include "Templates/SubclassOf.h"

#include "InworldNodeAsset.generated.h"

class UInworldGraphAsset;
class UInworldEdgeAsset;

/**
 * @enum EInworldNodeType
 * @brief Enumeration defining the role of a node within the graph structure.
 */
UENUM(BlueprintType)
enum class EInworldNodeType : uint8
{
	/** A regular processing node within the graph */
	NODE,
	/** A starting point node where graph execution begins */
	START_NODE,
	/** An ending point node where graph execution concludes */
	END_NODE,
	/** A node that serves as both start and end point (single-node graphs) */
	START_END_NODE
};

/**
 * @class UInworldNodeAsset
 * @brief Asset representation of a graph node for editor and serialization purposes.
 *
 * This class serves as the asset version of an Inworld graph node, containing
 * all the data necessary to represent, serialize, and manage nodes within the
 * graph editor and runtime system. It maintains relationships with other nodes
 * through edges and manages the creation of runtime node instances.
 */
UCLASS(Blueprintable)
class INWORLDRUNTIME_API UInworldNodeAsset : public UObject
{
	GENERATED_BODY()

public:
	/**
	 * @brief Default constructor for the node asset.
	 *
	 * Initializes the node asset with default settings for graph editor and runtime use.
	 */
	UInworldNodeAsset();

	/**
	 * @brief Destructor for the node asset.
	 *
	 * Performs cleanup of node asset resources and references.
	 */
	~UInworldNodeAsset();

	/** The runtime class type that this node asset will instantiate */
	UPROPERTY(BlueprintReadOnly, Category = "InworldNode",
		Meta = (ToolTip = "The runtime class type that this node asset will instantiate"))
	TSubclassOf<UInworldNode> NodeClass;

	/** The class name of the runtime node type as a string */
	UPROPERTY(Meta = (ToolTip = "The class name of the runtime node type as a string"))
	FString NodeClassName;

	/** The full class path to the runtime node type */
	UPROPERTY(Meta = (ToolTip = "The full class path to the runtime node type"))
	FString NodeClassPath;

	/** Array of parent node assets that connect to this node as input */
	UPROPERTY(Meta = (ToolTip = "Array of parent node assets that connect to this node as input"))
	TArray<TObjectPtr<UInworldNodeAsset>> ParentNodes;

	/** Array of child node assets that this node connects to as output */
	UPROPERTY(Meta = (ToolTip = "Array of child node assets that this node connects to as output"))
	TArray<TObjectPtr<UInworldNodeAsset>> ChildrenNodes;

	/** Map of connected child nodes to their corresponding edge assets */
	UPROPERTY(Meta = (ToolTip = "Map of connected child nodes to their corresponding edge assets"))
	TMap<TObjectPtr<UInworldNodeAsset>, TObjectPtr<UInworldEdgeAsset>> Edges;

	/** The role of this node within the graph (regular, start, end, or both) */
	UPROPERTY(Meta = (ToolTip = "The role of this node within the graph (regular, start, end, or both)"))
	EInworldNodeType NodeAs;

	/** Display title for this node in the graph editor */
	UPROPERTY(Meta = (ToolTip = "Display title for this node in the graph editor"))
	FString NodeTitle;

	/** The actual runtime node instance created from this asset */
	UPROPERTY(VisibleAnywhere, Instanced, BlueprintReadOnly, Category = "Inworld",
		Meta = (ToolTip = "The actual runtime node instance created from this asset"))
	TObjectPtr<UInworldNode> RuntimeNode;

	/** Serialized data of the runtime node for persistence and loading */
	UPROPERTY(Meta = (ToolTip = "Serialized data of the runtime node for persistence and loading"))
	FString SerializedNode;

	/**
	 * @brief Retrieves the edge asset connecting this node to a specified child node.
	 *
	 * This function searches for and returns the edge asset that connects this node
	 * to the specified child node. Returns nullptr if no connection exists.
	 *
	 * @param ChildNode The target child node to find the edge for
	 * @return Pointer to the edge asset connecting to the child node, or nullptr if not found
	 */
	virtual UInworldEdgeAsset* GetEdge(UInworldNodeAsset* ChildNode);

	/**
	 * @brief Checks if this node is a leaf node (has no children).
	 *
	 * A leaf node is a node that has no outgoing connections to other nodes,
	 * typically representing an end point in the graph execution flow.
	 *
	 * @return True if this node has no child nodes, false otherwise
	 */
	bool IsLeafNode() const;

	/**
	 * @brief Checks if this node is a root node (has no parents).
	 *
	 * A root node is a node that has no incoming connections from other nodes,
	 * typically representing a starting point in the graph execution flow.
	 *
	 * @return True if this node has no parent nodes, false otherwise
	 */
	bool IsRootNode() const;

	/**
	 * @brief Retrieves the display title of this node for UI purposes.
	 *
	 * This function returns the node's title as formatted text suitable for
	 * display in the graph editor and other user interfaces.
	 *
	 * @return The formatted node title as FText
	 */
	FText GetNodeTitle();

#if WITH_EDITOR
	/**
	 * @brief Checks if the node's name/title can be edited in the editor.
	 *
	 * This function determines whether the node's title is editable based on
	 * the node's configuration and current state.
	 *
	 * @return True if the node title can be edited, false if it's read-only
	 */
	bool IsNameEditable() const;

	/**
	 * @brief Sets a new title for this node in the editor.
	 *
	 * This function updates the node's display title to the provided text.
	 * The change is reflected in the graph editor interface.
	 *
	 * @param NewTitle The new title text to assign to this node
	 */
	void SetNodeTitle(const FText& NewTitle);

	/**
	 * @brief Validates whether a connection can be created between this node and another.
	 *
	 * This function checks if it's valid to create an edge connection between this
	 * node and the specified target node, considering graph rules and constraints.
	 *
	 * @param Other The target node to potentially connect to
	 * @param ErrorMessage Output parameter containing error description if connection is invalid
	 * @return True if connection is valid, false otherwise
	 */
	bool CanCreateConnection(UInworldNodeAsset* Other, FText& ErrorMessage);

	/**
	 * @brief Validates whether this node can connect TO another node as its child.
	 *
	 * This function checks if this node can have the specified node as a child,
	 * considering output connection limits and graph constraints.
	 *
	 * @param Other The target node to potentially connect to as a child
	 * @param NumberOfChildrenNodes Current number of child connections
	 * @param ErrorMessage Output parameter containing error description if connection is invalid
	 * @return True if the outgoing connection is valid, false otherwise
	 */
	bool CanCreateConnectionTo(UInworldNodeAsset* Other, int32 NumberOfChildrenNodes, FText& ErrorMessage);

	/**
	 * @brief Validates whether this node can accept a connection FROM another node as its parent.
	 *
	 * This function checks if this node can have the specified node as a parent,
	 * considering input connection limits and graph constraints.
	 *
	 * @param Other The source node that wants to connect to this node
	 * @param NumberOfParentNodes Current number of parent connections
	 * @param ErrorMessage Output parameter containing error description if connection is invalid
	 * @return True if the incoming connection is valid, false otherwise
	 */
	bool CanCreateConnectionFrom(UInworldNodeAsset* Other, int32 NumberOfParentNodes, FText& ErrorMessage);

	/**
	 * @brief Creates the runtime node instance from this asset.
	 *
	 * This function instantiates the actual runtime node based on this asset's
	 * configuration, preparing it for graph execution. Used during graph compilation.
	 *
	 * @param Outer The graph asset that will own the created runtime node
	 * @param SubGraphAsset Optional subgraph asset for subgraph node creation
	 */
	void CreateRuntimeNode(UInworldGraphAsset* Outer, UInworldGraphAsset* SubGraphAsset = nullptr);
#endif
};
