#include "UIServerSelectScreen_Base.h"

#include "ZMMO.h"
#include "ZMMOThemeSubsystem.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 ZMMOWire::ReadU8;
using ZMMOWire::ReadU16;
using ZMMOWire::ReadStringUtf8;

namespace
{
	FUIStyle ResolveServerSelectStyle(const UUserWidget* Widget, const FUIStyle& Fallback)
	{
		if (Widget)
		{
			if (const UGameInstance* GI = Widget->GetGameInstance())
			{
				if (const UZMMOThemeSubsystem* Theme = GI->GetSubsystem<UZMMOThemeSubsystem>())
				{
					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;
		}
	}

	RequestWorldList();
}

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::RequestWorldList()
{
	UZeusCharServerSubsystem* Char = GetCharServer();
	if (!Char || !Char->IsConnected())
	{
		UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: CharServer indisponivel — pulando RequestWorldList"));
		return;
	}
	TArray<uint8> EmptyPayload;
	Char->SendCharRequest(ZMMOCharOp::C_WORLD_LIST_REQUEST, EmptyPayload);
}

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

void UUIServerSelectScreen_Base::HandleCharRawMessage(int32 Opcode, const TArray<uint8>& Payload)
{
	if (Opcode == ZMMOCharOp::S_WORLD_LIST)
	{
		ParseWorldList(Payload);
	}
	else if (Opcode == ZMMOCharOp::S_WORLD_STATUS_UPDATE)
	{
		ApplyStatusUpdate(Payload);
	}
}

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

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

	TArray<FZMMOWorldEntry> NewWorlds;
	NewWorlds.Reserve(Count);

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

		if (!ReadStringUtf8(Payload, Pos, Entry.WorldId)
			|| !ReadStringUtf8(Payload, Pos, Entry.WorldName)
			|| !ReadStringUtf8(Payload, Pos, RegionStr)
			|| !ReadStringUtf8(Payload, Pos, Entry.Host)
			|| !ReadU16(Payload, Pos, Port)
			|| !ReadU16(Payload, Pos, Cap)
			|| !ReadU16(Payload, Pos, Pop)
			|| !ReadU16(Payload, Pos, QueueLen)
			|| !ReadU8(Payload, Pos, State))
		{
			UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: S_WORLD_LIST malformado (entry %d)"), i);
			return;
		}
		Entry.Region = RegionStr;
		Entry.Port = static_cast<int32>(Port);
		Entry.Capacity = static_cast<int32>(Cap);
		Entry.Population = static_cast<int32>(Pop);
		Entry.QueueLen = static_cast<int32>(QueueLen);
		Entry.State = State;
		NewWorlds.Add(MoveTemp(Entry));
	}

	Worlds = MoveTemp(NewWorlds);
	UE_LOG(LogZMMO, Log, TEXT("ServerSelect: recebidos %d mundos"), Worlds.Num());

	RebuildCards();
	OnWorldListReceived();
}

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 FZMMOWorldEntry& W : Worlds)
	{
		UUIServerCard_Base* Card = CreateWidget<UUIServerCard_Base>(this, CardClass);
		if (!Card)
		{
			UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: falha ao criar card para %s"), *W.WorldName);
			continue;
		}
		Card->SetFromEntry(W);
		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 WorldId, uint8 State)
{
	// O ServerSelect permite escolher qualquer mundo (online/maintenance/
	// offline) — o usuario entra no Lobby pra gerenciar chars desse mundo.
	// O bloqueio real (handoff -> world) acontece no botao "Entrar no
	// Servidor" do Lobby, que checa World.State antes do C_CHAR_SELECT.
	UE_LOG(LogZMMO, Log, TEXT("ServerSelect: mundo %s selecionado (state=%d)"), *WorldId, State);
	SelectWorldAndProceed(WorldId);
}

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