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

#include "InworldRuntimeSettings.h"

#include "HAL/FileManager.h"
#include "Interfaces/IPluginManager.h"
#include "InworldRuntime.h"
#include "InworldRuntimeSubsystem.h"
#include "Misc/Paths.h"
#include "Modules/ModuleManager.h"

UInworldRuntimeSettings::UInworldRuntimeSettings(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
	, bTelemetryEnabled{true}
	, TelemetryLogVerbosity{EInworldLogVerbosity::Display}
	, InworldEndpoint{"api-engine.inworld.ai:443"}
	, InworldHttpEndpoint{"https://api.inworld.ai"}
	, RemoteLLMProviders{}
	, RemoteLLMModels{}
	, RemoteTTSModels({"inworld-tts-1", "inworld-tts-1-max"})
	, RemoteTextEmbedderProviders{{"inworld"}}
	, RemoteTextEmbedderModels{{"BAAI/bge-large-en-v1.5"}, {"sentence-transformers/paraphrase-multilingual-mpnet-base-v2"}}
{
	{
		auto AddRemoteModel = [this](const FString& Key, const FString& Provider, const FString& Model)
		{
			FInworldLLMCreationConfig Config;
			Config.ComputeHost = EInworldComputeHost::REMOTE;
			Config.RemoteCreationConfig.Provider = Provider;
			Config.RemoteCreationConfig.Model = Model;
			LLMCreationConfig.Add(Key, Config);
			RemoteLLMProviders.AddUnique(Provider);
			RemoteLLMModels.AddUnique(Model);
		};

		// --- Anthropic Models ---
		// Provider: "anthropic"
		AddRemoteModel(TEXT("Claude Sonnet 4.5"), TEXT("anthropic"), TEXT("claude-sonnet-4-5"));
		AddRemoteModel(TEXT("Claude Sonnet 4"), TEXT("anthropic"), TEXT("claude-sonnet-4-0"));
		AddRemoteModel(TEXT("Claude Haiku 4.5"), TEXT("anthropic"), TEXT("claude-haiku-4-5"));
		AddRemoteModel(TEXT("Claude Opus 4.1"), TEXT("anthropic"), TEXT("claude-opus-4-1"));

		// --- OpenAI Models ---
		// Provider: "openai"
		AddRemoteModel(TEXT("GPT-5"), TEXT("openai"), TEXT("gpt-5"));
		AddRemoteModel(TEXT("GPT-5 mini"), TEXT("openai"), TEXT("gpt-5-mini"));
		AddRemoteModel(TEXT("GPT-5 nano"), TEXT("openai"), TEXT("gpt-5-nano"));
		AddRemoteModel(TEXT("GPT-4.1"), TEXT("openai"), TEXT("gpt-4.1"));
		AddRemoteModel(TEXT("GPT-4.1 mini"), TEXT("openai"), TEXT("gpt-4.1-mini"));
		AddRemoteModel(TEXT("GPT-4.1 nano"), TEXT("openai"), TEXT("gpt-4.1-nano"));
		AddRemoteModel(TEXT("GPT-4o"), TEXT("openai"), TEXT("gpt-4o"));
		AddRemoteModel(TEXT("GPT-4o mini"), TEXT("openai"), TEXT("gpt-4o-mini"));

		// --- Google Models ---
		// Provider: "google"
		AddRemoteModel(TEXT("Gemini 2.5 Pro"), TEXT("google"), TEXT("gemini-2.5-pro"));
		AddRemoteModel(TEXT("Gemini 2.5 Flash"), TEXT("google"), TEXT("gemini-2.5-flash"));
		AddRemoteModel(TEXT("Gemini 2.5 Flash Lite"), TEXT("google"), TEXT("gemini-2.5-flash-lite"));

		// --- Mistral Models ---
		// Provider: "mistral"
		AddRemoteModel(TEXT("Ministral-8b-Latest"), TEXT("mistral"), TEXT("ministral-8b-latest"));

		// --- Groq Models ---
		// Provider: "groq"
		AddRemoteModel(TEXT("gpt-oss-20b [Groq]"), TEXT("groq"), TEXT("openai/gpt-oss-20b"));
		AddRemoteModel(TEXT("gpt-oss-120b [Groq]"), TEXT("groq"), TEXT("openai/gpt-oss-120b"));

		// --- Fireworks Models ---
		// Provider: "fireworks"
		AddRemoteModel(TEXT("DeepSeek-V3.1 [Fireworks]"), TEXT("fireworks"), TEXT("accounts/fireworks/models/deepseek-v3p1"));
		AddRemoteModel(TEXT("DeepSeek-V3-0324 [Fireworks]"), TEXT("fireworks"), TEXT("accounts/fireworks/models/deepseek-v3-0324"));
	}
	{
		FInworldSTTCreationConfig Default;
		Default.ComputeHost = EInworldComputeHost::REMOTE;
		STTCreationConfig.Add("Default", Default);
	}
	{
		FInworldStreamingSTTCreationConfig Default;
		Default.ComputeHost = EInworldComputeHost::REMOTE;
		StreamingSTTCreationConfig.Add("Default", Default);
	}
	{
		FInworldTTSCreationConfig Default;
		Default.ComputeHost = EInworldComputeHost::REMOTE;
		TTSCreationConfig.Add("Default", Default);

		FInworldTTSCreationConfig InworldTTS1Max;
		InworldTTS1Max.ComputeHost = EInworldComputeHost::REMOTE;
		InworldTTS1Max.RemoteCreationConfig.InworldSpeechSynthesisConfig.ModelId = RemoteTTSModels[1];
		TTSCreationConfig.Add("Inworld-TTS-1-Max", InworldTTS1Max);
	}
	{
		FInworldTextEmbedderCreationConfig Default;
		Default.ComputeHost = EInworldComputeHost::REMOTE;
		Default.RemoteCreationConfig.Provider = RemoteTextEmbedderProviders[0];
		Default.RemoteCreationConfig.Model = RemoteTextEmbedderModels[0];
		TextEmbedderCreationConfig.Add("Default", Default);
	}
}

#if WITH_EDITOR
void UInworldRuntimeSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
	Super::PostEditChangeProperty(PropertyChangedEvent);
	const FName PropertyName{(PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None};
	const FName MemberProperty{
		(PropertyChangedEvent.MemberProperty != nullptr) ? PropertyChangedEvent.MemberProperty->GetFName() : NAME_None};
	if (PropertyName == GET_MEMBER_NAME_CHECKED(UInworldRuntimeSettings, bTelemetryEnabled) ||
		PropertyName == GET_MEMBER_NAME_CHECKED(UInworldRuntimeSettings, InworldEndpoint) ||
		PropertyName == GET_MEMBER_NAME_CHECKED(UInworldRuntimeSettings, InworldHttpEndpoint) ||
		PropertyName == GET_MEMBER_NAME_CHECKED(UInworldRuntimeSettings, TelemetryLogVerbosity) ||
		PropertyName == GET_MEMBER_NAME_CHECKED(UInworldRuntimeSettings, RuntimeApiKey))
	{
		FInworldRuntimeModule& InworldRuntime{FModuleManager::GetModuleChecked<FInworldRuntimeModule>("InworldRuntime")};
		InworldRuntime.ConfigureTelemetry();
	}
}
#endif

TArray<FString> UInworldRuntimeSettings::GetAlternativeBinaryTypes()
{
	return
	{
		"",
#if defined(INWORLD_WITH_CUDA)
			"CUDA",
#endif
	};
}

TArray<FString> UInworldRuntimeSettings::GetRemoteLLMProviders()
{
	return GetDefault<UInworldRuntimeSettings>()->RemoteLLMProviders;
}

TArray<FString> UInworldRuntimeSettings::GetLLMProviders()
{
	return GetRemoteLLMProviders();
}

TArray<FString> UInworldRuntimeSettings::GetLocalLLMModels()
{
	const FString PluginBaseDir{IPluginManager::Get().FindPlugin(TEXT("InworldRuntime"))->GetBaseDir()};
	const FString ModelFilePath{FPaths::Combine(PluginBaseDir, "Extras", "llm")};
	const FString Extension{TEXT("gguf")};
	TArray<FString> ExistingModelFiles;
	IFileManager::Get().FindFiles(ExistingModelFiles, *ModelFilePath, *Extension);
	return ExistingModelFiles;
}

TArray<FString> UInworldRuntimeSettings::GetRemoteLLMModels()
{
	return GetDefault<UInworldRuntimeSettings>()->RemoteLLMModels;
}

TArray<FString> UInworldRuntimeSettings::GetRemoteTTSModels()
{
	return GetDefault<UInworldRuntimeSettings>()->RemoteTTSModels;
}

TArray<FString> UInworldRuntimeSettings::GetLLMModels()
{
	TArray<FString> Models{};
	Models.Append(GetLocalLLMModels());
	Models.Append(GetRemoteLLMModels());
	return Models;
}

TArray<FString> UInworldRuntimeSettings::GetLocalSTTModels()
{
	const FString PluginBaseDir{IPluginManager::Get().FindPlugin(TEXT("InworldRuntime"))->GetBaseDir()};
	const FString ModelFilePath{FPaths::Combine(PluginBaseDir, "Extras", "stt")};
	const FString Extension{TEXT("bin")};
	TArray<FString> ExistingModelFiles;
	IFileManager::Get().FindFiles(ExistingModelFiles, *ModelFilePath, *Extension);
	return ExistingModelFiles;
}

TArray<FString> UInworldRuntimeSettings::GetRemoteTextEmbedderProviders()
{
	return GetDefault<UInworldRuntimeSettings>()->RemoteTextEmbedderProviders;
}

TArray<FString> UInworldRuntimeSettings::GetRemoteTextEmbedderModels()
{
	return GetDefault<UInworldRuntimeSettings>()->RemoteTextEmbedderModels;
}

TArray<FString> UInworldRuntimeSettings::GetLocalTextEmbedderModels()
{
	const FString PluginBaseDir{IPluginManager::Get().FindPlugin(TEXT("InworldRuntime"))->GetBaseDir()};
	const FString ModelFilePath{FPaths::Combine(PluginBaseDir, "Extras", "embedders")};
	const FString Extension{TEXT("gguf")};
	TArray<FString> ExistingModelFiles;
	IFileManager::Get().FindFiles(ExistingModelFiles, *ModelFilePath, *Extension);
	return ExistingModelFiles;
}

TArray<FString> UInworldRuntimeSettings::GetLocalTTSModels()
{
	const FString PluginBaseDir{IPluginManager::Get().FindPlugin(TEXT("InworldRuntime"))->GetBaseDir()};
	const FString ModelFilePath{FPaths::Combine(PluginBaseDir, "Extras", "tts")};
	const FString Extension{TEXT("onnx")};
	TArray<FString> ExistingModelFiles;
	IFileManager::Get().FindFiles(ExistingModelFiles, *ModelFilePath, *Extension);
	return ExistingModelFiles;
}
