#include "ZMMOHudWidget.h"

#include "Components/TextBlock.h"
#include "Components/Widget.h"
#include "CommonInputTypeEnum.h"  // ECommonInputMode
#include "Engine/World.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/PlayerState.h"
#include "TimerManager.h"
#include "ZMMO.h"
#include "ZMMOAttributeComponent.h"
#include "ZMMOHudHpSpWidget.h"
#include "ZMMOHudPlayerVitalsWidget.h"
#include "ZMMOJobsLibrary.h"
#include "ZMMOPlayerState.h"

UZMMOHudWidget::UZMMOHudWidget(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	// HUD nao bloqueia "back" (Esc / B no controller). Menus reais (StatusWindow,
	// EscapeMenu) sim — eles setam bIsBackHandler=true.
	bIsBackHandler = false;
	// HUD nao reativa stack ao virar topo (e' sempre fundo). Garante que o
	// UCommonActivatableWidgetStack do Layer.Game nao force activate/deactivate.
	bAutoActivate = true;
}

TOptional<FUIInputConfig> UZMMOHudWidget::GetDesiredInputConfig() const
{
	// Modo Game: input flui pro PlayerController/Pawn, mouse capturado (3a pessoa).
	// Sem isso, CommonUI assume Menu config (mostra cursor + bloqueia movimento).
	return FUIInputConfig(ECommonInputMode::Game, EMouseCaptureMode::CapturePermanently);
}

void UZMMOHudWidget::NativeOnActivated()
{
	Super::NativeOnActivated();
	// HUD nao intercepta cliques — mouse passa direto pro Pawn (camera, etc.).
	// Sub-widgets que precisam de click (futuro: hotkeys) podem override.
	SetVisibility(ESlateVisibility::HitTestInvisible);
	// Toast comeca escondido — so' aparece em HandleLevelUp.
	HideLevelUpToast();
	BindToLocalPlayer();
	UE_LOG(LogZMMO, Log, TEXT("ZMMOHudWidget activated (HpSpBar=%s)"),
		HpSpBar ? *HpSpBar->GetName() : TEXT("none"));
}

void UZMMOHudWidget::NativeOnDeactivated()
{
	UnbindFromComponent();
	Super::NativeOnDeactivated();
}

void UZMMOHudWidget::NativeDestruct()
{
	UnbindFromComponent();
	Super::NativeDestruct();
}

void UZMMOHudWidget::BindToLocalPlayer()
{
	UWorld* World = GetWorld();
	if (!World) { return; }

	APlayerController* PC = World->GetFirstPlayerController();
	if (!PC) { return; }
	APlayerState* PS = PC->PlayerState;
	if (!PS)
	{
		// PlayerState pode nao existir ainda — race com GameMode setup.
		// AZMMOPlayerCharacter::HandleLocalSpawnReady chama BindToAttributeComponent
		// manualmente como fallback (seed do EntityId).
		UE_LOG(LogZMMO, Log, TEXT("ZMMOHudWidget: PlayerState ausente no activate (race fix via HandleLocalSpawnReady esperado)"));
		return;
	}
	UZMMOAttributeComponent* Comp = PS->FindComponentByClass<UZMMOAttributeComponent>();
	if (!Comp) { return; }
	if (Comp == BoundComponent.Get()) { return; }  // idempotente

	UnbindFromComponent();
	BoundComponent = Comp;

	Comp->OnAttributesChanged.AddDynamic(this, &UZMMOHudWidget::HandleAttributesChanged);
	Comp->OnHpSpChanged.AddDynamic(this, &UZMMOHudWidget::HandleHpSpChanged);
	Comp->OnLevelUp.AddDynamic(this, &UZMMOHudWidget::HandleLevelUp);

	// Refresh imediato — caso o snapshot ja' tenha chegado.
	HandleAttributesChanged(Comp->GetSnapshot());
}

void UZMMOHudWidget::UnbindFromComponent()
{
	if (UZMMOAttributeComponent* Old = BoundComponent.Get())
	{
		Old->OnAttributesChanged.RemoveDynamic(this, &UZMMOHudWidget::HandleAttributesChanged);
		Old->OnHpSpChanged.RemoveDynamic(this, &UZMMOHudWidget::HandleHpSpChanged);
		Old->OnLevelUp.RemoveDynamic(this, &UZMMOHudWidget::HandleLevelUp);
	}
	BoundComponent.Reset();
}

void UZMMOHudWidget::HandleAttributesChanged(const FZMMOAttributesSnapshot& Snapshot)
{
	// Propaga snapshot aos sub-widgets que ja existem.
	if (HpSpBar)
	{
		HpSpBar->ApplySnapshot(Snapshot);
	}
	if (PlayerVitals)
	{
		PlayerVitals->ApplySnapshot(Snapshot);

		// Identidade: nome vem do PlayerState (não está no snapshot); job vem
		// do JobsSubsystem resolvendo ClassId -> display name.
		if (UZMMOAttributeComponent* Comp = BoundComponent.Get())
		{
			if (const AZMMOPlayerState* PS = Cast<AZMMOPlayerState>(Comp->GetOwner()))
			{
				PlayerVitals->SetCharName(PS->CharName);
			}
		}
		PlayerVitals->SetClassName(
			UZMMOJobsLibrary::GetJobDisplayName(this, Snapshot.ClassId).ToString());
	}
	// Futuros: Buffs->RefreshIcons, etc.
}

void UZMMOHudWidget::HandleHpSpChanged(int32 Hp, int32 Sp)
{
	if (UZMMOAttributeComponent* Comp = BoundComponent.Get())
	{
		const FZMMOAttributesSnapshot& Snap = Comp->GetSnapshot();
		if (HpSpBar)
		{
			HpSpBar->ApplyHpSp(Hp, Snap.MaxHp, Sp, Snap.MaxSp);
		}
		if (PlayerVitals)
		{
			PlayerVitals->ApplyHpSp(Hp, Snap.MaxHp, Sp, Snap.MaxSp);
		}
	}
}

void UZMMOHudWidget::HandleLevelUp(int32 NewBaseLevel, int32 StatusPointDelta)
{
	// Snapshot subsequente atualiza HpSpBar (level/maxhp/maxsp). Aqui mostra
	// toast visual instantaneo. StatusPointDelta nao usado no texto (UI mostra
	// no StatusWindow). Polish futuro: SFX + particle aura.
	UE_LOG(LogZMMO, Log, TEXT("[HUD] LEVEL UP! newLevel=%d +sp=%d"), NewBaseLevel, StatusPointDelta);
	ShowLevelUpToast(NewBaseLevel);
}

void UZMMOHudWidget::ShowLevelUpToast(int32 NewBaseLevel)
{
	if (LevelUpToastText)
	{
		LevelUpToastText->SetText(FText::FromString(
			FString::Printf(TEXT("LEVEL UP!  Lv %d"), NewBaseLevel)));
	}
	if (LevelUpToastContainer)
	{
		LevelUpToastContainer->SetVisibility(ESlateVisibility::HitTestInvisible);
	}

	// Cancela timer anterior (caso level up duplo em sequencia) + agenda hide.
	if (UWorld* World = GetWorld())
	{
		World->GetTimerManager().ClearTimer(LevelUpToastHideTimer);
		World->GetTimerManager().SetTimer(LevelUpToastHideTimer,
			FTimerDelegate::CreateUObject(this, &UZMMOHudWidget::HideLevelUpToast),
			LevelUpToastDurationSec, /*loop*/ false);
	}
}

void UZMMOHudWidget::HideLevelUpToast()
{
	if (LevelUpToastContainer)
	{
		LevelUpToastContainer->SetVisibility(ESlateVisibility::Collapsed);
	}
}
