#include "UIInGameFlowSubsystem.h"

#include "CommonActivatableWidget.h"  // Necessario pra TSoftClassPtr<UCommonActivatableWidget>::Get()
#include "Engine/AssetManager.h"
#include "Engine/GameInstance.h"
#include "Engine/StreamableManager.h"
#include "GameFramework/PlayerController.h"
#include "GameplayTagContainer.h"
#include "ZMMO.h"
#include "Data/UI/UILayerTags.h"
#include "FrontEnd/UIFrontEndFlowSubsystem.h"
#include "FrontEnd/UIManagerSubsystem.h"
#include "InGame/UIInGameScreenSet.h"

void UUIInGameFlowSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
	Super::Initialize(Collection);
	UE_LOG(LogZMMO, Log, TEXT("UIInGameFlowSubsystem initialized"));
}

void UUIInGameFlowSubsystem::Deinitialize()
{
	ScreenSet = nullptr;
	CurrentState = EZMMOInGameUIState::None;
	Super::Deinitialize();
}

void UUIInGameFlowSubsystem::StartInGame()
{
	if (CurrentState != EZMMOInGameUIState::None)
	{
		// Idempotente: chamadas extras (ex.: respawn) sao no-op se ja in-world.
		UE_LOG(LogZMMO, Verbose,
			TEXT("InGameFlow: StartInGame ignorado (ja em estado %s)"),
			*UEnum::GetValueAsString(CurrentState));
		return;
	}

	// CRITICAL: OpenLevel invalida widgets no viewport ("InvalidateAllWidgets"),
	// orfanando o RootLayout (UUIPrimaryGameLayout_Base) que foi criado pelo
	// FrontEndFlow no boot. Sem RootLayout no viewport, PushScreenToLayer
	// silenciosamente adiciona widgets ao stack do RootLayout — mas o stack
	// nao e' visivel porque o parent (RootLayout) nao esta no viewport.
	//
	// Re-cria via EnsureRootLayout (idempotente) ANTES de qualquer push.
	if (UGameInstance* GI = GetGameInstance())
	{
		if (UUIFrontEndFlowSubsystem* FE = GI->GetSubsystem<UUIFrontEndFlowSubsystem>())
		{
			FE->EnsureRootLayout();
		}
	}

	// Limpa qualquer residuo do front-end (Menu/Modal podem ter "Loading" ou
	// dialogs orfaos do handoff). Layer.Game vai receber o HUD novo.
	if (UUIManagerSubsystem* Mgr = GetUIManager())
	{
		Mgr->ClearLayer(ZMMOUITags::UI_Layer_Menu.GetTag());
		Mgr->ClearLayer(ZMMOUITags::UI_Layer_Modal.GetTag());
		Mgr->ClearLayer(ZMMOUITags::UI_Layer_GameMenu.GetTag());
	}

	SetState(EZMMOInGameUIState::Playing);
}

void UUIInGameFlowSubsystem::SetState(EZMMOInGameUIState NewState)
{
	if (NewState == CurrentState)
	{
		return;
	}

	const EZMMOInGameUIState OldState = CurrentState;
	CurrentState = NewState;

	UE_LOG(LogZMMO, Log, TEXT("InGameFlow: state %s -> %s"),
		*UEnum::GetValueAsString(OldState),
		*UEnum::GetValueAsString(NewState));

	// Decisao de stack: ao voltar pra `Playing`, fechamos quaisquer overlays
	// (Status/Inventory/Menu) limpando o Layer.GameMenu — HUD em Layer.Game
	// permanece. Para qualquer outro estado, mantemos HUD e empilhamos no
	// layer correspondente.
	const bool bGoingToPlaying = (NewState == EZMMOInGameUIState::Playing);
	if (UUIManagerSubsystem* Mgr = GetUIManager())
	{
		if (bGoingToPlaying)
		{
			Mgr->ClearLayer(ZMMOUITags::UI_Layer_GameMenu.GetTag());
		}
	}

	// Skip re-push do HUD quando ja' estavamos in-game (Old != None significa
	// que StartInGame ja' rodou e o HUD esta no Layer.Game). Senao, cada vez
	// que o usuario fechar StatusWindow/Inventory/etc, o HUD seria recriado
	// — flicker em todos os textos (HP/SP/etc) por causa do Construct novo.
	const bool bSkipRepush = bGoingToPlaying
		&& OldState != EZMMOInGameUIState::None
		&& OldState != EZMMOInGameUIState::Playing;
	if (!bSkipRepush)
	{
		ResolveAndPushScreen(NewState);
	}
	OnStateChanged.Broadcast(NewState);
}

void UUIInGameFlowSubsystem::ToggleScreen(EZMMOInGameUIState Screen)
{
	if (Screen == EZMMOInGameUIState::Playing || Screen == EZMMOInGameUIState::None)
	{
		// Toggle para Playing nao faz sentido (estado base). Use SetState direto.
		return;
	}
	if (CurrentState == Screen)
	{
		SetState(EZMMOInGameUIState::Playing);
	}
	else
	{
		SetState(Screen);
	}
}

UUIInGameScreenSet* UUIInGameFlowSubsystem::GetScreenSet()
{
	if (ScreenSet) { return ScreenSet; }
	if (ScreenSetAsset.IsNull())
	{
		UE_LOG(LogZMMO, Warning,
			TEXT("InGameFlow: ScreenSetAsset nao configurado em DefaultGame.ini "
				"[/Script/ZMMO.UIInGameFlowSubsystem] ScreenSetAsset=..."));
		return nullptr;
	}
	// Sync load — DA e' pequeno (so' soft refs); PIE/dev OK.
	ScreenSet = ScreenSetAsset.LoadSynchronous();
	return ScreenSet;
}

UUIManagerSubsystem* UUIInGameFlowSubsystem::GetUIManager() const
{
	if (const UGameInstance* GI = GetGameInstance())
	{
		if (ULocalPlayer* LP = GI->GetFirstGamePlayer())
		{
			return LP->GetSubsystem<UUIManagerSubsystem>();
		}
	}
	return nullptr;
}

FGameplayTag UUIInGameFlowSubsystem::LayerForState(EZMMOInGameUIState State) const
{
	switch (State)
	{
		case EZMMOInGameUIState::Playing:
			return ZMMOUITags::UI_Layer_Game.GetTag();
		case EZMMOInGameUIState::Loading:
			return ZMMOUITags::UI_Layer_Modal.GetTag();
		case EZMMOInGameUIState::StatusWindow:
		case EZMMOInGameUIState::Inventory:
		case EZMMOInGameUIState::SkillTree:
		case EZMMOInGameUIState::EscapeMenu:
		default:
			return ZMMOUITags::UI_Layer_GameMenu.GetTag();
	}
}

void UUIInGameFlowSubsystem::ResolveAndPushScreen(EZMMOInGameUIState State)
{
	if (State == EZMMOInGameUIState::None) { return; }

	UUIInGameScreenSet* SS = GetScreenSet();
	if (!SS)
	{
		UE_LOG(LogZMMO, Warning,
			TEXT("InGameFlow: ScreenSet ausente; tela de %s ignorada"),
			*UEnum::GetValueAsString(State));
		return;
	}

	const TSoftClassPtr<UCommonActivatableWidget> Soft = SS->GetScreenForState(State);
	if (Soft.IsNull())
	{
		UE_LOG(LogZMMO, Warning,
			TEXT("InGameFlow: Screen for state %s not configured em DA_InGameScreenSet (verificar StateScreens)"),
			*UEnum::GetValueAsString(State));
		return;
	}

	UUIManagerSubsystem* Mgr = GetUIManager();
	if (!Mgr)
	{
		UE_LOG(LogZMMO, Warning, TEXT("InGameFlow: UIManagerSubsystem ausente"));
		return;
	}

	const FGameplayTag Layer = LayerForState(State);
	const FSoftObjectPath Path = Soft.ToSoftObjectPath();

	UAssetManager::GetStreamableManager().RequestAsyncLoad(
		Path,
		FStreamableDelegate::CreateWeakLambda(this, [this, Soft, Layer, State]()
		{
			UUIManagerSubsystem* M = GetUIManager();
			if (!M) { return; }
			UClass* Cls = Soft.Get();
			if (!Cls)
			{
				UE_LOG(LogZMMO, Warning,
					TEXT("InGameFlow: async load falhou pra %s"),
					*UEnum::GetValueAsString(State));
				return;
			}
			UCommonActivatableWidget* W = M->PushScreenToLayer(Layer, Cls);
			UE_LOG(LogZMMO, Log,
				TEXT("InGameFlow: tela %s pushed em %s (widget=%s)"),
				*UEnum::GetValueAsString(State), *Layer.ToString(),
				W ? *W->GetName() : TEXT("null"));
		}));
}
