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

#include "Graph/Nodes/InworldNode_GetRuntimeData.h"

#include "Graph/InworldGraph.h"

FInworldDataHandle UInworldNode_GetRuntimeData::Process_GetRuntimeData(UInworldProcessContext* ProcessContext)
{
	if (ProcessContext == nullptr)
	{
		return FInworldData_Error("Invalid Process Context!");
	}

	if (FInworldDataHandle DataHandle; ProcessContext->GetRuntimeData(RuntimeDataKey, DataHandle) && DataHandle.IsValid())
	{
		UStruct* DataStruct = DataHandle->GetScriptStruct();
		if (FInworldData_Struct* Struct = DataHandle.Unwrap<FInworldData_Struct>())
		{
			DataStruct = Struct->GetDataScriptStruct();
			if (FInworldData_Object Object; Struct->GetData(Object))
			{
				if (DataType == EInworldGetRuntimeDataType::OBJECT)
				{
					DataStruct = Object.Object != nullptr ? Object.Object->GetClass() : nullptr;
				}
				else
				{
					UE_LOG(LogInworldGraph, Warning,
						TEXT("Passing Object as non-object type! Prefer using Object DataType, and explicitly set UObject Class."))
				}
			}
		}
		switch (DataType)
		{
			case EInworldGetRuntimeDataType::STRUCT:
				if (Cast<UClass>(DataStruct))
				{
					return FInworldData_Error(FString::Format(
						TEXT(
							"Data type mismatch for Key {0}. Expected data type STRUCT (of type {1}), but found data of type OBJECT (of class {2}) instead."),
						{RuntimeDataKey.ToString(), StructType ? StructType->GetName() : TEXT("nullptr"),
							DataStruct ? DataStruct->GetName() : TEXT("nullptr")}));
				}

				if (DataStruct != StructType)
				{
					return FInworldData_Error(
						FString::Format(TEXT("Failed to match RuntimeData for Key {0}. Expected Struct {1}, but found {2} instead"),
							{RuntimeDataKey.ToString(), StructType != nullptr ? StructType->GetName() : TEXT("nullptr"),
								DataStruct != nullptr ? DataStruct->GetName() : TEXT("nullptr")}));
				}
				break;
			case EInworldGetRuntimeDataType::OBJECT:
				if (!Cast<UClass>(DataStruct))
				{
					return FInworldData_Error(FString::Format(
						TEXT(
							"Data type mismatch for Key {0}. Expected data type OBJECT (of class {1}), but found data of type STRUCT (of type {2}) instead."),
						{RuntimeDataKey.ToString(), *ObjectType != nullptr ? (*ObjectType)->GetName() : TEXT("nullptr"),
							DataStruct ? DataStruct->GetName() : TEXT("nullptr")}));
				}

				if (DataStruct != *ObjectType && (DataStruct == nullptr || !DataStruct->IsChildOf(ObjectType)))
				{
					return FInworldData_Error(
						FString::Format(TEXT("Failed to match RuntimeData for Key {0}. Expected Class {1}, but found {2} instead"),
							{RuntimeDataKey.ToString(), *ObjectType != nullptr ? *ObjectType->GetName() : TEXT("nullptr"),
								DataStruct != nullptr ? DataStruct->GetName() : TEXT("nullptr")}));
				}
				break;
			case EInworldGetRuntimeDataType::BOOL:
				if (DataStruct != FInworldData_Bool::StaticStruct())
				{
					return FInworldData_Error(
						FString::Format(TEXT("Failed to match RuntimeData for Key {0}. Expected Bool, but found {1} instead"),
							{RuntimeDataKey.ToString(), DataStruct != nullptr ? DataStruct->GetName() : TEXT("nullptr")}));
				}
				break;
			case EInworldGetRuntimeDataType::INT:
				if (DataStruct != FInworldData_Int::StaticStruct())
				{
					return FInworldData_Error(
						FString::Format(TEXT("Failed to match RuntimeData for Key {0}. Expected Int, but found {1} instead"),
							{RuntimeDataKey.ToString(), DataStruct != nullptr ? DataStruct->GetName() : TEXT("nullptr")}));
				}
				break;
			case EInworldGetRuntimeDataType::FLOAT:
				if (DataStruct != FInworldData_Float::StaticStruct())
				{
					return FInworldData_Error(
						FString::Format(TEXT("Failed to match RuntimeData for Key {0}. Expected Float, but found {1} instead"),
							{RuntimeDataKey.ToString(), DataStruct != nullptr ? DataStruct->GetName() : TEXT("nullptr")}));
				}
				break;
			default:
				checkNoEntry();
				break;
		}
		return DataHandle;
	}
	else
	{
		return FInworldData_Error(FString::Format(TEXT("No runtime data found for key: {0}"), {RuntimeDataKey.ToString()}));
	}
}

FString UInworldNode_GetRuntimeData::GetRuntimeDataClassName()
{
	if (DataType == EInworldGetRuntimeDataType::STRUCT && StructType)
	{
		return StructType->GetStructCPPName();
	}
	else if (DataType == EInworldGetRuntimeDataType::OBJECT && ObjectType)
	{
		return ObjectType->GetName();
	}
	else if (DataType == EInworldGetRuntimeDataType::BOOL)
	{
		return TEXT("bool");
	}
	else if (DataType == EInworldGetRuntimeDataType::INT)
	{
		return TEXT("int");
	}
	else if (DataType == EInworldGetRuntimeDataType::FLOAT)
	{
		return TEXT("float");
	}
	return {};
}
