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

#include "Components/InworldSimpleCharacterComponent.h"

#include "Components/InworldSimplePlayerComponent.h"
#include "Graph/InworldData.h"
#include "InworldBlueprintFunctionLibrary.h"
#include "InworldCharacter.h"

void UInworldSimpleCharacterComponent::BeginPlay()
{
	Super::BeginPlay();
	if (CharacterProfile == nullptr)
	{
		CharacterProfile = NewObject<UInworldGraphRuntimeData_CharacterProfile>(this);
	}
	if (Voice == nullptr)
	{
		Voice = NewObject<UInworldGraphRuntimeData_Voice>(this);
	}
	if (EventHistory == nullptr)
	{
		EventHistory = NewObject<UInworldGraphRuntimeData_EventHistory>(this);
	}

	TrySignalReady();
}

void UInworldSimpleCharacterComponent::AddHistoryEvent(const FInworldEventSpeech& EventHistoryEntry)
{
	if (!IsValid(EventHistory))
	{
		UE_LOG(LogInworldCharacter, Error,
			TEXT("UInworldSimpleCharacterComponent::UpdateHistory - Failed to update "
				 "Event History. Event History runtime data is null."));
		return;
	}

	EventHistory->AddSpeechEvent(EventHistoryEntry);
	if (IsValid(Memory))
	{
		Memory->HandleEventHistoryAddition(EventHistory);
	}
}

void UInworldSimpleCharacterComponent::ClearHistory() const
{
	if (!IsValid(EventHistory))
	{
		UE_LOG(LogInworldCharacter, Error,
			TEXT("UInworldSimpleCharacterComponent::ClearHistory - Failed to clear "
				 "Event History. Event History runtime data is null."));
		return;
	}

	EventHistory->Clear();
}

void UInworldSimpleCharacterComponent::OnDialogueGenerationResult_Implementation(
	const FString& ExecutionId, const FString& NodeId, const FInworldDataHandle& DataHandle)
{
	if (ExecutionId != CurrentMessage.ExecutionId)
	{
		return;
	}

	if (FInworldData_Text* TextData = DataHandle.Unwrap<FInworldData_Text>())
	{
		if (NodeId == "PlayerOut")
		{
			CurrentResponse.Query = TextData->Text;
		}
		if (NodeId == "ResponseOut")
		{
			CurrentResponse.Result = TextData->Text;
		}
		return;
	}

	if (FInworldData_TTSOutput* TTSOutputData = DataHandle.Unwrap<FInworldData_TTSOutput>())
	{
		if (InworldCharacterAudioComponent.IsValid())
		{
			UInworldBlueprintFunctionLibrary::TrimLLMOutput(TTSOutputData->Text, TTSOutputData->Text);
			InworldCharacterAudioComponent->QueueVoice(*TTSOutputData);
		}
		if (TTSResponseCount++ == 0)
		{
			FTimespan ResponseLatency = FDateTime::Now() - CurrentMessage.TimeSent;
			UE_LOG(LogInworldCharacter, Log, TEXT("Response Latency: %f seconds"), ResponseLatency.GetTotalSeconds());
		}

		OnCharacterMessage.Broadcast(this, this, CurrentMessage.MessageId, TTSOutputData->Text);
		OnCharacterMessageNative.Broadcast(this, this, CurrentMessage.MessageId, TTSOutputData->Text);
	}
}

void UInworldSimpleCharacterComponent::OnDialogueGenerationStatus_Implementation(
	const FString& ExecutionId, EInworldGraphExecutionStatus Status)
{
	if (ExecutionId != CurrentMessage.ExecutionId)
	{
		return;
	}

	if (Status == EInworldGraphExecutionStatus::Completed || Status == EInworldGraphExecutionStatus::Failed ||
		Status == EInworldGraphExecutionStatus::Canceled)
	{
		CurrentResponse.MessageId = CurrentMessage.MessageId;
		if (Status == EInworldGraphExecutionStatus::Completed)
		{
			if (FInworldPlayerProfile PlayerProfile;
				CurrentMessage.GetPlayerProfileRuntimeData(PlayerProfileRuntimeDataKey, PlayerProfile))
			{
				AddHistoryEvent({PlayerProfile.Name, CurrentResponse.Query});
			}
			else
			{
				AddHistoryEvent({"Player", CurrentResponse.Query});
			}

			if (CharacterProfile && !CharacterProfile->Get().Name.IsEmpty())
			{
				AddHistoryEvent({CharacterProfile->Get().Name, CurrentResponse.Result});
			}
			else
			{
				AddHistoryEvent({"Character", CurrentResponse.Result});
			}

			OnDialogueResponse.Broadcast(this, CurrentMessage.MessageId, CurrentResponse);
			OnDialogueResponseNative.Broadcast(this, CurrentMessage.MessageId, CurrentResponse);
		}

		CurrentMessage.Reset();
		ClearCurrentResponse();
		TrySendNextMessage();
	}
}

void UInworldSimpleCharacterComponent::GetRuntimeData(TMap<FName, FInworldDataHandle>& OutRuntimeData) const
{
	Super::GetRuntimeData(OutRuntimeData);
	if (CharacterProfile)
	{
		OutRuntimeData.Add(CharacterProfile->GetDataPair());
	}
	if (Voice)
	{
		OutRuntimeData.Add(Voice->GetDataPair());
	}
	if (EventHistory)
	{
		OutRuntimeData.Add(EventHistory->GetDataPair());
	}
	if (Memory)
	{
		OutRuntimeData.Add(Memory->GetDataPair());
	}
}

void UInworldSimpleCharacterComponent::ClearCurrentResponse()
{
	CurrentResponse.Reset();
	TTSResponseCount = 0;
}

void UInworldSimpleCharacterComponent::TrySignalReady()
{
	if (EventHistory != nullptr)
	{
		Super::TrySignalReady();
	}
}

void UInworldSimpleCharacterComponent::Interrupt_Implementation()
{
	Super::Interrupt_Implementation();
	ClearCurrentResponse();
}

TArray<FRuntimeDataInfo> UInworldSimpleCharacterComponent::GetRuntimeDataAndType() const
{
	TArray<FRuntimeDataInfo> DataInfoArray;

	DataInfoArray.Add({EventHistory, UInworldGraphRuntimeData_EventHistory::StaticClass()});
	DataInfoArray.Add({Memory, UInworldGraphRuntimeData_Memory::StaticClass()});

	return DataInfoArray;
}

void UInworldSimpleCharacterComponent::ApplyCharacterData(const FInworldCharacterConfig& Data)
{
	Super::ApplyCharacterData(Data);

	if (CharacterProfile)
	{
		CharacterProfile->Set(Data.CharacterProperties);
	}

	if (Voice)
	{
		Voice->Set(Data.VoiceProperties);
	}

	if (EventHistory)
	{
		EventHistory->Set(Data.InitialHistoryTable);
	}
}

EInworldComponentType UInworldSimpleCharacterComponent::GetComponentType() const
{
	return EInworldComponentType::Simple;
}
