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

#pragma once

#include "CoreMinimal.h"
#include "Graph/InworldData.h"
#include "Graph/InworldEdge.h"
#include "Graph/InworldProcessContext.h"

#include "InworldEdge_WithCondition.generated.h"

namespace inworld
{
class BaseData;

/**
 * @brief Executes condition evaluation for a conditional edge within the native runtime system.
 *
 * This function serves as the bridge between the Unreal Engine graph system and the native
 * Inworld runtime for condition evaluation. It handles the execution of custom condition
 * logic while managing thread safety and data conversion requirements.
 *
 * @param Edge The conditional edge instance to evaluate
 * @param ProcessContext The process context containing runtime execution state
 * @param Input The input data to evaluate for the condition
 * @return True if the condition is met and data should flow through the edge
 */
bool ExecuteMeetsCondition(
	class UInworldEdge_WithCondition* Edge, class UInworldProcessContext* ProcessContext, FInworldDataHandle Input);
}	 // namespace inworld

/**
 * @class UInworldEdge_WithCondition
 * @brief Base class for creating custom edge conditions in the InworldGraph
 *
 * This abstract class allows developers to implement custom conditional logic for graph edges
 * through Blueprint or C++ implementations. Custom edges can evaluate input data to determine
 * if data flow should be allowed through the edge.
 *
 * Key features:
 * - Blueprintable: Can be extended in Blueprints to create custom edge conditions
 * - Configurable execution thread: Condition evaluation can occur in game thread or background thread
 * - Custom condition logic: Evaluates FInworldDataHandle inputs for flexible condition checking
 *
 * To implement a custom edge:
 * 1. Create a Blueprint or C++ class inheriting from UInworldEdge_Custom
 * 2. Implement the MeetsCondition function to define custom condition logic
 */
UCLASS(Blueprintable, BlueprintType, Abstract)
class INWORLDRUNTIME_API UInworldEdge_WithCondition : public UInworldEdge
{
	GENERATED_BODY()
public:
	/**
	 * @brief Display title for this edge in the graph editor.
	 *
	 * This property provides a human-readable name for the edge that appears
	 * in the graph editor interface. It helps developers identify the edge's
	 * purpose and condition type visually in complex graph layouts.
	 */
	UPROPERTY(EditDefaultsOnly, Category = "Inworld", Meta = (ToolTip = "Display title for this edge in the graph editor"))
	FString EdgeTitle{};

	/**
	 * @brief Determines if the condition evaluation should execute on the game thread.
	 *
	 * This function returns the current thread execution preference for the edge's
	 * condition evaluation logic, allowing the runtime system to schedule execution appropriately.
	 *
	 * @return True if condition evaluation should occur on the game thread, false for background execution
	 */
	bool ShouldExecuteMeetsConditionInGameThread() const
	{
		return bExecuteMeetsConditionInGameThread;
	}

	/**
	 * @brief Determines if the condition result should be negated.
	 *
	 * This function returns whether the condition evaluation result should be inverted.
	 * When true, the edge allows data flow when the condition is NOT met, effectively
	 * creating an inverted logic flow control.
	 *
	 * @return True if the condition result should be negated, false for normal evaluation
	 */
	bool ShouldNegateCondition() const
	{
		return bNegateCondition;
	}

protected:
	/**
	 * @brief Controls whether the MeetsCondition function executes on the game thread
	 *
	 * When true, ensures the MeetsCondition function runs on the game thread, allowing safe
	 * access to UObject properties and other game thread-only operations. When false, the
	 * condition may be evaluated on a background thread for better performance.
	 */
	UPROPERTY(EditDefaultsOnly, Category = "Inworld",
		Meta = (ToolTip = "When true, ensures the MeetsCondition function runs on the game thread for safe UObject access"))
	bool bExecuteMeetsConditionInGameThread{true};

	/**
	 * @brief Controls whether the condition result should be inverted.
	 *
	 * When true, the edge allows data flow when the condition is NOT met, creating
	 * inverted logic flow control. This is useful for creating "unless" conditions
	 * or exception handling flows. Only visible when bHideNegatation is false.
	 */
	UPROPERTY(EditInstanceOnly, Category = "Inworld",
		meta = (EditCondition = "!bHideNegatation", EditConditionHides = true,
			ToolTip = "When true, the edge allows data flow when the condition is NOT met"))
	bool bNegateCondition{false};

	/**
	 * @brief Controls the visibility of the negation option in the editor.
	 *
	 * When true, hides the bNegateCondition property from the editor interface.
	 * This is used by derived classes that handle negation through their own
	 * specialized properties or logic (such as safety edges with bRequireSafe).
	 */
	UPROPERTY(VisibleAnywhere, Category = "Inworld",
		meta = (EditCondition = "false", EditConditionHides = true,
			ToolTip = "Controls the visibility of the negation option in the editor"))
	bool bHideNegatation{false};

private:
	/**
	 * @brief MeetsCondition function will match Input to matching "MeetsCondition" functions.
	 *
	 * This function will iterate over any UFUNCTION prefixed with "MeetsCondition" to find
	 * a suitable match. Once found, the UFUNCTION will be called with the unwraped
	 * type.
	 *
	 * @param ProcessContext Runtime context containing graph execution state and runtime data
	 * @param Input  data handle to be checked for meeting condition
	 * @return bool if condition is met for the edge or not
	 */
	bool MeetsCondition(const UInworldProcessContext* ProcessContext, const FInworldDataHandle& Input) const;

	/**
	 * @brief Generates JSON configuration for the conditional edge.
	 *
	 * This override generates the JSON configuration needed for the conditional edge's
	 * processing operations, including condition evaluation settings, thread preferences,
	 * and negation logic.
	 *
	 * @param GraphJson The JSON object to populate with the edge's configuration data
	 * @return Error code indicating success or failure of configuration generation
	 */
	virtual EInworldRuntimeErrors GetJsonConfig(const TSharedRef<FJsonObject>& GraphJson) override;

	/**
	 * @brief Retrieves the array of MeetsCondition functions available for this edge.
	 */
	TArray<UFunction*> GetMeetsConditionFunctions() const;

	friend bool inworld::ExecuteMeetsCondition(
		UInworldEdge_WithCondition* Edge, UInworldProcessContext* ProcessContext, FInworldDataHandle Input);
	friend class UInworldEdgeAsset;
};
