#include "ZMMOStatusWindowWidget.h"

#include "CommonInputTypeEnum.h"
#include "Components/Button.h"
#include "Components/TextBlock.h"
#include "Engine/World.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/PlayerState.h"
#include "Internationalization/Text.h"
#include "ZMMO.h"
#include "ZMMOAttributeComponent.h"
#include "UI/InGame/UIInGameFlowSubsystem.h"
#include "UI/InGameTypes.h"

UZMMOStatusWindowWidget::UZMMOStatusWindowWidget(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	bIsBackHandler = true;   // Esc/B fecha
	bAutoActivate = true;
}

TOptional<FUIInputConfig> UZMMOStatusWindowWidget::GetDesiredInputConfig() const
{
	// Modo Menu: cursor visivel, input flui pra UI (clicks nos botões),
	// movimento bloqueado. Padrao pra menus modais.
	return FUIInputConfig(ECommonInputMode::Menu, EMouseCaptureMode::NoCapture);
}

void UZMMOStatusWindowWidget::NativeOnActivated()
{
	Super::NativeOnActivated();
	BindToLocalPlayer();

	if (PlusBtn_Str) PlusBtn_Str->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusStr);
	if (PlusBtn_Agi) PlusBtn_Agi->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusAgi);
	if (PlusBtn_Vit) PlusBtn_Vit->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusVit);
	if (PlusBtn_Int) PlusBtn_Int->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusInt);
	if (PlusBtn_Dex) PlusBtn_Dex->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusDex);
	if (PlusBtn_Luk) PlusBtn_Luk->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusLuk);
	if (CloseBtn)    CloseBtn->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnCloseClicked);

	UE_LOG(LogZMMO, Log, TEXT("ZMMOStatusWindow activated"));
}

void UZMMOStatusWindowWidget::NativeOnDeactivated()
{
	UnbindFromComponent();

	if (PlusBtn_Str) PlusBtn_Str->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusStr);
	if (PlusBtn_Agi) PlusBtn_Agi->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusAgi);
	if (PlusBtn_Vit) PlusBtn_Vit->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusVit);
	if (PlusBtn_Int) PlusBtn_Int->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusInt);
	if (PlusBtn_Dex) PlusBtn_Dex->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusDex);
	if (PlusBtn_Luk) PlusBtn_Luk->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusLuk);
	if (CloseBtn)    CloseBtn->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnCloseClicked);

	Super::NativeOnDeactivated();
}

void UZMMOStatusWindowWidget::BindToLocalPlayer()
{
	UWorld* World = GetWorld();
	if (!World) { return; }
	APlayerController* PC = World->GetFirstPlayerController();
	if (!PC || !PC->PlayerState) { return; }
	UZMMOAttributeComponent* Comp = PC->PlayerState->FindComponentByClass<UZMMOAttributeComponent>();
	if (!Comp || Comp == BoundComponent.Get()) { return; }

	UnbindFromComponent();
	BoundComponent = Comp;
	Comp->OnAttributesChanged.AddDynamic(this, &UZMMOStatusWindowWidget::HandleAttributesChanged);
	Comp->OnStatAllocReply.AddDynamic(this, &UZMMOStatusWindowWidget::HandleStatAllocReply);
	RefreshFromSnapshot(Comp->GetSnapshot());
}

void UZMMOStatusWindowWidget::UnbindFromComponent()
{
	if (UZMMOAttributeComponent* Old = BoundComponent.Get())
	{
		Old->OnAttributesChanged.RemoveDynamic(this, &UZMMOStatusWindowWidget::HandleAttributesChanged);
		Old->OnStatAllocReply.RemoveDynamic(this, &UZMMOStatusWindowWidget::HandleStatAllocReply);
	}
	BoundComponent.Reset();
}

void UZMMOStatusWindowWidget::HandleAttributesChanged(const FZMMOAttributesSnapshot& Snapshot)
{
	RefreshFromSnapshot(Snapshot);
}

void UZMMOStatusWindowWidget::HandleStatAllocReply(bool bAccepted, int32 Reason)
{
	UE_LOG(LogZMMO, Log, TEXT("StatusWindow: alloc reply accepted=%d reason=%d"),
		bAccepted ? 1 : 0, Reason);
	// Snapshot subsequente vai disparar HandleAttributesChanged e atualizar UI.
	// Aqui poderia popup de erro visual quando bAccepted=false (TODO polish).
}

void UZMMOStatusWindowWidget::RefreshFromSnapshot(const FZMMOAttributesSnapshot& S)
{
	if (StrText) StrText->SetText(FText::FromString(FString::Printf(TEXT("STR  %d"), S.Str)));
	if (AgiText) AgiText->SetText(FText::FromString(FString::Printf(TEXT("AGI  %d"), S.Agi)));
	if (VitText) VitText->SetText(FText::FromString(FString::Printf(TEXT("VIT  %d"), S.Vit)));
	if (IntText) IntText->SetText(FText::FromString(FString::Printf(TEXT("INT  %d"), S.Int)));
	if (DexText) DexText->SetText(FText::FromString(FString::Printf(TEXT("DEX  %d"), S.Dex)));
	if (LukText) LukText->SetText(FText::FromString(FString::Printf(TEXT("LUK  %d"), S.Luk)));
	if (StatusPointText)
	{
		StatusPointText->SetText(FText::FromString(
			FString::Printf(TEXT("Status Points: %d"), S.StatusPoint)));
	}

	// Derivados — formato RO "STAT  base + equip" (ex.: "ATK  51 + 0").
	// Servidor manda os dois separados; cliente NUNCA recalcula.
	if (AtkText)  AtkText->SetText(FText::FromString(FString::Printf(TEXT("ATK  %d + %d"),  S.AtkBase,  S.AtkEquipBonus)));
	if (MatkText) MatkText->SetText(FText::FromString(FString::Printf(TEXT("MATK  %d + %d"), S.MatkBase, S.MatkEquipBonus)));
	if (DefText)  DefText->SetText(FText::FromString(FString::Printf(TEXT("DEF  %d + %d"),  S.DefBase,  S.DefEquipBonus)));
	if (MdefText) MdefText->SetText(FText::FromString(FString::Printf(TEXT("MDEF  %d + %d"), S.MdefBase, S.MdefEquipBonus)));
	if (HitText)  HitText->SetText(FText::FromString(FString::Printf(TEXT("HIT  %d + %d"),  S.HitBase,  S.HitEquipBonus)));
	if (FleeText) FleeText->SetText(FText::FromString(FString::Printf(TEXT("FLEE  %d + %d"), S.FleeBase, S.FleeEquipBonus)));
	if (CritText)
	{
		// CRIT vem ×10 em ambas as camadas — soma X10 antes de converter pra
		// decimal (10 = 1.0). Display: "CRIT  base + equip" com 1 casa cada.
		const int32 BaseInt   = S.CritBaseX10 / 10;
		const int32 BaseFrac  = S.CritBaseX10 % 10;
		const int32 EquipInt  = S.CritEquipBonusX10 / 10;
		const int32 EquipFrac = S.CritEquipBonusX10 % 10;
		CritText->SetText(FText::FromString(FString::Printf(
			TEXT("CRIT  %d.%d + %d.%d"), BaseInt, BaseFrac, EquipInt, EquipFrac)));
	}
	// ASPD nao splita (calculo nao-linear server-side via aspd_base × stats).
	if (AspdText) AspdText->SetText(FText::FromString(FString::Printf(TEXT("ASPD  %d"), S.Aspd)));
	// Habilita botões `+` somente se ha status_point disponível.
	const bool bCanAlloc = S.StatusPoint > 0;
	if (PlusBtn_Str) PlusBtn_Str->SetIsEnabled(bCanAlloc);
	if (PlusBtn_Agi) PlusBtn_Agi->SetIsEnabled(bCanAlloc);
	if (PlusBtn_Vit) PlusBtn_Vit->SetIsEnabled(bCanAlloc);
	if (PlusBtn_Int) PlusBtn_Int->SetIsEnabled(bCanAlloc);
	if (PlusBtn_Dex) PlusBtn_Dex->SetIsEnabled(bCanAlloc);
	if (PlusBtn_Luk) PlusBtn_Luk->SetIsEnabled(bCanAlloc);
}

void UZMMOStatusWindowWidget::RequestAlloc(int32 StatId)
{
	if (UZMMOAttributeComponent* Comp = BoundComponent.Get())
	{
		Comp->RequestStatAlloc(StatId, /*Amount*/ 1);
	}
}

void UZMMOStatusWindowWidget::OnPlusStr() { RequestAlloc(0); }
void UZMMOStatusWindowWidget::OnPlusAgi() { RequestAlloc(1); }
void UZMMOStatusWindowWidget::OnPlusVit() { RequestAlloc(2); }
void UZMMOStatusWindowWidget::OnPlusInt() { RequestAlloc(3); }
void UZMMOStatusWindowWidget::OnPlusDex() { RequestAlloc(4); }
void UZMMOStatusWindowWidget::OnPlusLuk() { RequestAlloc(5); }

void UZMMOStatusWindowWidget::OnCloseClicked()
{
	if (UGameInstance* GI = GetGameInstance())
	{
		if (UUIInGameFlowSubsystem* Flow = GI->GetSubsystem<UUIInGameFlowSubsystem>())
		{
			Flow->SetState(EZMMOInGameUIState::Playing);
		}
	}
}
