#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "ZeusEntityInterface.h"
#include "ZeusEntityTypes.h"
#include "ZeusPlayerProxy.generated.h"

/**
 *  AZeusPlayerProxy
 *
 *  Proxy visual para jogadores remotos (e, no futuro, mobs com locomotion
 *  por capsula) cuja posicao vem 100% do servidor via snapshots
 *  (`UZeusNetworkSubsystem::OnPlayerStateUpdate`).
 *
 *  Diferencas chave vs. `AZeusCharacter` (jogador local):
 *    - SEM `EnhancedInput`, sem envio de `C_INPUT_AXIS`.
 *    - SEM buffer de pending inputs nem reconciliacao.
 *    - Posicao aplicada por snapshot (`ApplyEntitySnapshot`); a `ACharacter`
 *      continua a existir para preservar capsula + skeletal mesh + AnimBP,
 *      mas o CMC fica em modo "kinematic-ish" (nao integra fisica).
 *
 *  Animacao no AnimBP do Quinn (`ABP_Unarmed`): a formula `ShouldMove` exige
 *  `Acceleration != 0`. Como proxies nao tem input, adoptamos a estrategia
 *  de CMC custom (`UZeusProxyMovementComponent`) com escrita externa de
 *  `Acceleration`, alimentada pela derivada da velocidade entre snapshots.
 *  Tick do proxy roda **antes** do mesh tick para que Velocity+Acceleration
 *  estejam populados quando o `NativeUpdateAnimation` do AnimBP os ler.
 */
/**
 *  Snapshot bruto guardado no buffer de interpolacao. Mantido como struct privada
 *  ao .h porque so o proxy (e debugging local) precisa olhar para ele.
 */
USTRUCT()
struct FZeusProxySnapshot
{
	GENERATED_BODY()

	UPROPERTY()
	FVector PosCm = FVector::ZeroVector;

	UPROPERTY()
	FVector VelCmS = FVector::ZeroVector;

	/** Yaw autoritativo do servidor (graus) = o MESMO yaw que o player LOCAL aplicou
	 *  ao virar. O proxy replica EXATAMENTE a viradinha do local (apertou D = mesma
	 *  virada nos dois). Sem armazenar isto, o proxy derivava o yaw da velocidade
	 *  (Atan2) e "virava totalmente pra direcao do movimento" (bug 2026-06-13).
	 *  NAO confundir com "mira dita a locomocao" (strafe / Motion Matching) -- isso
	 *  esta' DESABILITADO por ora (ver AVISO em ZeusPlayerProxy.cpp::Tick). */
	UPROPERTY()
	float YawDeg = 0.0f;

	UPROPERTY()
	bool bGrounded = true;

	/** Timestamp do servidor que produziu o snapshot (ms desde Unix epoch). */
	UPROPERTY()
	int64 ServerTimeMs = 0;
};

UCLASS(Blueprintable, BlueprintType)
class ZMMO_API AZeusPlayerProxy : public ACharacter, public IZeusEntityInterface
{
	GENERATED_BODY()

public:
	AZeusPlayerProxy(const FObjectInitializer& ObjectInitializer);

	virtual void BeginPlay() override;
	virtual void Tick(float DeltaSeconds) override;

	// --- IZeusEntityInterface ---
	virtual int64 GetZeusEntityId() const override { return EntityId; }
	virtual EZeusEntityType GetZeusEntityType() const override { return EntityType; }
	virtual void ApplyEntitySnapshot(const FZeusEntitySnapshot& Snapshot) override;
	virtual void SetEntityRelevant(bool bRelevant) override;

	UFUNCTION(BlueprintCallable, Category = "Zeus|Entity")
	void SetZeusIdentity(int64 InEntityId, EZeusEntityType InEntityType);

protected:
	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "Zeus|Entity")
	int64 EntityId = 0;

	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "Zeus|Entity")
	EZeusEntityType EntityType = EZeusEntityType::Player;

	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "Zeus|Entity")
	bool bIsZeusRelevant = true;

	/** True apos o primeiro snapshot ser aplicado — antes disso o ator pode ficar oculto. */
	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "Zeus|Entity")
	bool bHasFirstSnapshot = false;

	/**
	 *  Ultima posicao **autoritativa** recebida do servidor (cm, mundo). Difere
	 *  de `InterpolatedPosCm` (visual) — separar os dois e essencial para
	 *  comparar drift autoritativo vs visual via debug overlay/logs.
	 */
	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "Zeus|Entity")
	FVector AuthoritativePosCm = FVector::ZeroVector;

	/** Posicao **visual** atual (resultado da interpolacao do tick). */
	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "Zeus|Entity")
	FVector InterpolatedPosCm = FVector::ZeroVector;

	/** Compat: alias para `AuthoritativePosCm` (preserva binding de Blueprint, se houver). */
	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "Zeus|Entity")
	FVector LastSnapshotPosition = FVector::ZeroVector;

	/**
	 *  Velocidade visual derivada para o AnimBP. Espelha a velocidade
	 *  *interpolada* (nao o snapshot bruto) e e re-imposta no `CMC->Velocity`
	 *  em cada `Tick` para manter os drivers de animacao alimentados entre
	 *  updates de rede.
	 */
	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "Zeus|Entity")
	FVector VisualVelocity = FVector::ZeroVector;

	/**
	 *  Atraso de interpolacao em ms. Render-time = wall_now - InterpolationDelayMs,
	 *  expresso no dominio do tempo do servidor. Padrao 100 ms (~3 snapshots a 30 Hz)
	 *  cobre 1 perda + 1 jitter sem extrapolar.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|Network",
		meta = (ClampMin = "0.0", ClampMax = "500.0"))
	double InterpolationDelayMs = 100.0;

	/** Tamanho maximo do buffer (em snapshots). Cobre ~270 ms a 30 Hz. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|Network",
		meta = (ClampMin = "2", ClampMax = "32"))
	int32 SnapshotBufferCapacity = 8;

	/** Limite duro de extrapolacao (s) quando nao ha snapshot futuro. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|Network",
		meta = (ClampMin = "0.0", ClampMax = "0.5"))
	float MaxExtrapolationSeconds = 0.1f;

	/** Buffer ordenado por `ServerTimeMs` ascendente. */
	UPROPERTY()
	TArray<FZeusProxySnapshot> SnapshotBuffer;

	/**
	 *  Offset entre o relogio local (`FPlatformTime::Seconds * 1000`) e o
	 *  relogio do servidor (`ServerTimeMs` do snapshot). Estimado pela primeira
	 *  amostra como `local_now_ms - server_time_ms`. Subtraindo este offset do
	 *  relogio local obtemos o tempo do servidor estimado.
	 */
	int64 ServerClockOffsetMs = 0;

	/** DIAG (2026-06-13): instante (s, monotonic) do ultimo snapshot recebido em
	 *  ApplyEntitySnapshot. Usado no log do Tick pra medir "secsSinceSnap" = quanto
	 *  tempo o proxy ficou MUDO (sem delta). secsSinceSnap grande == fome (player
	 *  remoto parado -> ComputeDelta suprime). 0 = nenhum snapshot ainda. */
	double LastSnapshotRecvSec = 0.0;

	/** Aceleracao derivada (com smoothing) escrita no CMC custom para alimentar
	 *  o AnimBP (`ShouldMove`/`Jump_Start` exigem Acceleration!=0). */
	FVector LastDerivedAccelerationCmS2 = FVector::ZeroVector;

	/** Velocidade visual do tick anterior (para a derivada `Acceleration = dV/dt`). */
	FVector PreviousVisualVelocity = FVector::ZeroVector;

	bool bHasPreviousSnapshot = false;

	/** Ultimo log de diagnostico (s, monotonic). Log emitido 1x/s no Tick. */
	double LastDiagLogSec = 0.0;

private:
	/** Reaplica `CMC->Velocity` (com clamp de Z em walking) e injeta a
	 *  aceleracao derivada no CMC custom. Chamado em `Tick` (pre-mesh) e em
	 *  `ApplyEntitySnapshot` (para refletir a chegada de novo snapshot
	 *  imediatamente). */
	void RefreshAnimationDrivers();

	/** Liga/desliga colisao da capsula consoante a relevancia + visual ready. */
	void ApplyCollisionPolicy();
};
