// Copyright Zeus Server Engine. All rights reserved.

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "ZeusAOIDebugTarget.h" // plugin ZeusAdminToolsRuntime: interface + EZeusAOIOverlay
#include "ZeusV1Protocol.h"     // ZeusV1::FDebugFrontierInfo (overlay de fronteira)
#include "ZeusAOIComponent.generated.h"

class AZeusFrontierOverlayActor;

// Delegates BlueprintAssignable pra game systems reagirem a entrada/saida de
// outras entidades na AOI do player local. Disparados quando ENT_SPAWN /
// ENT_DESPAWN chega do servidor (V1 canonico). Self-entity filtrada -- o
// proprio char NAO fires aqui.
//   - Entered: 1 param (EntityId)
//   - Exited:  2 params (EntityId, Reason). Reason vem do payload ENT_DESPAWN.
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FZeusAOIEntityEnter, int64, EntityId);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FZeusAOIEntityExit, int64, EntityId, int32, Reason);

/**
 *  UZeusAOIComponent (cliente / jogo)
 *
 *  Componente de gameplay do player local. Dominio principal: Area of Interest
 *  do cliente (proxies, despawn coordenado, interpolacao — ver
 *  [[project_aoi_client_component]]). AQUI, alem disso, expoe um "plus" de
 *  DEBUG: overlays via DrawDebug* dirigidos pelo painel admin (plugin) atraves
 *  de IZeusAOIDebugTarget, e/ou por console vars (zeus.debug.aoi etc.).
 *
 *  As DUAS zonas do AOI (Interesse interna + Despawn externa = histerese) vem do
 *  SERVIDOR: ao ligar um overlay (ou no BeginPlay) o componente pede a config via
 *  C_DEBUG_AOI_REQUEST e recebe S_DEBUG_AOI_INFO (delegate OnDebugAoiInfo do
 *  UZeusNetworkSubsystem) com interestRadiusCm + despawnRadiusCm. Enquanto nao
 *  chega (raio == 0), nao desenha (loading).
 *
 *  Vive na src do ZMMO (nao no plugin): e' um componente do JOGO. O plugin
 *  ZeusAdminTools apenas COMUNICA com ele via a interface — direcao da
 *  dependencia: ZMMO -> plugin (implementa a interface).
 */
UCLASS(ClassGroup = (Zeus), meta = (BlueprintSpawnableComponent), DisplayName = "Zeus AOI Component")
class ZMMO_API UZeusAOIComponent : public UActorComponent, public IZeusAOIDebugTarget
{
	GENERATED_BODY()

public:
	UZeusAOIComponent();

	virtual void BeginPlay() override;
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
	virtual void TickComponent(float DeltaTime, ELevelTick TickType,
	                           FActorComponentTickFunction* ThisTickFunction) override;

	// === IZeusAOIDebugTarget (dirigido pelo painel admin) ===
	virtual void SetOverlayEnabled_Implementation(EZeusAOIOverlay Overlay, bool bEnabled) override;
	virtual void SetAllOverlays_Implementation(bool bEnabled) override;
	virtual bool IsOverlayEnabled_Implementation(EZeusAOIOverlay Overlay) const override;
	virtual float GetInterestRadiusCm_Implementation() const override;
	virtual float GetDespawnRadiusCm_Implementation() const override;

	// === Flags de debug por overlay ===
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowAOIRadius = false;    // Zona de Interesse
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowDespawnZone = false;  // Zona de Despawn
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowCellBounds = false;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowProxyLabels = false;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowHandoffLine = false;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowAuthDrift = false;
	// Fronteiras do mesh: paredes own<->neighbor com label "Cell <id>", renderizadas
	// por AZeusFrontierOverlayActor a partir do DEBUG_FRONTIER_INFO (6163).
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowFrontier = false;

	// === Parametros ===
	// Raios reais vem do servidor (S_DEBUG_AOI_INFO). 0 = ainda nao recebido (loading).
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Zeus|AOI Debug|Params") float InterestRadiusCm = 0.0f;
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Zeus|AOI Debug|Params") float DespawnRadiusCm = 0.0f;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Params") float CellBoundsExtentCm = 50000.0f;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Params") float LineThickness = 2.0f;

	UFUNCTION(BlueprintPure, Category = "Zeus|AOI Debug")
	int32 GetEnabledOverlayCount() const;

	// === Gameplay AOI: visibility tracking (1.4 do roteiro PvP) ============
	//
	// Espelha quem o servidor mandou ENT_SPAWN/ENT_DESPAWN pro player local.
	// Cliente NAO faz histérese própria -- server já filtra com
	// margem de ~2048cm ([[project_aoi_crossserver_hysteresis]]). Game systems
	// (HUD, audio, ability target filtering) escutam estes delegates pra
	// reagir a entrada/saída. Self-entity filtrada (o próprio char
	// não fires nem entra no tracking).
	UPROPERTY(BlueprintAssignable, Category = "Zeus|AOI") FZeusAOIEntityEnter OnEntityEnteredAOI;
	UPROPERTY(BlueprintAssignable, Category = "Zeus|AOI") FZeusAOIEntityExit  OnEntityExitedAOI;

	UFUNCTION(BlueprintPure, Category = "Zeus|AOI") int32         GetVisibleEntityCount() const;
	UFUNCTION(BlueprintPure, Category = "Zeus|AOI") TArray<int64> GetVisibleEntityIds()   const;
	UFUNCTION(BlueprintPure, Category = "Zeus|AOI") bool          IsEntityVisible(int64 EntityId) const;
	UFUNCTION(BlueprintPure, Category = "Zeus|AOI") int64         GetSelfEntityId()       const { return SelfEntityId_; }

private:
	void DrawOverlays();
	bool ResolveOverlay(EZeusAOIOverlay Overlay) const;
	bool AnyOverlayActive() const;

	/** Pede a config de AOI ao servidor (C_DEBUG_AOI_REQUEST). No-op se ja' temos
	 *  os raios ou se nao houver UZeusNetworkSubsystem conectado. */
	void RequestAoiConfig();

	/** Bind de UZeusNetworkSubsystem::OnDebugAoiInfo: armazena os raios reais. */
	void HandleAoiConfig(float InterestCm, float DespawnCm);

	/** Pede o frontier do mesh ao servidor (DEBUG_FRONTIER_REQUEST 6162). */
	void RequestFrontierConfig();
	/** Bind de OnDebugFrontierInfo: guarda as cells e (se overlay on) reconstroi as paredes. */
	void HandleFrontierConfig(const ZeusV1::FDebugFrontierInfo& Info);
	/** Spawna (lazy) o ator que desenha as paredes + labels. */
	AZeusFrontierOverlayActor* EnsureFrontierActor();
	/** Liga / reconstroi / limpa o overlay de fronteira conforme bShowFrontier + dados. */
	void UpdateFrontierOverlay();

	/** Handle do bind do OnDebugAoiInfo (pra remover no EndPlay). */
	FDelegateHandle AoiConfigHandle_;

	/** Handle do bind do OnDebugFrontierInfo (frontier overlay). */
	FDelegateHandle FrontierConfigHandle_;
	/** Ultimo frontier recebido do server (own cells + neighbors). */
	ZeusV1::FDebugFrontierInfo FrontierInfo_;
	bool bFrontierReceived_ = false;
	/** Estado do overlay JA aplicado (paredes desenhadas). O TickComponent compara
	 *  com ResolveOverlay p/ pegar liga/desliga via CVar tambem (nao so' o toggle). */
	bool bFrontierApplied_ = false;
	/** Re-poll periodico do frontier enquanto ligado -> overlay reflete split/merge
	 *  da topologia dinamica (o 6163 traz o estado atual do topology:current). */
	float FrontierRepollIntervalSec = 2.0f;
	float FrontierRepollAccumSec_ = 0.0f;
	/** Ator que desenha as paredes + labels (spawn lazy ao ligar o overlay). */
	UPROPERTY() TObjectPtr<AZeusFrontierOverlayActor> FrontierActor_ = nullptr;

	// === Visibility tracking (1.4) ==========================================
	UFUNCTION() void HandleEntitySpawnedV1(int64 EntityId, int32 Kind, FVector PosCm,
	                                       float YawDeg, FVector VelCmS, bool bGrounded,
	                                       int64 ServerTimeMs);
	UFUNCTION() void HandleEntityDespawnedV1(int64 EntityId, int32 Reason);
	UFUNCTION() void HandleSelfEntityAssignedV1(int64 EntityId);

	/** EntityIds atualmente visíveis (espelho do server). TSet pra O(1) check.
	 *  Não inclui self. Limpo no EndPlay. */
	TSet<int64> VisibleEntityIds_;
	/** EntityId do próprio char (recebido via ENT_SELF). 0 = ainda não atribuído. */
	int64 SelfEntityId_ = 0;
};
