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

#include "Primitives/STT/InworldSTTTasks.h"

#include "InworldRuntimeTypes.h"
#include "InworldRuntimeUtils.h"
#include "Misc/Optional.h"
#include "Misc/Paths.h"
#include "Primitives/STT/InworldSTTTypes.h"

#include <Interfaces/IPluginManager.h>

THIRD_PARTY_INCLUDES_START
#include "InworldRuntimeLibrary/inworld.h"
THIRD_PARTY_INCLUDES_END

static TVariant<TSharedPtr<inworld::STTInterface>, FString> CreateSTT(const FInworldLocalSTTCreationConfig& LocalSTTCreationConfig)
{
#define RETURN_ERROR(Error) return TVariant<TSharedPtr<inworld::STTInterface>, FString>{TInPlaceType<FString>(), Error};
#define RETURN_SUCCESS(Object) \
	return TVariant<TSharedPtr<inworld::STTInterface>, FString>{TInPlaceType<TSharedPtr<inworld::STTInterface>>(), Object};

	if (const FString ModelFilePath{GetInworldExtrasPath("stt", LocalSTTCreationConfig.Model)}; !FPaths::FileExists(ModelFilePath))
	{
		RETURN_ERROR(FString::Format(TEXT("{0}: Model not found at {1}"), {*FString(__func__), *ModelFilePath}))
	}

	inworld::LocalSTTConfig local_stt_config;
	inworld::Convert(LocalSTTCreationConfig, local_stt_config);
	if (const inworld::StatusOr_STTInterface status_or_stt{inworld::STTFactory().CreateSTT(local_stt_config)}; status_or_stt.ok())
	{
		RETURN_SUCCESS(MakeShared<inworld::STTInterface>(status_or_stt.value()));
	}
	else
	{
		RETURN_ERROR(
			FString::Format(TEXT("{0}: {1}"), {*FString(__func__), UTF8_TO_TCHAR(status_or_stt.status().ToString().c_str())}))
	}

#undef RETURN_ERROR
#undef RETURN_SUCCESS
}

static TVariant<TSharedPtr<inworld::STTInterface>, FString> CreateSTT(
	const FInworldRemoteSTTCreationConfig& RemoteSTTCreationConfig)
{
#define RETURN_ERROR(Error) return TVariant<TSharedPtr<inworld::STTInterface>, FString>{TInPlaceType<FString>(), Error};
#define RETURN_SUCCESS(Object) \
	return TVariant<TSharedPtr<inworld::STTInterface>, FString>{TInPlaceType<TSharedPtr<inworld::STTInterface>>(), Object};

	const FString& RuntimeApiKey{GetInworldRuntimeApiKey()};
	if (RuntimeApiKey.IsEmpty())
	{
		RETURN_ERROR(FString::Format(TEXT("{0}: Inworld Runtime API Key is not set. Check ProjectSettings."), {*FString(__func__)}))
	}

	inworld::RemoteSTTConfig remote_stt_config;
	inworld::Convert(RemoteSTTCreationConfig, remote_stt_config);
	if (const inworld::StatusOr_STTInterface status_or_stt{inworld::STTFactory().CreateSTT(remote_stt_config)}; status_or_stt.ok())
	{
		RETURN_SUCCESS(MakeShared<inworld::STTInterface>(status_or_stt.value()));
	}
	else
	{
		RETURN_ERROR(
			FString::Format(TEXT("{0}: {1}"), {*FString(__func__), UTF8_TO_TCHAR(status_or_stt.status().ToString().c_str())}))
	}

#undef RETURN_ERROR
#undef RETURN_SUCCESS
}

namespace inworld
{
namespace Tasks
{
namespace STT
{

UE::Tasks::TTask<TVariant<TSharedPtr<inworld::STTInterface>, FString>> CreateSTTTask(
	const FInworldSTTCreationConfig& STTCreationConfig)
{
#define RETURN_ERROR(Error) return TVariant<TSharedPtr<inworld::STTInterface>, FString>{TInPlaceType<FString>(), Error};

	return UE::Tasks::Launch(UE_SOURCE_LOCATION,
		[STTCreationConfig]()
		{
			switch (STTCreationConfig.ComputeHost)
			{
				case EInworldComputeHost::LOCAL:
				{
					const FInworldLocalSTTCreationConfig& LocalSTTConfig{STTCreationConfig.LocalCreationConfig};
					return CreateSTT(LocalSTTConfig);
				}
				break;
				case EInworldComputeHost::REMOTE:
				{
					const FInworldRemoteSTTCreationConfig& RemoteSTTConfig{STTCreationConfig.RemoteCreationConfig};
					return CreateSTT(RemoteSTTConfig);
				}
				break;
				default:
					RETURN_ERROR(
						FString::Format(TEXT("{0}: Invalid Enum Value: STTCreationConfig.ComputeHost"), {*FString(__func__)}));
			}
		});
#undef RETURN_ERROR
}

}	 // namespace STT
}	 // namespace Tasks
}	 // namespace inworld
