#include "ZeusHudWidget.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 "ZeusAttributesPayload.h"  // FZeusAttributesPayload (ZeusNetwork)
#include "ZeusGASComponent.h"        // UZeusGASComponent (ZeusGAS)
#include "ZeusHudHpSpWidget.h"
#include "ZeusHudPlayerVitalsWidget.h"
#include "ZeusJobsLibrary.h"
#include "ZeusPlayerState.h"

UZeusHudWidget::UZeusHudWidget(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> UZeusHudWidget::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 UZeusHudWidget::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("ZeusHudWidget activated (HpSpBar=%s)"),
		HpSpBar ? *HpSpBar->GetName() : TEXT("none"));
}

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

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

void UZeusHudWidget::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.
		UE_LOG(LogZMMO, Log, TEXT("ZeusHudWidget: PlayerState ausente no activate (esperado se ativou antes do replicar)"));
		return;
	}
	UZeusGASComponent* Comp = PS->FindComponentByClass<UZeusGASComponent>();
	if (!Comp)
	{
		UE_LOG(LogZMMO, Warning, TEXT("ZeusHudWidget::BindToLocalPlayer: UZeusGASComponent NAO encontrado em PS=%s"),
			*PS->GetName());
		return;
	}
	if (Comp == BoundComponent.Get()) { return; }  // idempotente

	UnbindFromComponent();
	BoundComponent = Comp;

	// Raw e' non-dynamic (FZeusAttributesPayload nao e' BlueprintType) — AddUObject.
	SnapshotRawHandle = Comp->OnSnapshotAppliedRaw.AddUObject(this, &UZeusHudWidget::HandleSnapshotRaw);
	Comp->OnHpSpUpdated.AddDynamic(this, &UZeusHudWidget::HandleHpSpUpdated);
	Comp->OnLevelUp.AddDynamic(this, &UZeusHudWidget::HandleLevelUp);

	UE_LOG(LogZMMO, Log, TEXT("ZeusHudWidget::BindToLocalPlayer: bound to %s (HasSnapshot=%d, PlayerVitals=%s, HpSpBar=%s)"),
		*Comp->GetName(), Comp->HasSnapshot() ? 1 : 0,
		PlayerVitals ? *PlayerVitals->GetName() : TEXT("nullptr"),
		HpSpBar ? *HpSpBar->GetName() : TEXT("nullptr"));

	// Refresh imediato — caso o snapshot ja' tenha chegado antes do bind.
	if (Comp->HasSnapshot())
	{
		HandleSnapshotRaw(Comp->GetLastSnapshot());
	}
}

void UZeusHudWidget::UnbindFromComponent()
{
	if (UZeusGASComponent* Old = BoundComponent.Get())
	{
		if (SnapshotRawHandle.IsValid())
		{
			Old->OnSnapshotAppliedRaw.Remove(SnapshotRawHandle);
			SnapshotRawHandle.Reset();
		}
		Old->OnHpSpUpdated.RemoveDynamic(this, &UZeusHudWidget::HandleHpSpUpdated);
		Old->OnLevelUp.RemoveDynamic(this, &UZeusHudWidget::HandleLevelUp);
	}
	BoundComponent.Reset();
}

void UZeusHudWidget::HandleSnapshotRaw(const FZeusAttributesPayload& Payload)
{
	const FZeusAttributesSnapshot Snapshot = FZeusAttributesSnapshot::FromPayload(Payload);

	UE_LOG(LogZMMO, Log, TEXT("ZeusHudWidget::HandleSnapshotRaw: entity=%lld level=%d hp=%d/%d (PlayerVitals=%s)"),
		Snapshot.EntityId, Snapshot.BaseLevel, Snapshot.Hp, Snapshot.MaxHp,
		PlayerVitals ? TEXT("ok") : TEXT("NULLPTR"));

	if (HpSpBar)
	{
		HpSpBar->ApplySnapshot(Snapshot);
	}
	if (PlayerVitals)
	{
		PlayerVitals->ApplySnapshot(Snapshot);

		// Identidade: nome vem do PlayerState; job vem do JobsSubsystem.
		if (UZeusGASComponent* Comp = BoundComponent.Get())
		{
			if (const AZeusPlayerState* PS = Cast<AZeusPlayerState>(Comp->GetOwner()))
			{
				PlayerVitals->SetCharName(PS->CharName);
			}
		}
		PlayerVitals->SetClassName(
			UZeusJobsLibrary::GetJobDisplayName(this, Snapshot.ClassId).ToString());
	}
	// Futuros: Buffs->RefreshIcons, etc.
}

void UZeusHudWidget::HandleHpSpUpdated(float Hp, float Sp)
{
	UZeusGASComponent* Comp = BoundComponent.Get();
	if (!Comp || !Comp->HasSnapshot()) { return; }

	const FZeusAttributesPayload& P = Comp->GetLastSnapshot();
	const int32 HpInt = static_cast<int32>(Hp);
	const int32 SpInt = static_cast<int32>(Sp);
	if (HpSpBar)
	{
		HpSpBar->ApplyHpSp(HpInt, P.MaxHp, SpInt, P.MaxSp);
	}
	if (PlayerVitals)
	{
		PlayerVitals->ApplyHpSp(HpInt, P.MaxHp, SpInt, P.MaxSp);
	}
}

void UZeusHudWidget::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 UZeusHudWidget::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, &UZeusHudWidget::HideLevelUpToast),
			LevelUpToastDurationSec, /*loop*/ false);
	}
}

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