#include "UIUserLobbyScreen_Base.h"

#include "ZMMO.h"
#include "CharServerOpcodes.h"
#include "UIFrontEndFlowSubsystem.h"
#include "UICharCard_Base.h"
#include "UICharacterCreatePage_Base.h"
#include "WireHelpers.h"
#include "ZeusCharServerSubsystem.h"
#include "ZeusNetworkSubsystem.h"
#include "CommonTextBlock.h"
#include "Components/PanelWidget.h"
#include "Components/WidgetSwitcher.h"
#include "Engine/GameInstance.h"
#include "UI/Widgets/UIButton_Base.h"

using ZMMOWire::ReadU8;
using ZMMOWire::ReadU16;
using ZMMOWire::ReadU32;
using ZMMOWire::ReadU64;
using ZMMOWire::ReadFloat;
using ZMMOWire::ReadStringUtf8;
using ZMMOWire::ReadUuid16;
using ZMMOWire::WriteUuid16;

void UUIUserLobbyScreen_Base::NativeOnActivated()
{
	Super::NativeOnActivated();
	UE_LOG(LogZMMO, Log, TEXT("Lobby: ativada."));

	if (UZeusCharServerSubsystem* Char = GetCharServer())
	{
		if (!bSubscribed)
		{
			Char->OnRawMessage.AddDynamic(this, &UUIUserLobbyScreen_Base::HandleCharRawMessage);
			bSubscribed = true;
		}
	}

	if (!bButtonsBound)
	{
		if (Btn_Back)   Btn_Back->OnClicked().AddUObject(this, &UUIUserLobbyScreen_Base::BackToServerSelect);
		if (Btn_Create) Btn_Create->OnClicked().AddUObject(this, &UUIUserLobbyScreen_Base::ShowCharCreatePage);
		bButtonsBound = true;
	}

	ShowCharSelectPage();
	RequestCharList();
}

void UUIUserLobbyScreen_Base::NativeOnDeactivated()
{
	if (bSubscribed)
	{
		if (UZeusCharServerSubsystem* Char = GetCharServer())
		{
			Char->OnRawMessage.RemoveDynamic(this, &UUIUserLobbyScreen_Base::HandleCharRawMessage);
		}
		bSubscribed = false;
	}
	Super::NativeOnDeactivated();
}

void UUIUserLobbyScreen_Base::RequestCharList()
{
	UZeusCharServerSubsystem* Char = GetCharServer();
	if (!Char || !Char->IsConnected()) return;

	FString WorldId;
	if (UGameInstance* GI = GetGameInstance())
	{
		if (UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem<UUIFrontEndFlowSubsystem>())
		{
			WorldId = Flow->GetSelectedWorldId();
		}
	}

	// Wire: uint8 hasFilter + (uint8[16] worldId se hasFilter==1)
	TArray<uint8> Payload;
	if (WorldId.IsEmpty())
	{
		Payload.Add(0);
	}
	else
	{
		Payload.Add(1);
		if (!WriteUuid16(Payload, WorldId))
		{
			UE_LOG(LogZMMO, Warning, TEXT("Lobby: SelectedWorldId invalido (%s) — pedindo sem filtro"), *WorldId);
			Payload.Reset();
			Payload.Add(0);
		}
	}
	Char->SendCharRequest(ZMMOCharOp::C_CHAR_LIST_REQUEST, Payload);
}

void UUIUserLobbyScreen_Base::ShowCharSelectPage()
{
	if (PageSwitcher) PageSwitcher->SetActiveWidgetIndex(0);
}

void UUIUserLobbyScreen_Base::ShowCharCreatePage()
{
	if (PageSwitcher) PageSwitcher->SetActiveWidgetIndex(1);
}

void UUIUserLobbyScreen_Base::BackToServerSelect()
{
	UGameInstance* GI = GetGameInstance();
	if (!GI) return;
	if (UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem<UUIFrontEndFlowSubsystem>())
	{
		Flow->ClearSelectedWorld();
		Flow->SetState(EZMMOFrontEndState::ServerSelect);
	}
}

void UUIUserLobbyScreen_Base::HandleCharRawMessage(int32 Opcode, const TArray<uint8>& Payload)
{
	switch (Opcode)
	{
	case ZMMOCharOp::S_CHAR_LIST:               ParseCharList(Payload); break;
	case ZMMOCharOp::S_CHAR_SELECT_OK:          HandleCharSelectOk(Payload); break;
	case ZMMOCharOp::S_CHAR_SELECT_REJECT:      HandleCharSelectReject(Payload); break;
	case ZMMOCharOp::S_CHAR_CREATE_OK:          HandleCharCreateOk(Payload); break;
	case ZMMOCharOp::S_CHAR_CREATE_REJECT:      HandleCharCreateReject(Payload); break;
	case ZMMOCharOp::S_CHAR_DELETE_ACK:         HandleCharDeleteAck(Payload); break;
	case ZMMOCharOp::S_CHAR_DELETE_ACCEPT_ACK:  HandleCharDeleteAcceptAck(Payload); break;
	case ZMMOCharOp::S_CHAR_DELETE_CANCEL_ACK:  HandleCharDeleteCancelAck(Payload); break;
	default: break;
	}
}

void UUIUserLobbyScreen_Base::ParseCharList(const TArray<uint8>& Payload)
{
	int32 Pos = 0;
	uint16 Count = 0;
	if (!ReadU16(Payload, Pos, Count))
	{
		UE_LOG(LogZMMO, Warning, TEXT("Lobby: S_CHAR_LIST malformado (sem count)"));
		return;
	}

	TArray<FZMMOCharSummary> New;
	New.Reserve(Count);

	for (uint16 i = 0; i < Count; ++i)
	{
		FZMMOCharSummary E;
		uint64 charId64 = 0;
		uint8 slot = 0;
		uint16 classId = 0;
		uint32 baseLvl = 0, baseExp = 0, jobLvl = 0, jobExp = 0;
		uint16 s = 0, a = 0, v = 0, in_ = 0, d = 0, l = 0;
		uint32 hp = 0, maxHp = 0, sp = 0, maxSp = 0, money = 0;
		uint16 mapId16 = 0;
		float px = 0, py = 0, pz = 0, yaw = 0;
		uint32 hair = 0, hairColor = 0, skinColor = 0;
		uint8 bodyType = 0;
		uint64 deleteAt = 0;

		if (!ReadU64(Payload, Pos, charId64)
			|| !ReadUuid16(Payload, Pos, E.WorldId)
			|| !ReadU8(Payload, Pos, slot)
			|| !ReadStringUtf8(Payload, Pos, E.Name)
			|| !ReadU16(Payload, Pos, classId)
			|| !ReadU32(Payload, Pos, baseLvl)
			|| !ReadU32(Payload, Pos, baseExp)
			|| !ReadU32(Payload, Pos, jobLvl)
			|| !ReadU32(Payload, Pos, jobExp)
			|| !ReadU16(Payload, Pos, s) || !ReadU16(Payload, Pos, a) || !ReadU16(Payload, Pos, v)
			|| !ReadU16(Payload, Pos, in_) || !ReadU16(Payload, Pos, d) || !ReadU16(Payload, Pos, l)
			|| !ReadU32(Payload, Pos, hp) || !ReadU32(Payload, Pos, maxHp)
			|| !ReadU32(Payload, Pos, sp) || !ReadU32(Payload, Pos, maxSp)
			|| !ReadU32(Payload, Pos, money)
			|| !ReadU16(Payload, Pos, mapId16)
			|| !ReadFloat(Payload, Pos, px) || !ReadFloat(Payload, Pos, py) || !ReadFloat(Payload, Pos, pz)
			|| !ReadFloat(Payload, Pos, yaw)
			|| !ReadU32(Payload, Pos, hair) || !ReadU32(Payload, Pos, hairColor) || !ReadU32(Payload, Pos, skinColor)
			|| !ReadU8(Payload, Pos, bodyType)
			|| !ReadU64(Payload, Pos, deleteAt))
		{
			UE_LOG(LogZMMO, Warning, TEXT("Lobby: S_CHAR_LIST malformado (entry %d)"), i);
			return;
		}
		E.CharId = FString::Printf(TEXT("%llu"), static_cast<unsigned long long>(charId64));
		E.Slot = slot; E.ClassId = classId;
		E.BaseLevel = baseLvl; E.BaseExp = static_cast<int32>(baseExp);
		E.JobLevel = jobLvl;   E.JobExp = static_cast<int32>(jobExp);
		E.Stats.Str = s; E.Stats.Agi = a; E.Stats.Vit = v;
		E.Stats.Int = in_; E.Stats.Dex = d; E.Stats.Luk = l;
		E.Hp = hp; E.MaxHp = maxHp; E.Sp = sp; E.MaxSp = maxSp;
		E.Money = static_cast<int32>(money);
		E.MapId = static_cast<int32>(mapId16);
		E.Position = FVector(px, py, pz); E.YawDeg = yaw;
		E.Appearance.Hair = hair; E.Appearance.HairColor = hairColor;
		E.Appearance.SkinColor = skinColor; E.Appearance.BodyType = bodyType;
		E.DeleteScheduledAtMs = static_cast<int64>(deleteAt);
		New.Add(MoveTemp(E));
	}

	Chars = MoveTemp(New);
	UE_LOG(LogZMMO, Log, TEXT("Lobby: recebidos %d chars"), Chars.Num());
	RebuildCards();
	OnCharListReceived();
}

void UUIUserLobbyScreen_Base::RebuildCards()
{
	if (!CardContainer || !CharCardClass) return;

	for (UUICharCard_Base* Old : SpawnedCards)
	{
		if (Old)
		{
			Old->OnCardSelected.RemoveDynamic(this, &UUIUserLobbyScreen_Base::HandleCardSelected);
			Old->OnCardDeleteRequest.RemoveDynamic(this, &UUIUserLobbyScreen_Base::HandleCardDeleteRequest);
			Old->OnCardDeleteAccept.RemoveDynamic(this, &UUIUserLobbyScreen_Base::HandleCardAcceptDeleteRequest);
			Old->OnCardDeleteCancel.RemoveDynamic(this, &UUIUserLobbyScreen_Base::HandleCardCancelDeleteRequest);
		}
	}
	SpawnedCards.Reset();
	CardContainer->ClearChildren();

	for (const FZMMOCharSummary& C : Chars)
	{
		UUICharCard_Base* Card = CreateWidget<UUICharCard_Base>(this, CharCardClass);
		if (!Card) continue;
		Card->SetFromSummary(C);
		Card->OnCardSelected.AddDynamic(this, &UUIUserLobbyScreen_Base::HandleCardSelected);
		Card->OnCardDeleteRequest.AddDynamic(this, &UUIUserLobbyScreen_Base::HandleCardDeleteRequest);
		Card->OnCardDeleteAccept.AddDynamic(this, &UUIUserLobbyScreen_Base::HandleCardAcceptDeleteRequest);
		Card->OnCardDeleteCancel.AddDynamic(this, &UUIUserLobbyScreen_Base::HandleCardCancelDeleteRequest);
		CardContainer->AddChild(Card);
		SpawnedCards.Add(Card);
	}
}

void UUIUserLobbyScreen_Base::HandleCardSelected(FString CharId)
{
	UE_LOG(LogZMMO, Log, TEXT("Lobby: char %s selecionado — enviando C_CHAR_SELECT"), *CharId);
	UZeusCharServerSubsystem* Char = GetCharServer();
	if (!Char) return;
	uint64 CharId64 = 0;
	LexFromString(CharId64, *CharId);
	// Wire: uint64 charId
	TArray<uint8> Payload;
	for (int32 i = 0; i < 8; ++i) Payload.Add(static_cast<uint8>((CharId64 >> (8 * i)) & 0xFF));
	Char->SendCharRequest(ZMMOCharOp::C_CHAR_SELECT, Payload);
}

void UUIUserLobbyScreen_Base::HandleCardDeleteRequest(FString CharId)
{
	UE_LOG(LogZMMO, Log, TEXT("Lobby: DELETE_REQUEST char %s"), *CharId);
	SendCharIdOpcode(ZMMOCharOp::C_CHAR_DELETE_REQUEST, CharId);
}

void UUIUserLobbyScreen_Base::HandleCardAcceptDeleteRequest(FString CharId)
{
	UE_LOG(LogZMMO, Log, TEXT("Lobby: DELETE_ACCEPT char %s"), *CharId);
	SendCharIdOpcode(ZMMOCharOp::C_CHAR_DELETE_ACCEPT, CharId);
}

void UUIUserLobbyScreen_Base::HandleCardCancelDeleteRequest(FString CharId)
{
	UE_LOG(LogZMMO, Log, TEXT("Lobby: DELETE_CANCEL char %s"), *CharId);
	SendCharIdOpcode(ZMMOCharOp::C_CHAR_DELETE_CANCEL, CharId);
}

void UUIUserLobbyScreen_Base::SendCharIdOpcode(int32 Opcode, const FString& CharId)
{
	UZeusCharServerSubsystem* Char = GetCharServer();
	if (!Char) return;
	uint64 CharId64 = 0;
	LexFromString(CharId64, *CharId);
	TArray<uint8> Payload;
	for (int32 i = 0; i < 8; ++i) Payload.Add(static_cast<uint8>((CharId64 >> (8 * i)) & 0xFF));
	Char->SendCharRequest(Opcode, Payload);
}

void UUIUserLobbyScreen_Base::HandleCharDeleteAck(const TArray<uint8>& Payload)
{
	// Wire: uint64 charId + uint16 reason + uint64 effectiveAtMs
	int32 Pos = 0; uint64 CharId64 = 0; uint16 Reason = 0; uint64 EffectiveAtMs = 0;
	if (!ReadU64(Payload, Pos, CharId64) || !ReadU16(Payload, Pos, Reason) || !ReadU64(Payload, Pos, EffectiveAtMs))
	{
		UE_LOG(LogZMMO, Warning, TEXT("Lobby: S_CHAR_DELETE_ACK malformado"));
		return;
	}
	if (Reason == 0)
	{
		UE_LOG(LogZMMO, Log, TEXT("Lobby: delete agendado char=%llu (effective=%llu)"),
			static_cast<unsigned long long>(CharId64), static_cast<unsigned long long>(EffectiveAtMs));
	}
	else
	{
		UE_LOG(LogZMMO, Warning, TEXT("Lobby: DELETE_ACK rejeitado (reason=%d)"), Reason);
	}
	RequestCharList();
}

void UUIUserLobbyScreen_Base::HandleCharDeleteAcceptAck(const TArray<uint8>& Payload)
{
	int32 Pos = 0; uint64 CharId64 = 0; uint16 Reason = 0;
	ReadU64(Payload, Pos, CharId64); ReadU16(Payload, Pos, Reason);
	if (Reason == 0)
	{
		UE_LOG(LogZMMO, Log, TEXT("Lobby: char %llu DELETADO"), static_cast<unsigned long long>(CharId64));
	}
	else
	{
		UE_LOG(LogZMMO, Warning, TEXT("Lobby: DELETE_ACCEPT rejeitado (reason=%d)"), Reason);
	}
	RequestCharList();
}

void UUIUserLobbyScreen_Base::HandleCharDeleteCancelAck(const TArray<uint8>& Payload)
{
	int32 Pos = 0; uint64 CharId64 = 0; uint16 Reason = 0;
	ReadU64(Payload, Pos, CharId64); ReadU16(Payload, Pos, Reason);
	if (Reason == 0)
	{
		UE_LOG(LogZMMO, Log, TEXT("Lobby: delete cancelado char %llu"), static_cast<unsigned long long>(CharId64));
	}
	RequestCharList();
}

void UUIUserLobbyScreen_Base::HandleCharSelectOk(const TArray<uint8>& Payload)
{
	// Wire: uint64 charId + uint16 mapId + float x/y/z + float yaw +
	//       string worldHost + uint16 worldPort + string handoffToken + string region
	int32 Pos = 0;
	uint64 CharId64 = 0; FString WorldHost, HandoffToken, Region;
	uint16 MapId16 = 0;
	float Px=0, Py=0, Pz=0, Yaw=0; uint16 WorldPort = 0;
	if (!ReadU64(Payload, Pos, CharId64)
		|| !ReadU16(Payload, Pos, MapId16)
		|| !ReadFloat(Payload, Pos, Px) || !ReadFloat(Payload, Pos, Py) || !ReadFloat(Payload, Pos, Pz)
		|| !ReadFloat(Payload, Pos, Yaw)
		|| !ReadStringUtf8(Payload, Pos, WorldHost)
		|| !ReadU16(Payload, Pos, WorldPort)
		|| !ReadStringUtf8(Payload, Pos, HandoffToken)
		|| !ReadStringUtf8(Payload, Pos, Region))
	{
		UE_LOG(LogZMMO, Warning, TEXT("Lobby: S_CHAR_SELECT_OK malformado"));
		return;
	}
	UE_LOG(LogZMMO, Log, TEXT("Lobby: handoff -> world=%s:%d mapId=%u token=%s..."),
		*WorldHost, WorldPort, static_cast<uint32>(MapId16), *HandoffToken.Left(8));

	UGameInstance* GI = GetGameInstance();
	if (!GI) return;
	UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem<UUIFrontEndFlowSubsystem>();
	if (Flow)
	{
		Flow->SetState(EZMMOFrontEndState::EnteringWorld);
		// Memoriza pose pra `AZMMOPlayerCharacter::BeginPlay` reposicionar
		// o pawn local na pos salva no DB (em vez do PlayerStart default).
		Flow->SetPendingSpawnPose(FVector(Px, Py, Pz), Yaw);
	}

	// Fase 3: handoff UDP — apresenta o ticket no `C_CONNECT_REQUEST`. O
	// WorldServer valida via GETDEL no Valkey regional. WorldServer envia
	// `S_CONNECT_OK` (sucesso) ou `S_CONNECT_REJECT` (ticket invalido/expirado).
	// Disparamos PRIMEIRO o connect (nao-bloqueante), depois o OpenLevel —
	// ZeusNetworkSubsystem e per-GameInstance e persiste cross-travel.
	if (UZeusNetworkSubsystem* ZeusNet = GI->GetSubsystem<UZeusNetworkSubsystem>())
	{
		ZeusNet->ConnectToZeusServerWithTicket(WorldHost, static_cast<int32>(WorldPort), HandoffToken);
	}
	else
	{
		UE_LOG(LogZMMO, Error, TEXT("Lobby: ZeusNetworkSubsystem ausente — handoff abortado"));
	}

	// Fase 4: lookup mapId no DT_Maps e dispara OpenLevel pro level certo.
	// Se DT_Maps nao tem entrada (ou ainda nao foi criada no editor),
	// caimos no fallback do S_TRAVEL_TO_MAP do server (mapName/mapPath string).
	if (Flow && MapId16 != 0)
	{
		if (!Flow->TravelToMapById(static_cast<int32>(MapId16)))
		{
			UE_LOG(LogZMMO, Warning, TEXT("Lobby: mapId=%u nao resolvido no DT_Maps — esperando S_TRAVEL_TO_MAP do server."),
				static_cast<uint32>(MapId16));
		}
	}
}

void UUIUserLobbyScreen_Base::HandleCharSelectReject(const TArray<uint8>& Payload)
{
	int32 Pos = 0;
	uint16 Reason = 0;
	ReadU16(Payload, Pos, Reason);
	UE_LOG(LogZMMO, Warning, TEXT("Lobby: S_CHAR_SELECT_REJECT reason=%d"), Reason);
}

void UUIUserLobbyScreen_Base::HandleCharCreateOk(const TArray<uint8>& /*Payload*/)
{
	UE_LOG(LogZMMO, Log, TEXT("Lobby: S_CHAR_CREATE_OK — refresh lista"));
	ShowCharSelectPage();
	RequestCharList();
}

void UUIUserLobbyScreen_Base::HandleCharCreateReject(const TArray<uint8>& Payload)
{
	int32 Pos = 0;
	uint16 Reason = 0;
	ReadU16(Payload, Pos, Reason);
	UE_LOG(LogZMMO, Warning, TEXT("Lobby: S_CHAR_CREATE_REJECT reason=%d"), Reason);
}

UZeusCharServerSubsystem* UUIUserLobbyScreen_Base::GetCharServer() const
{
	if (const UGameInstance* GI = GetGameInstance())
	{
		return GI->GetSubsystem<UZeusCharServerSubsystem>();
	}
	return nullptr;
}
