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

#include "InworldRuntimeEditor.h"

#include "Assets/AssetActions_InworldTextEmbeddings.h"
#include "Assets/InworldTextEmbeddingAssetDetailsCustomization.h"
#include "EditorUtilitySubsystem.h"
#include "EditorUtilityWidgetBlueprint.h"
#include "Graph/InworldData.h"
#include "Graph/Nodes/InworldNode_Custom.h"
#include "GraphEditor/AssetActions_InworldCustomEdge.h"
#include "GraphEditor/AssetActions_InworldCustomNode.h"
#include "GraphEditor/AssetActions_InworldGraph.h"
#include "GraphEditor/InworldGraphAssetEditor/EdGraph_InworldGraph.h"
#include "GraphEditor/InworldGraphAssetEditor/InworldGraphEditorStyle.h"
#include "GraphEditor/InworldGraphAssetEditor/InworldGraphPanelNodeFactory.h"
#include "ISettingsModule.h"
#include "Interfaces/IPluginManager.h"
#include "InworldRuntimeEditorSettings.h"
#include "InworldRuntimeSettings.h"
#include "Misc/Paths.h"
#include "Modules/ModuleManager.h"
#include "Primitives/TextEmbedder/InworldTextEmbedding.h"
#include "PropertyEditorModule.h"
#include "Style/InworldEditorUIStyle.h"
#include "ToolMenus.h"

#define LOCTEXT_NAMESPACE "FInworldRuntimeEditorModule"

DEFINE_LOG_CATEGORY(LogInworldRuntimeEditor);

void FInworldRuntimeEditorModule::StartupModule()
{
	FInworldEditorUIStyle::Initialize();

	// Register the details panel customization for our text embedding asset
	FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
	PropertyModule.RegisterCustomClassLayout(UInworldTextEmbeddingAsset::StaticClass()->GetFName(),
		FOnGetDetailCustomizationInstance::CreateStatic(&FInworldTextEmbeddingAssetDetailsCustomization::MakeInstance));

	if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"); SettingsModule != nullptr)
	{
		SettingsModule->RegisterSettings("Project", "Plugins", "Inworld", LOCTEXT("InworldRuntimeSettingsName", "Inworld"),
			LOCTEXT("InworldRuntimeSettingsDescription", "Inworld Runtime Settings"), GetMutableDefault<UInworldRuntimeSettings>());

		SettingsModule->RegisterSettings("Project", "Plugins", "Inworld Editor",
			LOCTEXT("InworldRuntimeEditorSettingsName", "Inworld Editor"),
			LOCTEXT("InworldRuntimeEditorSettingsDescription", "Inworld Runtime Editor Settings"),
			GetMutableDefault<UInworldRuntimeEditorSettings>());
	}

	UToolMenus::RegisterStartupCallback(
		FSimpleMulticastDelegate::FDelegate::CreateStatic(&FInworldRuntimeEditorModule::MenuRegistration));

	GraphEditor_Startup();

	OnInworldAssetsPreSaveHandle = FCoreUObjectDelegates::OnObjectPreSave.AddLambda(
		[](UObject* Object, FObjectPreSaveContext SaveContext)
		{
			if (UBlueprint* Blueprint = Cast<UBlueprint>(Object))
			{
				if (Blueprint->GeneratedClass && Blueprint->GeneratedClass->IsChildOf(UInworldNode_Custom::StaticClass()))
				{
					UEdGraph_InworldGraph::RefreshOpenedAssets();
				}
			}
		});
}

void FInworldRuntimeEditorModule::MenuRegistration()
{
	UToolMenu* ToolsMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Tools");
	if (!ToolsMenu)
	{
		return;
	}

	const FName SectionName = "Inworld";
	FToolMenuSection* Section = ToolsMenu->FindSection(SectionName);

	// If the section doesn't exist, create it.
	if (!Section)
	{
		UE_LOG(LogInworldRuntimeEditor, Log, TEXT("Cannot find Inworld section, creating one."));
		Section = &ToolsMenu->AddSection(SectionName, LOCTEXT("InworldMenuSection_Label", "Inworld"));
	}

	if (Section)
	{
		Section->AddEntry(FToolMenuEntry::InitMenuEntry("Text Embedding", LOCTEXT("TextEmbeddingMenuItem_Label", "Text Embedding"),
			LOCTEXT("TextEmbeddingMenuItem_ToolTip", "Tool for embedding text records."), FSlateIcon(),
			FExecuteAction::CreateLambda(
				[]()
				{
					UEditorUtilityWidgetBlueprint* EUWBP = LoadObject<UEditorUtilityWidgetBlueprint>(
						nullptr, TEXT("/InworldRuntime/Utilities/EUW_InworldTextEmbedder.EUW_InworldTextEmbedder"));
					UEditorUtilitySubsystem* EditorUtilitySubsystem = GEditor->GetEditorSubsystem<UEditorUtilitySubsystem>();
					if (EditorUtilitySubsystem && EUWBP)
					{
						EditorUtilitySubsystem->SpawnAndRegisterTab(EUWBP);
					}
					else
					{
						UE_LOG(LogInworldRuntimeEditor, Error, TEXT("Failed to spawn Inworld Text Embedder widget."));
					}
				})));
	}
}

void FInworldRuntimeEditorModule::ShutdownModule()
{
	// Unregister the details panel customization
	if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
	{
		FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
		PropertyModule.UnregisterCustomClassLayout(UInworldTextEmbeddingAsset::StaticClass()->GetFName());
	}

	if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"); SettingsModule != nullptr)
	{
		SettingsModule->UnregisterSettings("Project", "Plugins", "Inworld");
	}

	GraphEditor_Shutdown();

	FInworldEditorUIStyle::Shutdown();

	if (OnInworldAssetsPreSaveHandle.IsValid())
	{
		FCoreUObjectDelegates::OnObjectPreSave.Remove(OnInworldAssetsPreSaveHandle);
		OnInworldAssetsPreSaveHandle.Reset();
	}
}

void FInworldRuntimeEditorModule::GraphEditor_Startup()
{
	FInworldGraphEditorStyle::Initialize();
	GraphPanelNodeFactory_InworldGraph = MakeShareable(new FGraphPanelNodeFactory_InworldGraph());
	FEdGraphUtilities::RegisterVisualNodeFactory(GraphPanelNodeFactory_InworldGraph);

	IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
	InworldAssetCategoryBit = AssetTools.RegisterAdvancedAssetCategory(
		FName(TEXT("InworldAssetActions")), LOCTEXT("InworldAssetActionsCategory", "Inworld"));

	TSharedRef<IAssetTypeActions> GraphAssetActions = MakeShareable(new FAssetActions_InworldGraph(InworldAssetCategoryBit));
	AssetTools.RegisterAssetTypeActions(GraphAssetActions);
	CreatedAssetTypeActions.Add(GraphAssetActions);

	TSharedRef<IAssetTypeActions> CustomNodeAssetActions =
		MakeShareable(new FAssetActions_InworldCustomNode(InworldAssetCategoryBit));
	AssetTools.RegisterAssetTypeActions(CustomNodeAssetActions);
	CreatedAssetTypeActions.Add(CustomNodeAssetActions);

	TSharedRef<IAssetTypeActions> CustomEdgeAssetActions =
		MakeShareable(new FAssetActions_InworldCustomEdge(InworldAssetCategoryBit));
	AssetTools.RegisterAssetTypeActions(CustomEdgeAssetActions);
	CreatedAssetTypeActions.Add(CustomEdgeAssetActions);

	TSharedRef<IAssetTypeActions> TextEmbeddingsAssetActions =
		MakeShareable(new FAssetActions_InworldTextEmbeddings(InworldAssetCategoryBit));
	AssetTools.RegisterAssetTypeActions(TextEmbeddingsAssetActions);
	CreatedAssetTypeActions.Add(TextEmbeddingsAssetActions);
}

void FInworldRuntimeEditorModule::GraphEditor_Shutdown()
{
	if (FModuleManager::Get().IsModuleLoaded("AssetTools"))
	{
		IAssetTools& AssetTools = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools").Get();
		for (int32 Index = 0; Index < CreatedAssetTypeActions.Num(); ++Index)
		{
			AssetTools.UnregisterAssetTypeActions(CreatedAssetTypeActions[Index].ToSharedRef());
		}
	}
	if (GraphPanelNodeFactory_InworldGraph.IsValid())
	{
		FEdGraphUtilities::UnregisterVisualNodeFactory(GraphPanelNodeFactory_InworldGraph);
		GraphPanelNodeFactory_InworldGraph.Reset();
	}
	FInworldGraphEditorStyle::Shutdown();
}

#undef LOCTEXT_NAMESPACE

IMPLEMENT_MODULE(FInworldRuntimeEditorModule, InworldRuntimeEditor)
