#include "UILoadingScreen_Base.h"

#include "Components/ProgressBar.h"
#include "CommonTextBlock.h"
#include "Engine/DataTable.h"
#include "Engine/World.h"
#include "TimerManager.h"
#include "UILoadingProfilesDataAsset.h"
#include "UILoadingTipRow.h"

void UUILoadingScreen_Base::Configure(EZMMOLoadingContext InContext,
                                      UZMMOLoadingProfilesDataAsset* Profiles,
                                      UDataTable* TipsTable)
{
	Context_ = InContext;
	bCompleteFired_ = false;

	// Perfil (etapas + título).
	Profile_ = (Profiles != nullptr)
		? Profiles->GetProfile(InContext)
		: FZMMOLoadingProfile();

	StepStatus_.Reset();
	for (const FZMMOLoadingStepDef& Step : Profile_.Steps)
	{
		StepStatus_.Add(Step.StepId, EZMMOLoadingStepStatus::Pending);
	}

	// Pool de dicas filtrada por contexto (None = qualquer).
	TipPool_.Reset();
	if (TipsTable != nullptr)
	{
		TipsTable->ForeachRow<FZMMOLoadingTipRow>(TEXT("UUILoadingScreen_Base::Configure"),
			[this](const FName& /*Key*/, const FZMMOLoadingTipRow& Row)
			{
				if (Row.Context == EZMMOLoadingContext::None || Row.Context == Context_)
				{
					if (!Row.Tip.IsEmpty())
					{
						TipPool_.Add(Row.Tip);
					}
				}
			});
	}
	NextTipIndex_ = 0;

	RefreshUI();

	if (IsActivated())
	{
		StartTipTimer();
	}
}

void UUILoadingScreen_Base::NativeOnActivated()
{
	Super::NativeOnActivated();
	RefreshUI();
	StartTipTimer();
}

void UUILoadingScreen_Base::NativeOnDeactivated()
{
	StopTipTimer();
	Super::NativeOnDeactivated();
}

void UUILoadingScreen_Base::MarkStepRunning(FName StepId)
{
	if (EZMMOLoadingStepStatus* Status = StepStatus_.Find(StepId))
	{
		// Já Done não regride.
		if (*Status != EZMMOLoadingStepStatus::Done && *Status != EZMMOLoadingStepStatus::Failed)
		{
			*Status = EZMMOLoadingStepStatus::Running;
			RefreshUI();
		}
	}
}

void UUILoadingScreen_Base::MarkStepDone(FName StepId)
{
	if (EZMMOLoadingStepStatus* Status = StepStatus_.Find(StepId))
	{
		*Status = EZMMOLoadingStepStatus::Done;
		RefreshUI();

		if (!bCompleteFired_ && AreAllStepsDone())
		{
			bCompleteFired_ = true;
			OnLoadingComplete.Broadcast();
		}
	}
}

void UUILoadingScreen_Base::MarkStepFailed(FName StepId, const FText& Reason)
{
	if (EZMMOLoadingStepStatus* Status = StepStatus_.Find(StepId))
	{
		*Status = EZMMOLoadingStepStatus::Failed;
		if (Text_Status && !Reason.IsEmpty())
		{
			Text_Status->SetText(Reason);
		}
		RefreshUI();
	}
}

bool UUILoadingScreen_Base::AreAllStepsDone() const
{
	if (Profile_.Steps.Num() == 0)
	{
		return false;
	}
	for (const FZMMOLoadingStepDef& Step : Profile_.Steps)
	{
		const EZMMOLoadingStepStatus* Status = StepStatus_.Find(Step.StepId);
		if (Status == nullptr || *Status != EZMMOLoadingStepStatus::Done)
		{
			return false;
		}
	}
	return true;
}

int32 UUILoadingScreen_Base::FindStepIndex(FName StepId) const
{
	for (int32 i = 0; i < Profile_.Steps.Num(); ++i)
	{
		if (Profile_.Steps[i].StepId == StepId)
		{
			return i;
		}
	}
	return INDEX_NONE;
}

EZMMOLoadingStepStatus UUILoadingScreen_Base::GetStepStatus(int32 Index) const
{
	if (!Profile_.Steps.IsValidIndex(Index)) return EZMMOLoadingStepStatus::Pending;
	const EZMMOLoadingStepStatus* Status = StepStatus_.Find(Profile_.Steps[Index].StepId);
	return Status ? *Status : EZMMOLoadingStepStatus::Pending;
}

FText UUILoadingScreen_Base::ComputeCurrentStatusText() const
{
	// Prioridade: Running > último Done > primeiro Pending > Status default.
	int32 LastDone = INDEX_NONE;
	for (int32 i = 0; i < Profile_.Steps.Num(); ++i)
	{
		const EZMMOLoadingStepStatus St = GetStepStatus(i);
		if (St == EZMMOLoadingStepStatus::Running)
		{
			return Profile_.Steps[i].Label;
		}
		if (St == EZMMOLoadingStepStatus::Done)
		{
			LastDone = i;
		}
	}
	if (LastDone != INDEX_NONE)
	{
		return Profile_.Steps[LastDone].Label;
	}
	if (Profile_.Steps.Num() > 0)
	{
		return Profile_.Steps[0].Label;
	}
	return StatusDefault;
}

float UUILoadingScreen_Base::ComputeProgress01() const
{
	const int32 N = Profile_.Steps.Num();
	if (N == 0) return 0.f;
	float Acc = 0.f;
	for (int32 i = 0; i < N; ++i)
	{
		const EZMMOLoadingStepStatus St = GetStepStatus(i);
		if (St == EZMMOLoadingStepStatus::Done)        Acc += 1.f;
		else if (St == EZMMOLoadingStepStatus::Running) Acc += 0.5f;
	}
	return FMath::Clamp(Acc / static_cast<float>(N), 0.f, 1.f);
}

void UUILoadingScreen_Base::RefreshUI()
{
	if (Text_Title)
	{
		const FText& Title = Profile_.TitleOverride.IsEmpty() ? TitleDefault : Profile_.TitleOverride;
		Text_Title->SetText(Title);
	}
	if (Text_Status)
	{
		Text_Status->SetText(ComputeCurrentStatusText());
	}
	if (ProgressBar_Progress)
	{
		ProgressBar_Progress->SetPercent(ComputeProgress01());
	}
	if (Text_Tip && TipPool_.Num() > 0 && Text_Tip->GetText().IsEmpty())
	{
		// Primeira dica na inicialização (timer cuida das próximas).
		Text_Tip->SetText(TipPool_[0]);
		NextTipIndex_ = TipPool_.Num() > 1 ? 1 : 0;
	}
}

void UUILoadingScreen_Base::RotateTip()
{
	if (!Text_Tip || TipPool_.Num() == 0) return;
	Text_Tip->SetText(TipPool_[NextTipIndex_]);
	NextTipIndex_ = (NextTipIndex_ + 1) % TipPool_.Num();
}

void UUILoadingScreen_Base::StartTipTimer()
{
	StopTipTimer();
	if (TipRotationIntervalSeconds <= 0.f || TipPool_.Num() <= 1) return;
	if (UWorld* W = GetWorld())
	{
		W->GetTimerManager().SetTimer(TipTimerHandle_, this,
			&UUILoadingScreen_Base::RotateTip,
			TipRotationIntervalSeconds, /*bLoop=*/true);
	}
}

void UUILoadingScreen_Base::StopTipTimer()
{
	if (UWorld* W = GetWorld())
	{
		W->GetTimerManager().ClearTimer(TipTimerHandle_);
	}
	TipTimerHandle_.Invalidate();
}
