// Copyright Zeus Server Engine. All rights reserved.

#include "ZeusAOIComponent.h"

#include "ZeusNetworkSubsystem.h" // plugin ZeusNetwork: OnDebugAoiInfo + SendDebugAoiRequest

#include "DrawDebugHelpers.h"
#include "Engine/GameInstance.h"
#include "Engine/World.h"
#include "GameFramework/Actor.h"

// Console vars — espelham as flags do painel (overlay liga se flag OU CVar).
static TAutoConsoleVariable<int32> CVarShowAOI(
	TEXT("zeus.debug.aoi"), 0, TEXT("Desenha a Zona de Interesse (esfera do raio AOI)."), ECVF_Default);
static TAutoConsoleVariable<int32> CVarShowDespawn(
	TEXT("zeus.debug.despawn"), 0, TEXT("Desenha a Zona de Despawn (esfera externa do AOI)."), ECVF_Default);
static TAutoConsoleVariable<int32> CVarShowCell(
	TEXT("zeus.debug.cell"), 0, TEXT("Desenha os bounds da celula (placeholder)."), ECVF_Default);
static TAutoConsoleVariable<int32> CVarShowProxy(
	TEXT("zeus.debug.proxy"), 0, TEXT("Desenha labels de proxy (TODO)."), ECVF_Default);
static TAutoConsoleVariable<int32> CVarShowHandoff(
	TEXT("zeus.debug.handoff"), 0, TEXT("Desenha a linha de handoff (placeholder)."), ECVF_Default);
static TAutoConsoleVariable<int32> CVarShowDrift(
	TEXT("zeus.debug.drift"), 0, TEXT("Desenha o drift autoritativo (TODO)."), ECVF_Default);

namespace
{
	const FColor kColorInterest(93, 213, 255);  // cyan — Zona de Interesse
	const FColor kColorDespawn(255, 140, 90);   // laranja — Zona de Despawn (externa)
	const FColor kColorCell(232, 192, 96);
	const FColor kColorHandoff(255, 155, 190);
}

UZeusAOIComponent::UZeusAOIComponent()
{
	PrimaryComponentTick.bCanEverTick = true;
	PrimaryComponentTick.bStartWithTickEnabled = true;
	SetIsReplicatedByDefault(false); // debug/cliente; gameplay AOI real entra depois
}

void UZeusAOIComponent::BeginPlay()
{
	Super::BeginPlay();

	// Bind do feed de config de AOI (servidor -> cliente). Multicast plain C++.
	if (UWorld* World = GetWorld())
	{
		if (UGameInstance* GI = World->GetGameInstance())
		{
			if (UZeusNetworkSubsystem* Net = GI->GetSubsystem<UZeusNetworkSubsystem>())
			{
				AoiConfigHandle_ = Net->OnDebugAoiInfo.AddUObject(this, &UZeusAOIComponent::HandleAoiConfig);
			}
		}
	}

	// Best-effort: ja' pede a config (no-op se ainda nao conectado — o request
	// e' re-disparado ao ligar um overlay em SetOverlayEnabled).
	RequestAoiConfig();
}

void UZeusAOIComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	if (AoiConfigHandle_.IsValid())
	{
		if (UWorld* World = GetWorld())
		{
			if (UGameInstance* GI = World->GetGameInstance())
			{
				if (UZeusNetworkSubsystem* Net = GI->GetSubsystem<UZeusNetworkSubsystem>())
				{
					Net->OnDebugAoiInfo.Remove(AoiConfigHandle_);
				}
			}
		}
		AoiConfigHandle_.Reset();
	}
	Super::EndPlay(EndPlayReason);
}

void UZeusAOIComponent::TickComponent(float DeltaTime, ELevelTick TickType,
                                      FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	if (AnyOverlayActive())
	{
		DrawOverlays();
	}
}

void UZeusAOIComponent::RequestAoiConfig()
{
	if (UWorld* World = GetWorld())
	{
		if (UGameInstance* GI = World->GetGameInstance())
		{
			if (UZeusNetworkSubsystem* Net = GI->GetSubsystem<UZeusNetworkSubsystem>())
			{
				Net->SendDebugAoiRequest(); // no-op se nao conectado
			}
		}
	}
}

void UZeusAOIComponent::HandleAoiConfig(float InterestCm, float DespawnCm)
{
	InterestRadiusCm = InterestCm;
	DespawnRadiusCm  = DespawnCm;
}

bool UZeusAOIComponent::ResolveOverlay(EZeusAOIOverlay Overlay) const
{
	switch (Overlay)
	{
	case EZeusAOIOverlay::AOIRadius:   return bShowAOIRadius    || CVarShowAOI.GetValueOnGameThread()     > 0;
	case EZeusAOIOverlay::DespawnZone: return bShowDespawnZone  || CVarShowDespawn.GetValueOnGameThread() > 0;
	case EZeusAOIOverlay::CellBounds:  return bShowCellBounds   || CVarShowCell.GetValueOnGameThread()    > 0;
	case EZeusAOIOverlay::ProxyLabels: return bShowProxyLabels  || CVarShowProxy.GetValueOnGameThread()   > 0;
	case EZeusAOIOverlay::HandoffLine: return bShowHandoffLine  || CVarShowHandoff.GetValueOnGameThread() > 0;
	case EZeusAOIOverlay::AuthDrift:   return bShowAuthDrift    || CVarShowDrift.GetValueOnGameThread()   > 0;
	default: return false;
	}
}

bool UZeusAOIComponent::AnyOverlayActive() const
{
	return ResolveOverlay(EZeusAOIOverlay::AOIRadius)
		|| ResolveOverlay(EZeusAOIOverlay::DespawnZone)
		|| ResolveOverlay(EZeusAOIOverlay::CellBounds)
		|| ResolveOverlay(EZeusAOIOverlay::ProxyLabels)
		|| ResolveOverlay(EZeusAOIOverlay::HandoffLine)
		|| ResolveOverlay(EZeusAOIOverlay::AuthDrift);
}

void UZeusAOIComponent::DrawOverlays()
{
	const AActor* Owner = GetOwner();
	UWorld* World = GetWorld();
	if (!Owner || !World) { return; }

	const FVector Loc = Owner->GetActorLocation();

	// Zona de Interesse (interna) — raio real do servidor. Sem o raio (==0),
	// ainda nao chegou a config: nao desenha (loading).
	if (ResolveOverlay(EZeusAOIOverlay::AOIRadius) && InterestRadiusCm > 0.0f)
	{
		DrawDebugSphere(World, Loc, InterestRadiusCm, 32, kColorInterest, false, -1.0f, 0, LineThickness);
	}
	// Zona de Despawn (externa, > interesse = histerese) — raio real do servidor.
	if (ResolveOverlay(EZeusAOIOverlay::DespawnZone) && DespawnRadiusCm > 0.0f)
	{
		DrawDebugSphere(World, Loc, DespawnRadiusCm, 32, kColorDespawn, false, -1.0f, 0, LineThickness);
	}
	if (ResolveOverlay(EZeusAOIOverlay::CellBounds))
	{
		// PLACEHOLDER: box grande centrada no player. TODO bounds reais da cell.
		DrawDebugBox(World, Loc, FVector(CellBoundsExtentCm, CellBoundsExtentCm, 2000.0f),
			kColorCell, false, -1.0f, 0, LineThickness);
	}
	if (ResolveOverlay(EZeusAOIOverlay::HandoffLine))
	{
		// PLACEHOLDER: vetor pra frente. TODO apontar pro centro da cell vizinha.
		const float Len = InterestRadiusCm > 0.0f ? InterestRadiusCm : 3000.0f;
		const FVector End = Loc + Owner->GetActorForwardVector() * Len;
		DrawDebugDirectionalArrow(World, Loc, End, 200.0f, kColorHandoff, false, -1.0f, 0, LineThickness);
	}
	// ProxyLabels / AuthDrift: TODO (precisam do registry de proxies/snapshots).
}

void UZeusAOIComponent::SetOverlayEnabled_Implementation(EZeusAOIOverlay Overlay, bool bEnabled)
{
	switch (Overlay)
	{
	case EZeusAOIOverlay::AOIRadius:   bShowAOIRadius   = bEnabled; break;
	case EZeusAOIOverlay::DespawnZone: bShowDespawnZone = bEnabled; break;
	case EZeusAOIOverlay::CellBounds:  bShowCellBounds  = bEnabled; break;
	case EZeusAOIOverlay::ProxyLabels: bShowProxyLabels = bEnabled; break;
	case EZeusAOIOverlay::HandoffLine: bShowHandoffLine = bEnabled; break;
	case EZeusAOIOverlay::AuthDrift:   bShowAuthDrift   = bEnabled; break;
	default: break;
	}

	// Ao ligar uma zona de AOI sem ter os raios ainda, pede a config (loading).
	const bool bAoiZone = (Overlay == EZeusAOIOverlay::AOIRadius || Overlay == EZeusAOIOverlay::DespawnZone);
	if (bEnabled && bAoiZone && InterestRadiusCm <= 0.0f)
	{
		RequestAoiConfig();
	}
}

void UZeusAOIComponent::SetAllOverlays_Implementation(bool bEnabled)
{
	bShowAOIRadius = bShowDespawnZone = bShowCellBounds = bEnabled;
	bShowProxyLabels = bShowHandoffLine = bShowAuthDrift = bEnabled;

	if (bEnabled && InterestRadiusCm <= 0.0f)
	{
		RequestAoiConfig();
	}
}

bool UZeusAOIComponent::IsOverlayEnabled_Implementation(EZeusAOIOverlay Overlay) const
{
	// Reflete o estado real (flag OU CVar) p/ a UI mostrar o que esta' desenhando.
	return ResolveOverlay(Overlay);
}

float UZeusAOIComponent::GetInterestRadiusCm_Implementation() const
{
	return InterestRadiusCm; // 0 = config do servidor ainda nao chegou (loading)
}

float UZeusAOIComponent::GetDespawnRadiusCm_Implementation() const
{
	return DespawnRadiusCm;
}

int32 UZeusAOIComponent::GetEnabledOverlayCount() const
{
	return (bShowAOIRadius ? 1 : 0) + (bShowDespawnZone ? 1 : 0) + (bShowCellBounds ? 1 : 0)
		+ (bShowProxyLabels ? 1 : 0) + (bShowHandoffLine ? 1 : 0) + (bShowAuthDrift ? 1 : 0);
}
