#include "UIServerSelectScreen_Base.h"

#include "ZMMO.h"
#include "ZeusThemeSubsystem.h"
#include "CharServerOpcodes.h"
#include "UIFrontEndFlowSubsystem.h"
#include "UIServerCard_Base.h"
#include "WireHelpers.h"
#include "ZeusCharServerSubsystem.h"
#include "CommonTextBlock.h"
#include "Components/Border.h"
#include "Components/GridPanel.h"
#include "Components/GridSlot.h"
#include "Components/PanelWidget.h"
#include "Engine/GameInstance.h"
#include "Engine/DataTable.h"
#include "UI/UIStyleRow.h"

using ZeusWire::ReadU8;
using ZeusWire::ReadU16;
using ZeusWire::ReadStringUtf8;

namespace
{
	FUIStyle ResolveServerSelectStyle(const UUserWidget* Widget, const FUIStyle& Fallback)
	{
		if (Widget)
		{
			if (const UGameInstance* GI = Widget->GetGameInstance())
			{
				if (const UZeusThemeSubsystem* Theme = GI->GetSubsystem<UZeusThemeSubsystem>())
				{
					return Theme->GetActiveUIStyle();
				}
			}
		}
		FUIStyle DesignStyle;
		bool bHas = false;
		if (const UDataTable* DT = LoadObject<UDataTable>(nullptr,
			TEXT("/Game/ZMMO/Data/UI/DT_UI_Styles.DT_UI_Styles")))
		{
			if (const FUIStyleRow* Row = DT->FindRow<FUIStyleRow>(
				FName(TEXT("Default")), TEXT("UIServerSelectDesign"), false))
			{
				DesignStyle = Row->Style;
				bHas = true;
			}
		}
		return bHas ? DesignStyle : Fallback;
	}
}

void UUIServerSelectScreen_Base::NativePreConstruct()
{
	Super::NativePreConstruct();
	RefreshUIStyle();
}

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

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

	RequestRealmList();
}

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

void UUIServerSelectScreen_Base::RefreshUIStyle_Implementation()
{
	Super::RefreshUIStyle_Implementation();

	const FUIStyle Fallback;
	const FUIStyle AS = ResolveServerSelectStyle(this, Fallback);

	if (Border_Bg)
	{
		if (AS.ScreenBackground.GetResourceObject() != nullptr)
		{
			Border_Bg->SetBrush(AS.ScreenBackground);
		}
		else
		{
			Border_Bg->SetBrushColor(AS.Palette.Bg0);
		}
	}
	if (Text_Title)
	{
		Text_Title->SetColorAndOpacity(FSlateColor(AS.Semantic.OnSurface));
	}
}

void UUIServerSelectScreen_Base::RequestRealmList()
{
	UZeusCharServerSubsystem* Char = GetCharServer();
	if (!Char || !Char->IsConnected())
	{
		UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: CharServer indisponivel — pulando RequestRealmList"));
		return;
	}
	TArray<uint8> EmptyPayload;
	Char->SendCharRequest(ZeusCharOp::C_REALM_LIST_REQUEST, EmptyPayload);
}

void UUIServerSelectScreen_Base::SelectRealmAndProceed(const FString& RealmId)
{
	if (RealmId.IsEmpty())
	{
		UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: SelectRealmAndProceed com RealmId vazio."));
		return;
	}
	UGameInstance* GI = GetGameInstance();
	if (!GI)
	{
		return;
	}
	if (UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem<UUIFrontEndFlowSubsystem>())
	{
		Flow->SetSelectedRealmId(RealmId);
		Flow->SetState(EZeusFrontEndState::Lobby);
	}
}

void UUIServerSelectScreen_Base::HandleCharRawMessage(int32 Opcode, const TArray<uint8>& Payload)
{
	if (Opcode == ZeusCharOp::S_REALM_LIST)
	{
		ParseRealmList(Payload);
	}
	else if (Opcode == ZeusCharOp::S_REALM_STATUS_UPDATE)
	{
		ApplyStatusUpdate(Payload);
	}
}

void UUIServerSelectScreen_Base::ApplyStatusUpdate(const TArray<uint8>& Payload)
{
	// Wire: string realmId + uint16 pop + uint16 queueLen + uint8 state
	int32 Pos = 0;
	FString RealmId;
	uint16 Pop = 0;
	uint16 QueueLen = 0;
	uint8 State = 0;
	if (!ReadStringUtf8(Payload, Pos, RealmId)
		|| !ReadU16(Payload, Pos, Pop)
		|| !ReadU16(Payload, Pos, QueueLen)
		|| !ReadU8(Payload, Pos, State))
	{
		UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: S_REALM_STATUS_UPDATE malformado"));
		return;
	}
	int32 Idx = INDEX_NONE;
	for (int32 i = 0; i < Realms.Num(); ++i)
	{
		if (Realms[i].RealmId == RealmId)
		{
			Idx = i;
			break;
		}
	}
	if (Idx == INDEX_NONE)
	{
		// Realm desconhecido (ainda nao recebemos via S_REALM_LIST). Pede lista
		// completa pra trazer dados estaticos (name/gatewayEndpoint/region/capacity).
		UE_LOG(LogZMMO, Log, TEXT("ServerSelect: STATUS_UPDATE de realm desconhecido (%s) — pedindo lista"), *RealmId);
		RequestRealmList();
		return;
	}
	FZeusRealmEntry& Entry = Realms[Idx];
	Entry.Pop = static_cast<int32>(Pop);
	Entry.QueueLen = static_cast<int32>(QueueLen);
	Entry.State = State;
	UE_LOG(LogZMMO, Log, TEXT("ServerSelect: realm %s atualizado (state=%d pop=%d)"),
		*RealmId, State, Entry.Pop);
	// Atualiza apenas o card afetado (preserva animacoes/scroll dos demais).
	if (SpawnedCards.IsValidIndex(Idx) && SpawnedCards[Idx])
	{
		SpawnedCards[Idx]->SetFromEntry(Entry);
	}
	OnRealmListReceived();
}

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

	TArray<FZeusRealmEntry> NewRealms;
	NewRealms.Reserve(Count);

	for (uint16 i = 0; i < Count; ++i)
	{
		FZeusRealmEntry Entry;
		FString RegionStr;
		uint16 Cap = 0;
		uint16 Pop = 0;
		uint16 QueueLen = 0;
		uint8 State = 0;

		// Wire: string realmId + string name + string region + string gatewayEndpoint
		//       + uint16 capacity + uint16 pop + uint16 queueLen + uint8 state
		// Server-side: Server/ZeusCharServer/src/services/realm-list.service.ts
		if (!ReadStringUtf8(Payload, Pos, Entry.RealmId)
			|| !ReadStringUtf8(Payload, Pos, Entry.Name)
			|| !ReadStringUtf8(Payload, Pos, RegionStr)
			|| !ReadStringUtf8(Payload, Pos, Entry.GatewayEndpoint)
			|| !ReadU16(Payload, Pos, Cap)
			|| !ReadU16(Payload, Pos, Pop)
			|| !ReadU16(Payload, Pos, QueueLen)
			|| !ReadU8(Payload, Pos, State))
		{
			UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: S_REALM_LIST malformado (entry %d)"), i);
			return;
		}
		Entry.Region = RegionStr;
		Entry.Capacity = static_cast<int32>(Cap);
		Entry.Pop = static_cast<int32>(Pop);
		Entry.QueueLen = static_cast<int32>(QueueLen);
		Entry.State = State;
		NewRealms.Add(MoveTemp(Entry));
	}

	Realms = MoveTemp(NewRealms);
	UE_LOG(LogZMMO, Log, TEXT("ServerSelect: recebidos %d realms"), Realms.Num());

	RebuildCards();
	OnRealmListReceived();
}

void UUIServerSelectScreen_Base::RebuildCards()
{
	if (!CardContainer)
	{
		UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: CardContainer nao bindado — cards nao serao exibidos"));
		return;
	}
	if (!CardClass)
	{
		UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: CardClass nao configurada — defina no WBP_ServerSelect Defaults"));
		return;
	}

	// Limpa cards antigos (delegate + remove do container).
	for (UUIServerCard_Base* Old : SpawnedCards)
	{
		if (Old)
		{
			Old->OnCardPressed.RemoveDynamic(this, &UUIServerSelectScreen_Base::HandleCardPressed);
		}
	}
	SpawnedCards.Reset();
	CardContainer->ClearChildren();

	UGridPanel* AsGrid = Cast<UGridPanel>(CardContainer);
	const int32 GridColumns = 2; // bate com o mock (ColumnFill[0]/[1])

	int32 Index = 0;
	for (const FZeusRealmEntry& R : Realms)
	{
		UUIServerCard_Base* Card = CreateWidget<UUIServerCard_Base>(this, CardClass);
		if (!Card)
		{
			UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: falha ao criar card para %s"), *R.Name);
			continue;
		}
		Card->SetFromEntry(R);
		Card->OnCardPressed.AddDynamic(this, &UUIServerSelectScreen_Base::HandleCardPressed);
		CardContainer->AddChild(Card);

		// Quando o container e um GridPanel, distribui em grid (row, col) — bate
		// com o layout do mock (2 colunas, N linhas).
		if (AsGrid)
		{
			if (UGridSlot* GS = Cast<UGridSlot>(Card->Slot))
			{
				GS->SetRow(Index / GridColumns);
				GS->SetColumn(Index % GridColumns);
				GS->SetHorizontalAlignment(HAlign_Fill);
				GS->SetVerticalAlignment(VAlign_Fill);
				GS->SetPadding(FMargin(12.f, 12.f, 12.f, 12.f));
			}
		}

		SpawnedCards.Add(Card);
		++Index;
	}
}

void UUIServerSelectScreen_Base::HandleCardPressed(FString RealmId, uint8 State)
{
	// O ServerSelect permite escolher qualquer realm (online/maintenance/
	// offline) — o usuario entra no Lobby pra gerenciar chars desse realm.
	// O bloqueio real (handoff -> gateway) acontece no botao "Entrar no
	// Servidor" do Lobby, que checa Realm.State antes do C_CHAR_SELECT.
	UE_LOG(LogZMMO, Log, TEXT("ServerSelect: realm %s selecionado (state=%d)"), *RealmId, State);
	SelectRealmAndProceed(RealmId);
}

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