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

#include "CoreMinimal.h"
#include "Dom/JsonObject.h"
#include "InworldGraphRuntimeData.h"
#include "InworldRuntimeErrors.h"

#include "InworldNode.generated.h"

class UInworldGraph;
struct FInworldDataHandle;

class FJsonObject;

namespace inworld
{
class Node;
namespace graph
{
struct CompileContext;
}
}	 // namespace inworld

/**
 * @class UInworldNode
 * @brief Abstract base class for all workflow nodes.
 *
 * Node provides the core interface for workflow processing units. Each node
 * can process a set of inputs and produce an output.
 */
UCLASS(Abstract, BlueprintType, DefaultToInstanced, EditInlineNew)
class INWORLDRUNTIME_API UInworldNode : public UObject
{
	GENERATED_BODY()

public:
	virtual void BeginDestroy() override;

	/**
	 * @brief Retrieves the unique name identifier of this node.
	 * @return The node's name as a string
	 */
	UFUNCTION(BlueprintPure, Category = "Inworld")
	FString GetNodeName() const
	{
		return NodeName;
	}

	/**
	 * @brief Sets the unique name identifier for this node.
	 * @param InNodeName The new name to assign to this node
	 */
	void SetNodeName(const FString& InNodeName)
	{
		NodeName = InNodeName;
	}

	/**
	 * @brief Native version of GetNodeName that returns a reference.
	 * @return Reference to the node's name string
	 */
	const FString& GetNodeNameNative() const
	{
		return NodeName;
	}

	/**
	 * @brief Template function to create a new node instance of the specified type.
	 *
	 * This static template function creates a new node of the specified type T with
	 * the given outer object and node name. The type T must be derived from UInworldNode.
	 *
	 * @param Outer The outer object that will own the created node
	 * @param NodeName The unique name to assign to the new node
	 * @return Pointer to the newly created node of type T
	 */
	template <typename T>
	static T* CreateNative(UObject* Outer, const FString& NodeName)
	{
		static_assert(TIsDerivedFrom<T, UInworldNode>::Value, "T must be a child class of UInworldNode");
		T* NewNode = NewObject<T>(Outer, FName(NodeName));
		NewNode->NodeName = NodeName;
		return NewNode;
	}

	/**
	 * @brief Checks if this node is configured as a getter node.
	 *
	 * Getter nodes are special nodes that retrieve data from the runtime context
	 * rather than processing input data through traditional node logic.
	 *
	 * @return True if this node is a getter node, false otherwise
	 */
	UFUNCTION(BlueprintPure, Category = "Inworld")
	bool IsGetter() const
	{
		return bIsGetter;
	}

	void InitializeNode(const FString& InstanceData);
	void DestroyNode();

	virtual const FString& GetDescription();

	/**
	 * @brief Retrieves the array of Process functions available for this node.
	 */
	TArray<UFunction*> GetProcessFunctions() const;

protected:
	/** The unique name identifier for this node within the graph */
	UPROPERTY(EditDefaultsOnly, Category = "Inworld",
		meta = (EditCondition = "!IsInGraphEditor", EditConditionHides = true,
			ToolTip = "The unique name identifier for this node within the graph"))
	FString NodeName{};

	/** Description of the node functionality and purpose */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Inworld",
		meta = (EditCondition = "!IsInGraphEditor", EditConditionHides = true, MultiLine = true,
			ToolTip = "Description of the node functionality and purpose"))
	FString NodeDescription{};

	/** Flag indicating whether this node acts as a getter node for runtime data */
	UPROPERTY(EditDefaultsOnly, Category = "Inworld",
		meta = (EditCondition = "!IsInGraphEditor", EditConditionHides = true,
			ToolTip = "Flag indicating whether this node acts as a getter node for runtime data"))
	bool bIsGetter{false};

private:
	bool bPendingDestroy{false};

	UPROPERTY(VisibleAnywhere, Category = "Inworld",
		meta = (EditCondition = "false", EditConditionHides = true,
			ToolTip = "Whether this node is being edited in the graph editor"))
	bool IsInGraphEditor{false};

	virtual EInworldRuntimeErrors GetJsonConfig(const TSharedRef<FJsonObject>& GraphJson)
	{
		return EInworldRuntimeErrors::NOT_IMPLEMENTED;
	}

protected:
	// So we don't have to expose our internal node reference.
	friend class UInworldGraph;
	friend class UInworldNodeAsset;
	friend class UInworldGraphAsset;
#if WITH_EDITOR
	friend class UEdGraph_InworldGraph;
#endif
};
