#include "UIPlayerStatus_Window.h"

#include "CommonInputTypeEnum.h"
#include "Components/Button.h"
#include "Components/HorizontalBox.h"
#include "Components/TextBlock.h"
#include "InputCoreTypes.h"
#include "Engine/World.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/PlayerState.h"
#include "Internationalization/Text.h"
#include "ZMMO.h"
#include "ZMMOAttributeComponent.h"
#include "ZMMOJobDataAsset.h"
#include "ZMMOJobsSubsystem.h"
#include "ZMMOPlayerState.h"
#include "UI/InGame/UIInGameFlowSubsystem.h"
#include "UI/InGameTypes.h"
#include "UI/Widgets/UIPlayerStatus_BonusRow.h"
#include "UI/Widgets/UIPlayerStatus_StatRow.h"

UUIPlayerStatus_Window::UUIPlayerStatus_Window(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	// bIsBackHandler exige DefaultBackAction configurada no CommonUISettings;
	// projeto ainda não tem DT de input actions, então registrar o handler
	// poluía o log com "Cannot create action binding". Fechamento da janela
	// vai por Alt+A (NativeOnKeyDown) e Button_Close.
	bAutoActivate = true;
	// Focusable + DesiredFocusTarget=self → recebe NativeOnKeyDown (Alt+A).
	SetIsFocusable(true);
}

UWidget* UUIPlayerStatus_Window::NativeGetDesiredFocusTarget() const
{
	return const_cast<UUIPlayerStatus_Window*>(this);
}

TOptional<FUIInputConfig> UUIPlayerStatus_Window::GetDesiredInputConfig() const
{
	return FUIInputConfig(ECommonInputMode::Menu, EMouseCaptureMode::NoCapture);
}

void UUIPlayerStatus_Window::NativeOnActivated()
{
	Super::NativeOnActivated();

	BindRow(Row_Str, 0, TEXT("S"), TEXT("STR"));
	BindRow(Row_Agi, 1, TEXT("A"), TEXT("AGI"));
	BindRow(Row_Vit, 2, TEXT("V"), TEXT("VIT"));
	BindRow(Row_Int, 3, TEXT("I"), TEXT("INT"));
	BindRow(Row_Dex, 4, TEXT("D"), TEXT("DEX"));
	BindRow(Row_Luk, 5, TEXT("L"), TEXT("LUK"));

	if (Row_Atk)  Row_Atk->SetStatName(FText::FromString(TEXT("ATK")));
	if (Row_Matk) Row_Matk->SetStatName(FText::FromString(TEXT("MATK")));
	if (Row_Def)  Row_Def->SetStatName(FText::FromString(TEXT("DEF")));
	if (Row_Mdef) Row_Mdef->SetStatName(FText::FromString(TEXT("MDEF")));
	if (Row_Hit)  Row_Hit->SetStatName(FText::FromString(TEXT("HIT")));
	if (Row_Flee) Row_Flee->SetStatName(FText::FromString(TEXT("FLEE")));
	if (Row_Crit) Row_Crit->SetStatName(FText::FromString(TEXT("CRIT")));
	if (Row_Aspd) Row_Aspd->SetStatName(FText::FromString(TEXT("ASPD")));

	if (TitleText) TitleText->SetText(FText::FromString(TEXT("Status")));

	if (Button_Close) Button_Close->OnClicked.AddDynamic(this, &UUIPlayerStatus_Window::HandleCloseClicked);

	// Background_Panel default é SELF_HIT_TEST_INVISIBLE; precisa Visible
	// pra capturar clicks em areas vazias do modal e bubbla pra Window.
	if (Background_Panel)
	{
		Background_Panel->SetVisibility(ESlateVisibility::Visible);
	}

	BindToLocalPlayer();

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

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

	auto UnbindRow = [this](UUIPlayerStatus_StatRow* Row)
	{
		if (Row) Row->OnPlusClicked.RemoveDynamic(this, &UUIPlayerStatus_Window::HandleRowPlusClicked);
	};
	UnbindRow(Row_Str); UnbindRow(Row_Agi); UnbindRow(Row_Vit);
	UnbindRow(Row_Int); UnbindRow(Row_Dex); UnbindRow(Row_Luk);

	if (Button_Close) Button_Close->OnClicked.RemoveDynamic(this, &UUIPlayerStatus_Window::HandleCloseClicked);

	Super::NativeOnDeactivated();
}

void UUIPlayerStatus_Window::BindRow(UUIPlayerStatus_StatRow* Row, int32 StatId, const TCHAR* IconLetter, const TCHAR* DisplayName)
{
	if (!Row) return;
	Row->SetupStat(StatId, FText::FromString(IconLetter), FText::FromString(DisplayName));
	Row->OnPlusClicked.AddDynamic(this, &UUIPlayerStatus_Window::HandleRowPlusClicked);
}

void UUIPlayerStatus_Window::BindToLocalPlayer()
{
	UWorld* World = GetWorld();
	if (!World) return;
	APlayerController* PC = World->GetFirstPlayerController();
	if (!PC || !PC->PlayerState) return;

	// CharName vem do AZMMOPlayerState (setado pelo S_SPAWN_PLAYER no spawn
	// local). Fallback pra APlayerState::GetPlayerName() se o cast falhar ou
	// se CharName ainda nao chegou (race com handler que dispara o widget).
	if (Text_CharName)
	{
		FString DisplayName = PC->PlayerState->GetPlayerName();
		if (const AZMMOPlayerState* ZMMOPs = Cast<AZMMOPlayerState>(PC->PlayerState))
		{
			if (!ZMMOPs->CharName.IsEmpty())
			{
				DisplayName = ZMMOPs->CharName;
			}
		}
		Text_CharName->SetText(FText::FromString(DisplayName));
	}

	UZMMOAttributeComponent* Comp = PC->PlayerState->FindComponentByClass<UZMMOAttributeComponent>();
	if (!Comp || Comp == BoundComponent.Get()) return;

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

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

void UUIPlayerStatus_Window::HandleAttributesChanged(const FZMMOAttributesSnapshot& Snapshot)
{
	bPlusButtonsLocked = false;
	RefreshFromSnapshot(Snapshot);
}

void UUIPlayerStatus_Window::HandleStatAllocReply(bool bAccepted, int32 Reason)
{
	UE_LOG(LogZMMO, Log, TEXT("UIPlayerStatus_Window: alloc reply accepted=%d reason=%d"),
		bAccepted ? 1 : 0, Reason);

	bPlusButtonsLocked = false;
	if (UZMMOAttributeComponent* Comp = BoundComponent.Get())
	{
		const FZMMOAttributesSnapshot& S = Comp->GetSnapshot();
		SetAllPrimaryRowsPlusEnabled(S.StatusPoint > 0);
	}
}

void UUIPlayerStatus_Window::HandleRowPlusClicked(int32 StatId)
{
	if (bPlusButtonsLocked) return;
	UZMMOAttributeComponent* Comp = BoundComponent.Get();
	if (!Comp) return;

	bPlusButtonsLocked = true;
	SetAllPrimaryRowsPlusEnabled(false);

	Comp->RequestStatAlloc(StatId, /*Amount*/ 1);
}

void UUIPlayerStatus_Window::SetAllPrimaryRowsPlusEnabled(bool bEnabled)
{
	auto Set = [bEnabled](UUIPlayerStatus_StatRow* Row)
	{
		if (Row) Row->SetPlusEnabled(bEnabled);
	};
	Set(Row_Str); Set(Row_Agi); Set(Row_Vit);
	Set(Row_Int); Set(Row_Dex); Set(Row_Luk);
}

void UUIPlayerStatus_Window::RefreshFromSnapshot(const FZMMOAttributesSnapshot& S)
{
	// Primários: snapshot atual não splita base/bonus de primários (só derivados).
	if (Row_Str) Row_Str->SetValues(S.Str, 0);
	if (Row_Agi) Row_Agi->SetValues(S.Agi, 0);
	if (Row_Vit) Row_Vit->SetValues(S.Vit, 0);
	if (Row_Int) Row_Int->SetValues(S.Int, 0);
	if (Row_Dex) Row_Dex->SetValues(S.Dex, 0);
	if (Row_Luk) Row_Luk->SetValues(S.Luk, 0);

	// Header (level/job).
	if (Text_BaseLevel_Value) Text_BaseLevel_Value->SetText(FText::AsNumber(S.BaseLevel));
	if (Text_JobLevel_Value)  Text_JobLevel_Value->SetText(FText::AsNumber(S.JobLevel));
	if (Text_Job)
	{
		FText JobName;
		if (UGameInstance* GI = GetGameInstance())
		{
			if (const UZMMOJobsSubsystem* Jobs = GI->GetSubsystem<UZMMOJobsSubsystem>())
			{
				if (const UZMMOJobDataAsset* Data = Jobs->GetJobData(S.ClassId))
				{
					JobName = Data->DisplayName;
				}
			}
		}
		if (JobName.IsEmpty())
		{
			JobName = FText::FromString(FString::Printf(TEXT("Job %d"), S.ClassId));
		}
		Text_Job->SetText(JobName);
	}

	if (StatusPointText)
	{
		StatusPointText->SetText(FText::FromString(
			FString::Printf(TEXT("Status Points: %d"), S.StatusPoint)));
	}

	// Derivados (server-authoritative: base + equip).
	if (Row_Atk)  Row_Atk->SetValues(S.AtkBase, S.AtkEquipBonus);
	if (Row_Matk) Row_Matk->SetRangeValues(S.MatkBaseMin, S.MatkBaseMax, S.MatkEquipBonus);
	if (Row_Def)  Row_Def->SetValues(S.DefBase, S.DefEquipBonus);
	if (Row_Mdef) Row_Mdef->SetValues(S.MdefBase, S.MdefEquipBonus);
	if (Row_Hit)  Row_Hit->SetValues(S.HitBase, S.HitEquipBonus);
	if (Row_Flee) Row_Flee->SetValues(S.FleeBase, S.FleeEquipBonus);
	if (Row_Crit) Row_Crit->SetValuesX10(S.CritBaseX10, S.CritEquipBonusX10);
	// ASPD não splita.
	if (Row_Aspd) Row_Aspd->SetValues(S.Aspd, 0);

	const bool bCanAlloc = !bPlusButtonsLocked && S.StatusPoint > 0;
	SetAllPrimaryRowsPlusEnabled(bCanAlloc);
}

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

FReply UUIPlayerStatus_Window::NativeOnKeyDown(const FGeometry& Geom, const FKeyEvent& KeyEvent)
{
	// Alt+A em modo Menu — PlayerController nao recebe (CommonUI absorve).
	// Handle aqui pra completar o toggle.
	if (KeyEvent.GetKey() == EKeys::A && KeyEvent.IsAltDown())
	{
		if (UGameInstance* GI = GetGameInstance())
		{
			if (UUIInGameFlowSubsystem* Flow = GI->GetSubsystem<UUIInGameFlowSubsystem>())
			{
				Flow->SetState(EZMMOInGameUIState::Playing);
				return FReply::Handled();
			}
		}
	}
	return Super::NativeOnKeyDown(Geom, KeyEvent);
}

bool UUIPlayerStatus_Window::IsDragHandleHit(const FPointerEvent& MouseEvent) const
{
	// Drag em qualquer parte do modal EXCETO Container_Stats (rows + botões +STR).
	// Botão close consome o evento antes (UButton.OnClicked) e não bubbla aqui.
	const FVector2D Pos = MouseEvent.GetScreenSpacePosition();
	if (Container_Stats && Container_Stats->GetCachedGeometry().IsUnderLocation(Pos))
	{
		return false;
	}
	return true;
}
