#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "ZMMOEntityInterface.h"
#include "ZMMOEntityTypes.h"
#include "ZMMOPlayerProxy.generated.h"

/**
 *  AZMMOPlayerProxy
 *
 *  Proxy visual para jogadores remotos (e, no futuro, mobs com locomotion
 *  por capsula) cuja posicao vem 100% do servidor via snapshots
 *  (`UZeusNetworkSubsystem::OnPlayerStateUpdate`).
 *
 *  Inspirado em `AZeusRemoteCharacter` (ZeusV1). Diferencas chave vs.
 *  `AZMMOPlayerCharacter` (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 mesma
 *  estrategia do ZClientMMO: CMC custom (`UZMMOProxyMovementComponent`) 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 FZMMOProxySnapshot
{
	GENERATED_BODY()

	UPROPERTY()
	FVector PosCm = FVector::ZeroVector;

	UPROPERTY()
	FVector VelCmS = FVector::ZeroVector;

	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 AZMMOPlayerProxy : public ACharacter, public IZMMOEntityInterface
{
	GENERATED_BODY()

public:
	AZMMOPlayerProxy(const FObjectInitializer& ObjectInitializer);

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

	// --- IZMMOEntityInterface ---
	virtual int64 GetZMMOEntityId() const override { return EntityId; }
	virtual EZMMOEntityType GetZMMOEntityType() const override { return EntityType; }
	virtual void ApplyEntitySnapshot(const FZMMOEntitySnapshot& Snapshot) override;
	virtual void SetEntityRelevant(bool bRelevant) override;

	UFUNCTION(BlueprintCallable, Category = "ZMMO|Entity")
	void SetZMMOIdentity(int64 InEntityId, EZMMOEntityType InEntityType);

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

	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Entity")
	EZMMOEntityType EntityType = EZMMOEntityType::Player;

	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Entity")
	bool bIsZMMORelevant = true;

	/** True apos o primeiro snapshot ser aplicado — antes disso o ator pode ficar oculto. */
	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|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 = "ZMMO|Entity")
	FVector AuthoritativePosCm = FVector::ZeroVector;

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

	/** Compat: alias para `AuthoritativePosCm` (preserva binding de Blueprint, se houver). */
	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|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 = "ZMMO|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 = "ZMMO|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 = "ZMMO|Network",
		meta = (ClampMin = "2", ClampMax = "32"))
	int32 SnapshotBufferCapacity = 8;

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

	/** Buffer ordenado por `ServerTimeMs` ascendente. */
	UPROPERTY()
	TArray<FZMMOProxySnapshot> 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;

	/** 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();
};
