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

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

#include "InworldEdge.generated.h"

class UInworldGraph;
class UInworldNode;

class FJsonObject;

namespace inworld
{
class EdgeConditionExecutor;
class EdgeContext;
class BaseData;
}	 // namespace inworld

/**
 * @class UInworldEdge
 * @brief Represents a directed connection between nodes in an InworldGraph.
 *
 * An edge defines a relationship between two nodes (Source and Destination) in the graph,
 * controlling flow and transitions between different states or operations. Each edge can be
 * configured as required or not and can optionally form loops in the graph structure.
 */
UCLASS(BlueprintType, DefaultToInstanced, EditInlineNew)
class INWORLDRUNTIME_API UInworldEdge : public UObject
{
	GENERATED_BODY()

public:
	virtual void BeginDestroy() override;

	/** The name of the source node that this edge originates from */
	UPROPERTY(Meta = (ToolTip = "The name of the source node that this edge originates from"))
	FString Source{};

	/** The name of the destination node that this edge connects to */
	UPROPERTY(Meta = (ToolTip = "The name of the destination node that this edge connects to"))
	FString Destination{};

	/** If true, this edge is required to be present in the graph for successful compilation */
	UPROPERTY(
		EditInstanceOnly, Category = "Inworld", meta = (ToolTip = "If true, this edge is required to be present in the graph."))
	bool IsRequired{true};

	/** If true, this edge will form a loop in the graph structure, allowing cyclic execution */
	UPROPERTY(
		EditInstanceOnly, Category = "Inworld", meta = (ToolTip = "If true, this edge will form a loop in the graph structure."))
	bool IsLoopEdge{false};

	/**
	 * @brief Retrieves the list of available node names in the graph. Utility function or editor drop down
	 * @return Array of node names that can be used as Source or Destination
	 */
	UFUNCTION()
	TArray<FString> GetNodeNames() const;

	/**
	 * @brief Native utility function. Creates a new edge instance with the specified properties
	 * @param Outer The outer object that will own this edge
	 * @param Source The name of the source node
	 * @param Destination The name of the destination node
	 * @param IsRequired Whether the edge is required for graph completion
	 * @param IsLoopEdge Whether the edge can create a cycle
	 * @return Newly created edge instance
	 */
	static UInworldEdge* CreateNative(
		UObject* Outer, const FString& Source, const FString& Destination, bool IsRequired, bool IsLoopEdge);

	void InitializeEdge(const FString& InstanceData);
	void DestroyEdge();

private:
	bool bPendingDestroy{false};

	template <class T>
	T* GetGraphRuntimeData() const;

	virtual EInworldRuntimeErrors GetJsonConfig(const TSharedRef<FJsonObject>& GraphJson);

	// So we don't have to expose our internal edge condition reference.
	friend class UInworldGraph;
	friend class UInworldEdgeAsset;
	friend class UInworldGraphAsset;

#if WITH_EDITOR
	friend class UEdGraph_InworldGraph;
#endif
};
