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

#include "CoreMinimal.h"
#include "Graph/InworldGraph.h"
#include "InworldEdgeAsset.h"
#include "InworldNodeAsset.h"

#include "InworldGraphAsset.generated.h"

class UInworldGraph;
class UEdGraph;

USTRUCT(BlueprintType)
struct FInworldGraphRuntimeDataItem
{
	GENERATED_BODY()

	FString Key{};
	FString Type{};
	UInworldNodeAsset* NodePtr{nullptr};
};

/**
 * @class UInworldGraphAsset
 * @brief Asset representation of an Inworld graph for serialization and storage.
 *
 * This class serves as the asset representation of an Inworld graph, containing
 * all nodes, edges, and configuration data necessary to create and execute
 * runtime graphs. It provides functionality for graph validation, configuration
 * generation, and runtime graph instance creation.
 */
UCLASS(Blueprintable)
class INWORLDRUNTIME_API UInworldGraphAsset : public UObject
{
	GENERATED_BODY()

public:
	/**
	 * @brief Default constructor for the graph asset.
	 *
	 * Initializes the graph asset with default settings for graph creation and management.
	 */
	UInworldGraphAsset();

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

	/** Array of node assets that serve as graph entry points for execution */
	UPROPERTY(Meta = (ToolTip = "Array of node assets that serve as graph entry points for execution"))
	TArray<TObjectPtr<UInworldNodeAsset>> StartNodes;

	/** Array of node assets that serve as graph exit points for execution */
	UPROPERTY(Meta = (ToolTip = "Array of node assets that serve as graph exit points for execution"))
	TArray<TObjectPtr<UInworldNodeAsset>> EndNodes;

	/** Complete collection of all node assets in this graph */
	UPROPERTY(
		BlueprintReadOnly, Category = "InworldGraph", Meta = (ToolTip = "Complete collection of all node assets in this graph"))
	TArray<TObjectPtr<UInworldNodeAsset>> AllNodes;

	/** Complete collection of all edge assets connecting nodes in this graph */
	UPROPERTY(Meta = (ToolTip = "Complete collection of all edge assets connecting nodes in this graph"))
	TArray<TObjectPtr<UInworldEdgeAsset>> AllEdges;

	/** Editor-specific collection of node assets including temporary and non-runtime nodes */
	UPROPERTY(Meta = (ToolTip = "Editor-specific collection of node assets including temporary and non-runtime nodes"))
	TArray<TObjectPtr<UInworldNodeAsset>> AllEditorNodes;

	/** Editor-specific collection of edge assets including temporary and non-runtime edges */
	UPROPERTY(Meta = (ToolTip = "Editor-specific collection of edge assets including temporary and non-runtime edges"))
	TArray<TObjectPtr<UInworldEdgeAsset>> AllEditorEdges;

	/** The compiled runtime graph instance created from this asset */
	UPROPERTY(Meta = (ToolTip = "The compiled runtime graph instance created from this asset"))
	TObjectPtr<UInworldGraph> RuntimeGraph;

	/** Human-readable description of the graph's purpose and functionality */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Inworld",
		meta = (MultiLine = true, ToolTip = "Human-readable description of the graph's purpose and functionality"))
	FString Description{};

	/** Execution mode controlling how nodes are processed during graph execution */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Inworld",
		Meta = (ToolTip = "Execution mode controlling how nodes are processed during graph execution"))
	EInworldGraphExecutionMode ExecutionMode{EInworldGraphExecutionMode::Simultaneous};

	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Inworld",
		meta = (ToolTip =
					"Enables graph to use remote variants from Experiments, allowing you to run A/B experiments without code redeployments."))
	bool EnableRemoteConfig{false};

	/** Map of validation results and error messages for the graph configuration */
	UPROPERTY(VisibleAnywhere, Transient, Category = "Inworld",
		meta = (MultiLine = true, ToolTip = "Map of validation results and error messages for the graph configuration"))
	TMap<FString, FString> Validations{};

	TArray<FInworldGraphRuntimeDataItem> RuntimeDataList;
	/**
	 * @brief Retrieves the graph configuration as a string.
	 * @return The graph configuration string.
	 */
	UFUNCTION(BlueprintCallable, Category = "InworldGraph")
	const FString& GetGraphConfig();

	/**
	 * @brief Creates and returns a compiled graph instance asynchronously.
	 * @param Callback Delegate called when the graph instance is ready.
	 */
	UFUNCTION(BlueprintCallable, Category = "InworldGraph")
	void GetGraphInstance(FOnGraphCompiled Callback);
	void GetGraphInstance(FOnGraphCompiledNative Callback);

	/**
	 * @brief Prints debug information about the graph.
	 * @param ToConsole If true, prints to console output.
	 * @param ToScreen If true, prints to screen overlay.
	 */
	UFUNCTION(BlueprintCallable, Category = "InworldGraph")
	void Print(bool ToConsole = true, bool ToScreen = true);

	/**
	 * @brief Gets the number of levels in the graph hierarchy.
	 * @return The number of levels in the graph.
	 */
	UFUNCTION(BlueprintCallable, Category = "InworldGraph")
	int GetLevelNum() const;

	/**
	 * @brief Retrieves all nodes at a specific level in the graph hierarchy.
	 * @param Level The level to query for nodes.
	 * @param Nodes Output array of nodes at the specified level.
	 */
	UFUNCTION(BlueprintCallable, Category = "InworldGraph")
	void GetNodesByLevel(int Level, TArray<UInworldNodeAsset*>& Nodes);

	/**
	 * @brief Checks if this graph asset is valid for use as a subgraph.
	 * @return True if the graph can be used as a subgraph, false otherwise.
	 */
	UFUNCTION(BlueprintPure, Category = "InworldGraph")
	bool IsValidAsSubgraph();

	/**
	 * @brief Checks if this graph asset is valid for use.
	 * @return True if the graph is valid, false otherwise.
	 */
	UFUNCTION(BlueprintPure, Category = "InworldGraph")
	bool ValidateGraph();

	UFUNCTION(BlueprintCallable, Category = "InworldGraph")
	TArray<FInworldGraphRuntimeDataItem> GetRuntimeDataList();
#if WITH_EDITOR
	/**
	 * @brief Called after the asset has been renamed in the editor.
	 *
	 * This override handles any necessary updates when the graph asset is renamed,
	 * ensuring all references and internal data remain consistent.
	 *
	 * @param OldOuter The previous outer object before renaming
	 * @param OldName The previous name before renaming
	 */
	virtual void PostRename(UObject* OldOuter, const FName OldName) override;

	/**
	 * @brief Called after the asset has been duplicated in the editor.
	 *
	 * This override handles any necessary setup when the graph asset is duplicated,
	 * ensuring the copy has proper initialization and unique identifiers.
	 *
	 * @param bDuplicateForPIE True if duplicating for Play In Editor, false for regular duplication
	 */
	virtual void PostDuplicate(bool bDuplicateForPIE) override;
#endif

#if WITH_EDITORONLY_DATA
	/** The editor graph representation used for visual editing in the graph editor */
	UPROPERTY(Meta = (ToolTip = "The editor graph representation used for visual editing in the graph editor"))
	TObjectPtr<UEdGraph> EdGraph;

	/** Whether this graph is allowed to contain cycles in its node connections */
	UPROPERTY(Meta = (ToolTip = "Whether this graph is allowed to contain cycles in its node connections"))
	bool bCanBeCyclical = true;

	/** Whether this graph supports loop edges that create cycles in execution flow */
	UPROPERTY(Meta = (ToolTip = "Whether this graph supports loop edges that create cycles in execution flow"))
	bool bAllowLoops = true;
#endif

private:
	friend class UInworldRuntimeSubsystem;
#if WITH_EDITOR
	friend class UEdGraph_InworldGraph;
	friend class UInworldGraphFactory;
#endif

	TArray<FInworldGraphRuntimeDataItem> GetRuntimeDataList(UInworldNodeAsset* OverwriteNode);
	/**
	 * @brief Creates a new runtime graph instance from this asset.
	 *
	 * This function instantiates a new runtime graph based on the configuration
	 * stored in this asset, setting up all nodes and edges for execution.
	 *
	 * @param Owner The object that will own the created graph instance
	 * @return Pointer to the newly created runtime graph
	 */
	UInworldGraph* CreateGraph(UObject* Owner);

	/**
	 * @brief Clears the current runtime graph instance and associated data.
	 *
	 * This function releases the current runtime graph and cleans up all
	 * associated runtime resources, preparing for graph recreation or destruction.
	 */
	void ClearGraph();

	/**
	 * @brief Creates and configures the runtime graph with the specified type.
	 *
	 * This function creates a runtime graph instance of the specified class type
	 * and configures it according to this asset's node and edge definitions.
	 *
	 * @param InGraphType The class type to use for creating the runtime graph
	 */
	void CreateRuntimeGraph(UClass* InGraphType);
};
