/**
 * Copyright 2022-2025 Theai, Inc. dba Inworld AI
 */
#include "GraphEditor/NodeDescriptionParser.h"
#include "Graph/Assets/InworldGraphAsset.h"
#include "Graph/Nodes/InworldNode_SubgraphNode.h"
#include "GraphEditor/InworldGraphNodeInputOutputSlot.h"

FNodeDescriptionParser::FNodeDescriptionParser(UInworldNode* InNode)
{
	if (InNode)
	{
		Node = InNode;
		NodeClass = InNode->GetClass();
	}
}

FNodeDescriptionParser::FNodeDescriptionParser(TSubclassOf<UInworldNode> InNodeClass)
{
	if (InNodeClass)
	{
		Node = InNodeClass->GetDefaultObject<UInworldNode>();
		NodeClass = InNodeClass;
	}
}

FString FNodeDescriptionParser::GetMainDescription() const
{
	FString FullDescription{};
	TArray<FString> Lines;

	FString RawClassDescription = NodeClass->GetToolTipText().ToString();

	RawClassDescription.ParseIntoArrayLines(Lines);
	UInworldNode_SubgraphNode* SubgraphNode = Cast<UInworldNode_SubgraphNode>(Node);
	{
		FString Description{};
		bool bIsDescription = false;
		FString NodeClassName = NodeClass->GetName();
		if (NodeClassName.RemoveFromEnd("_C"))
		{
			bIsDescription = true;
			Description += FString::Printf(TEXT("%s (Blueprint Node)\n\n"), *NodeClassName);
			if (!Node->GetDescription().IsEmpty())
			{
				Description += FString::Printf(TEXT("%s\n"), *Node->GetDescription());
			}
		}
		else if (SubgraphNode && SubgraphNode->GetGraphAsset())
		{
			bIsDescription = true;
			Description += FString::Printf(TEXT("%s (Subgraph)\n\n"), *SubgraphNode->GetGraphAsset()->GetName());
			if (!Node->GetDescription().IsEmpty())
			{
				Description += FString::Printf(TEXT("%s\n"), *SubgraphNode->GetDescription());
			}
		}
		else
		{
			Description += FString::Printf(TEXT("%s\n\n"), *NodeClassName);

			for (const FString& Line : Lines)
			{
				FString TrimmedLine = Line.TrimStart();
				if (TrimmedLine.StartsWith(TEXT("@input")) || TrimmedLine.StartsWith(TEXT("@output")) ||
					TrimmedLine.StartsWith(TEXT("@class")))
				{
					continue;
				}
				if (bIsDescription)
				{
					Description += FString::Printf(TEXT("%s\n"), *Line);
				}
				else if (TrimmedLine.StartsWith(TEXT("@brief")))
				{
					bIsDescription = true;
					Description += FString::Printf(TEXT("%s\n"), *TrimmedLine.Mid(FString(TEXT("@brief")).Len()).TrimStart());
				}
			}
		}

		if (bIsDescription)
		{
			FullDescription = Description += "\n";
		}
	}

	
	TArray<FInworldNodeInputOutputSlot> Inputs;
	FInworldNodeInputOutputSlot Output;
	if (SubgraphNode)
	{
		const UInworldNode* StartNode = SubgraphNode->GetStartNode();
		const UInworldNode* EndNode = SubgraphNode->GetEndNode();
		if (StartNode && EndNode)
		{
			Inputs = FInworldNodeInputOutputSlot::GetInputOutputSlotsForNodeInput(StartNode);
			Output = FInworldNodeInputOutputSlot::GetInputOutputSlotForNodeOutput(EndNode);
		}
	}
	else
	{
		Inputs = FInworldNodeInputOutputSlot::GetInputOutputSlotsForNodeInput(Node);
		Output = FInworldNodeInputOutputSlot::GetInputOutputSlotForNodeOutput(Node);
	}

	{
		FString InputsDescription{};
		bool bIsInputs = false;

		for (const FInworldNodeInputOutputSlot& Input : Inputs)
		{
			if (!bIsInputs)
			{
				InputsDescription += TEXT("Inputs:\n");
				bIsInputs = true;
			}
			FString ValidTypes{""};
			for (const UStruct* InputType : Input.ValidTypes)
			{
				if (!InputType)
				{
					continue;
				}
				ValidTypes += InputType->GetName();
				if (InputType != Input.ValidTypes.Last())
				{
					ValidTypes += " OR ";
				}
			}
			FString InputDescription{};
			bool bInputValid = false;
			if (!Input.Name.IsEmpty())
			{
				InputDescription += FString::Printf(TEXT(" %s"), *Input.Name);
				bInputValid = true;
			}
			if (Input.bOptional)
			{
				InputDescription += " (OPTIONAL) ";
				bInputValid = true;
			}
			if (!Input.ValidTypes.IsEmpty())
			{
				InputDescription += FString::Printf(TEXT(" - %s"), *ValidTypes);
				bInputValid = true;
			}
			if (bInputValid)
			{
				InputsDescription += FString::Printf(TEXT(" %s\n"), *InputDescription);
			}
		}
		if (bIsInputs)
		{
			FullDescription += FString::Printf(TEXT("%s\n"), *InputsDescription);
		}
	}

	{
		FString OutputDescription{};
		bool bIsOutput = false;
		OutputDescription += TEXT("Output:\n ");
		if (!Output.Name.IsEmpty())
		{
			OutputDescription += FString::Printf(TEXT(" %s"), *Output.Name);
			bIsOutput = true;
		}
		FString ValidOutputTypes{""};
		for (const UStruct* OutputType : Output.ValidTypes)
		{
			ValidOutputTypes += OutputType->GetName();
			if (OutputType != Output.ValidTypes.Last())
			{
				ValidOutputTypes += " OR ";
			}
		}
		if (!ValidOutputTypes.IsEmpty())
		{
			OutputDescription += FString::Printf(TEXT(" - %s"), *ValidOutputTypes);
			bIsOutput = true;
		}
		if (bIsOutput)
		{
			FullDescription += FString::Printf(TEXT("%s\n"), *OutputDescription);
		}
	}

	return FullDescription.TrimEnd();
}
