#include "DruidsMCPBridge.h"

#include "Containers/Ticker.h"
#include "CoreGlobals.h"
#include "DruidsMCPBridgeAISetup.h"
#include "DruidsMCPBridgeServer.h"
#include "DruidsMCPBridgeEditorSettings.h"
#include "Extensions/DruidsMCPBridgeSageExtensionRegistry.h"
#include "ISettingsModule.h"
#include "Misc/ConfigCacheIni.h"
#include "ToolMenus.h"
#include "UObject/UObjectGlobals.h"

DEFINE_LOG_CATEGORY(LogDruidsMCPBridge);

#define LOCTEXT_NAMESPACE "FDruidsMCPBridgeModule"

void FDruidsMCPBridgeModule::StartupModule()
{
	bShutdownStarted = false;
	bEditorSettingsRegistered = false;
	UE_LOG(LogDruidsMCPBridge, Log, TEXT("Druids MCP Bridge module starting"));

	BridgeServer = MakeShared<FDruidsMCPBridgeServer>();

	StartCommand = MakeUnique<FAutoConsoleCommand>(
		TEXT("DruidsMCPBridge.Start"),
		TEXT("Start the local Druids MCP Bridge endpoint."),
		FConsoleCommandDelegate::CreateRaw(this, &FDruidsMCPBridgeModule::StartBridge));

	StopCommand = MakeUnique<FAutoConsoleCommand>(
		TEXT("DruidsMCPBridge.Stop"),
		TEXT("Stop the local Druids MCP Bridge endpoint."),
		FConsoleCommandDelegate::CreateRaw(this, &FDruidsMCPBridgeModule::StopBridge));

	StatusCommand = MakeUnique<FAutoConsoleCommand>(
		TEXT("DruidsMCPBridge.Status"),
		TEXT("Log the local Druids MCP Bridge endpoint status."),
		FConsoleCommandDelegate::CreateLambda([this]()
		{
			if (BridgeServer.IsValid() && BridgeServer->IsRunning())
			{
				UE_LOG(LogDruidsMCPBridge, Display, TEXT("Druids MCP Bridge status: running at %s"), *BridgeServer->GetEndpointUrl());
			}
			else
			{
				UE_LOG(LogDruidsMCPBridge, Display, TEXT("Druids MCP Bridge status: stopped"));
			}
		}));

	UE_LOG(LogDruidsMCPBridge, Display, TEXT("Registered console commands: DruidsMCPBridge.Start, DruidsMCPBridge.Stop, DruidsMCPBridge.Status"));

	UToolMenus::RegisterStartupCallback(
		FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FDruidsMCPBridgeModule::RegisterMenus));

	RegisterEditorSettings();
	ScheduleAutoStart();
}

void FDruidsMCPBridgeModule::ShutdownModule()
{
	if (bShutdownStarted)
	{
		return;
	}
	bShutdownStarted = true;

	UE_LOG(LogDruidsMCPBridge, Log, TEXT("Druids MCP Bridge module shutting down"));

	if (AutoStartTickHandle.IsValid())
	{
		FTSTicker::GetCoreTicker().RemoveTicker(AutoStartTickHandle);
		AutoStartTickHandle.Reset();
	}

	if (BridgeServer.IsValid())
	{
		BridgeServer->Stop();
		BridgeServer.Reset();
	}

	StartCommand.Reset();
	StopCommand.Reset();
	StatusCommand.Reset();

	if (!GExitPurge && !IsEngineExitRequested())
	{
		UnregisterEditorSettings();
		UDruidsMCPBridgeSageExtensionRegistry::Shutdown();
	}
	else
	{
		EditorSettings = nullptr;
		bEditorSettingsRegistered = false;
		UDruidsMCPBridgeSageExtensionRegistry::Shutdown();
	}

	if (!GExitPurge && !IsEngineExitRequested() && FModuleManager::Get().IsModuleLoaded(TEXT("ToolMenus")))
	{
		UToolMenus::UnRegisterStartupCallback(this);
		UToolMenus::UnregisterOwner(this);
	}
}

void FDruidsMCPBridgeModule::RegisterMenus()
{
	UE_LOG(LogDruidsMCPBridge, Display, TEXT("Registering Druids MCP Bridge editor menu entries"));

	FToolMenuOwnerScoped OwnerScoped(this);

	if (UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Tools"))
	{
		FToolMenuSection& Section = Menu->FindOrAddSection("DruidsMCPBridge");
		Section.Label = LOCTEXT("DruidsMCPBridgeSection", "Druids MCP Bridge");

		Section.AddMenuEntry(
			"DruidsMCPBridgeToggle",
			TAttribute<FText>::Create(TAttribute<FText>::FGetter::CreateRaw(this, &FDruidsMCPBridgeModule::GetToggleBridgeLabel)),
			TAttribute<FText>::Create(TAttribute<FText>::FGetter::CreateRaw(this, &FDruidsMCPBridgeModule::GetBridgeStatusLabel)),
			FSlateIcon(),
			FUIAction(FExecuteAction::CreateRaw(this, &FDruidsMCPBridgeModule::ToggleBridge)));

		Section.AddMenuEntry(
			"DruidsMCPBridgeAISetup",
			LOCTEXT("DruidsMCPBridgeAISetupLabel", "Connect AI Assistant..."),
			LOCTEXT("DruidsMCPBridgeAISetupTooltip", "Open a setup wizard for connecting Codex, Gemini, Antigravity, Claude Code, Cursor, and Rider to this local MCP bridge."),
			FSlateIcon(),
			FUIAction(FExecuteAction::CreateRaw(this, &FDruidsMCPBridgeModule::OpenAISetupWizard)));

		UE_LOG(LogDruidsMCPBridge, Display, TEXT("Registered Tools menu entry for Druids MCP Bridge"));
	}
	else
	{
		UE_LOG(LogDruidsMCPBridge, Warning, TEXT("Could not extend LevelEditor.MainMenu.Tools for Druids MCP Bridge"));
	}
}

void FDruidsMCPBridgeModule::RegisterEditorSettings()
{
	if (!EditorSettings)
	{
		EditorSettings = NewObject<UDruidsMCPBridgeEditorSettings>(GetTransientPackage(), FName(TEXT("DruidsMCPBridgeEditorSettings")));
		RefreshEditorSettings();
	}

	if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>(TEXT("Settings")))
	{
		SettingsModule->RegisterSettings(
			TEXT("Editor"),
			TEXT("Plugins"),
			TEXT("DruidsMCPBridge"),
			LOCTEXT("DruidsMCPBridgeSettingsName", "Druids MCP Bridge"),
			LOCTEXT("DruidsMCPBridgeSettingsDescription", "Connect AI assistants or copy MCP setup snippets for Codex, Gemini, Antigravity, Claude Code, Cursor, and JetBrains/Rider."),
			EditorSettings);
		bEditorSettingsRegistered = true;

		UE_LOG(LogDruidsMCPBridge, Display, TEXT("Registered Druids MCP Bridge editor preferences"));
	}
	else
	{
		UE_LOG(LogDruidsMCPBridge, Warning, TEXT("Settings module unavailable; Druids MCP Bridge editor preferences were not registered"));
	}
}

void FDruidsMCPBridgeModule::UnregisterEditorSettings()
{
	if (!bEditorSettingsRegistered)
	{
		EditorSettings = nullptr;
		return;
	}

	if (GExitPurge || IsEngineExitRequested() || !FModuleManager::Get().IsModuleLoaded(TEXT("Settings")))
	{
		EditorSettings = nullptr;
		bEditorSettingsRegistered = false;
		return;
	}

	if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>(TEXT("Settings")))
	{
		SettingsModule->UnregisterSettings(
			TEXT("Editor"),
			TEXT("Plugins"),
			TEXT("DruidsMCPBridge"));
	}

	EditorSettings = nullptr;
	bEditorSettingsRegistered = false;
}

void FDruidsMCPBridgeModule::RefreshEditorSettings()
{
	if (!EditorSettings)
	{
		return;
	}

	const bool bRunning = IsBridgeRunning();
	const FString EndpointUrl = bRunning && BridgeServer.IsValid()
		? BridgeServer->GetEndpointUrl()
		: TEXT("http://127.0.0.1:8765/mcp");

	EditorSettings->RefreshFromEndpoint(EndpointUrl, bRunning);
}

void FDruidsMCPBridgeModule::ScheduleAutoStart()
{
	if (!ShouldAutoStart())
	{
		UE_LOG(LogDruidsMCPBridge, Display, TEXT("Druids MCP Bridge auto-start is disabled"));
		return;
	}

	if (AutoStartTickHandle.IsValid())
	{
		return;
	}

	UE_LOG(LogDruidsMCPBridge, Display, TEXT("Scheduling Druids MCP Bridge auto-start"));
	AutoStartTickHandle = FTSTicker::GetCoreTicker().AddTicker(
		FTickerDelegate::CreateRaw(this, &FDruidsMCPBridgeModule::HandleAutoStartTick),
		0.1f);
}

bool FDruidsMCPBridgeModule::HandleAutoStartTick(float DeltaTime)
{
	AutoStartTickHandle.Reset();

	if (!IsBridgeRunning())
	{
		UE_LOG(LogDruidsMCPBridge, Display, TEXT("Auto-starting Druids MCP Bridge"));
		StartBridge();
	}

	return false;
}

bool FDruidsMCPBridgeModule::ShouldAutoStart() const
{
	bool bAutoStart = true;

	if (GConfig)
	{
		GConfig->GetBool(
			TEXT("DruidsMCPBridge"),
			TEXT("bAutoStart"),
			bAutoStart,
			GEngineIni);
	}

	return bAutoStart;
}

void FDruidsMCPBridgeModule::StartBridge()
{
	UE_LOG(LogDruidsMCPBridge, Display, TEXT("StartBridge requested"));

	if (!BridgeServer.IsValid())
	{
		UE_LOG(LogDruidsMCPBridge, Display, TEXT("Bridge server was not allocated; creating it now"));
		BridgeServer = MakeShared<FDruidsMCPBridgeServer>();
	}

	if (BridgeServer->Start())
	{
		UE_LOG(LogDruidsMCPBridge, Display, TEXT("Druids MCP Bridge running at %s"), *BridgeServer->GetEndpointUrl());
		RefreshEditorSettings();
	}
}

void FDruidsMCPBridgeModule::StopBridge()
{
	UE_LOG(LogDruidsMCPBridge, Display, TEXT("StopBridge requested"));

	if (BridgeServer.IsValid())
	{
		BridgeServer->Stop();
	}

	RefreshEditorSettings();
}

void FDruidsMCPBridgeModule::ToggleBridge()
{
	UE_LOG(LogDruidsMCPBridge, Display, TEXT("ToggleBridge requested. Current state: %s"), IsBridgeRunning() ? TEXT("running") : TEXT("stopped"));

	if (IsBridgeRunning())
	{
		StopBridge();
	}
	else
	{
		StartBridge();
	}
}

void FDruidsMCPBridgeModule::OpenAISetupWizard()
{
	FDruidsMCPBridgeAISetup::OpenWindow(
		FDruidsMCPBridgeAISetup::FEnsureBridgeRunning::CreateRaw(this, &FDruidsMCPBridgeModule::StartBridge),
		FDruidsMCPBridgeAISetup::FGetEndpointUrl::CreateRaw(this, &FDruidsMCPBridgeModule::GetCurrentEndpointUrl));
}

bool FDruidsMCPBridgeModule::IsBridgeRunning() const
{
	return BridgeServer.IsValid() && BridgeServer->IsRunning();
}

FString FDruidsMCPBridgeModule::GetCurrentEndpointUrl() const
{
	return BridgeServer.IsValid() && BridgeServer->IsRunning()
		? BridgeServer->GetEndpointUrl()
		: TEXT("http://127.0.0.1:8765/mcp");
}

FText FDruidsMCPBridgeModule::GetToggleBridgeLabel() const
{
	return IsBridgeRunning()
		? LOCTEXT("StopDruidsMCPBridge", "Stop Druids MCP Bridge")
		: LOCTEXT("StartDruidsMCPBridge", "Start Druids MCP Bridge");
}

FText FDruidsMCPBridgeModule::GetBridgeStatusLabel() const
{
	if (BridgeServer.IsValid() && BridgeServer->IsRunning())
	{
		return FText::Format(
			LOCTEXT("DruidsMCPBridgeRunningTooltip", "Local MCP endpoint is running at {0}"),
			FText::FromString(BridgeServer->GetEndpointUrl()));
	}

	return LOCTEXT("DruidsMCPBridgeStoppedTooltip", "Start the local MCP endpoint for Codex.");
}

#undef LOCTEXT_NAMESPACE

IMPLEMENT_MODULE(FDruidsMCPBridgeModule, DruidsMCPBridge)
