#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Logging/LogMacros.h"
#include "ZeusEntityInterface.h"
#include "ZeusEntityTypes.h"
#include "ZeusCharacter.generated.h"

class USpringArmComponent;
class UCameraComponent;
class UInputAction;
class UZeusNetworkSubsystem;
class UZeusNetworkingClientSubsystem;
class UZeusAOIComponent;
class UUserWidget;
struct FInputActionValue;

DECLARE_LOG_CATEGORY_EXTERN(LogZeusPlayer, Log, All);

/**
 *  AZeusCharacter
 *
 *  Personagem do jogador local. Parte do pipeline de rede Zeus:
 *
 *    - Continua a usar o `UCharacterMovementComponent` LIVRE para correr a
 *      fisica do landscape (ADR 0038): o cliente e autoritativo localmente
 *      sobre Z/colisao com terreno; o servidor apenas valida input/velocidade.
 *    - Envia `C_INPUT_AXIS` ao `UZeusNetworkSubsystem` na cadencia
 *      `InputSendRateHz` (com heartbeat 5 Hz quando parado para evitar
 *      starvation do `MovementSystem` no servidor).
 *    - Implementa `IZeusEntityInterface` para participar do registry do
 *      `UZeusWorldSubsystem`, mas `ApplyEntitySnapshot` e `SetEntityRelevant`
 *      sao no-op em V0 — o cliente local nao reconcilia (cliente solto).
 *
 *  TODO V1: reconciliacao opcional XY quando colisao de objetos chegar; AOI
 *  debug spheres; visual smoothing apos correcao do servidor.
 */
UCLASS(Blueprintable, BlueprintType)
class ZMMO_API AZeusCharacter : public ACharacter, public IZeusEntityInterface
{
	GENERATED_BODY()

	/** Camera boom positioning the camera behind the character */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
	USpringArmComponent* CameraBoom;

	/** Follow camera */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
	UCameraComponent* FollowCamera;

	/** AOI do cliente (+ overlays de debug dirigidos pelo Zeus Admin Tools). */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Zeus|AOI", meta = (AllowPrivateAccess = "true"))
	TObjectPtr<UZeusAOIComponent> AOIComponent;

	// GAS Component vive no PlayerState (AZeusPlayerState + Component Registry).
	// Acesso: GetPlayerState<AZeusPlayerState>()->GetZeusComponent<UZeusGASComponent>().

protected:
	UPROPERTY(EditAnywhere, Category = "Input")
	UInputAction* JumpAction;

	UPROPERTY(EditAnywhere, Category = "Input")
	UInputAction* MoveAction;

	UPROPERTY(EditAnywhere, Category = "Input")
	UInputAction* LookAction;

	UPROPERTY(EditAnywhere, Category = "Input")
	UInputAction* MouseLookAction;

	/// IA_Dash — dispara C_ABILITY_TRY_ACTIVATE pra Zeus.Ability.Movement.Dash
	/// via UZeusGASComponent. Bind padrao no IMC_Default: LeftShift -> Pressed.
	UPROPERTY(EditAnywhere, Category = "Input")
	UInputAction* DashAction;

	/** Painel Zeus Admin Tools (AIO Debug), aberto/fechado por F8. Default =
	 *  WBP_AdminToolsAIO (resolvido no construtor). */
	UPROPERTY(EditAnywhere, Category = "Zeus|Admin")
	TSubclassOf<UUserWidget> AdminPanelClass;

	/** Cadencia de envio dos pacotes `C_INPUT_AXIS` (Hz). */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Zeus|Networking", meta = (ClampMin = "1", ClampMax = "120"))
	int32 InputSendRateHz = 30;

	/**
	 *  Intervalo maximo (s) entre envios de `C_INPUT_AXIS` quando os inputs sao
	 *  vazios (forward=0, right=0, sem jump). Mantem ~5 Hz de heartbeat para
	 *  evitar starvation do servidor caso pacotes sejam perdidos. Em movimento
	 *  o envio segue `InputSendRateHz` (30 Hz).
	 */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Zeus|Networking", meta = (ClampMin = "0.05", ClampMax = "5.0"))
	float HeartbeatIntervalSec = 0.2f;

public:
	AZeusCharacter();

	virtual void Tick(float DeltaSeconds) override;

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

	UFUNCTION(BlueprintCallable, Category = "Zeus|Entity")
	void SetZeusEntityId(int64 InEntityId) { ZeusEntityId = InEntityId; }

	FORCEINLINE USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
	FORCEINLINE UCameraComponent* GetFollowCamera() const { return FollowCamera; }

protected:
	virtual void BeginPlay() override;
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	void Move(const FInputActionValue& Value);
	void MoveCompleted(const FInputActionValue& Value);
	void Look(const FInputActionValue& Value);
	void OnJumpPressed();
	void OnJumpReleased();

	/// IA_Dash Triggered -> resolve UZeusGASComponent do PlayerState e chama
	/// RequestActivateAbilityByTag("Zeus.Ability.Movement.Dash"). Server valida
	/// + responde S_ABILITY_ACTIVATED (HP/SP atualizado, montage roda local).
	void OnDashTriggered();

	/** F8: cria/mostra ou esconde o painel Zeus Admin Tools (input mode UI + cursor). */
	void ToggleAdminPanel();

public:
	/** Wrappers expostos a Blueprint (UI / mobile / automation). */
	UFUNCTION(BlueprintCallable, Category = "Input")
	virtual void DoMove(float Right, float Forward);

	UFUNCTION(BlueprintCallable, Category = "Input")
	virtual void DoLook(float Yaw, float Pitch);

	UFUNCTION(BlueprintCallable, Category = "Input")
	virtual void DoJumpStart();

	UFUNCTION(BlueprintCallable, Category = "Input")
	virtual void DoJumpEnd();

private:
	void ResolveZeusNetworkSubsystem();
	void FlushInputAxisToServer(float DeltaSeconds);

	/** Liga `OnPlayerSpawned` para captar EntityId local apos o handshake. */
	void BindZeusSpawnDelegate();
	void UnbindZeusSpawnDelegate();

	/** Caso o spawn ja tenha chegado antes deste pawn existir, aplica via cache. */
	void TryRegisterLocalEntityFromCachedSpawn();

	/**
	 *  Memoriza `EntityId` autoritativo e regista o pawn no `UZeusWorldSubsystem`
	 *  para o registry partilhado por proxies remotos (filtra snapshots locais).
	 *  Metadados publicos do char (Name/Guild) chegam via S_CHAR_INFO em
	 *  HandleZeusCharInfo (servidor envia ANTES de S_SPAWN_PLAYER).
	 */
	void HandleLocalSpawnReady(int64 InEntityId, FVector PosCm, float YawDeg, int64 ServerTimeMs);

	UFUNCTION()
	void HandleZeusPlayerSpawned(int64 InEntityId, bool bIsLocal, FVector PosCm, float YawDeg, int64 ServerTimeMs);

	// Self-entity do sistema de rede novo (ENT_SELF): o cliente aprende o
	// proprio entityId. Substitui o caminho legacy OnPlayerSpawned(bIsLocal=true).
	UFUNCTION()
	void HandleSelfEntityAssigned(int64 InEntityId);

	UFUNCTION()
	void HandleZeusCharInfo(int64 InEntityId, const FString& CharName, const FString& GuildName);

	void TryApplyCachedCharInfo();

	UPROPERTY()
	TObjectPtr<UZeusNetworkSubsystem> ZeusNetwork;

	// Sistema de rede novo (canonico): fonte do self-entity (ENT_SELF).
	UPROPERTY()
	TObjectPtr<UZeusNetworkingClientSubsystem> NetClient;

	/** Instancia viva do painel admin (criada lazy no primeiro F8). */
	UPROPERTY(Transient)
	TObjectPtr<UUserWidget> AdminPanelInstance;

	bool bSpawnDelegateBound = false;

	/** Identidade autoritativa atribuida quando o servidor envia S_SPAWN_PLAYER local. */
	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "Zeus|Entity", meta = (AllowPrivateAccess = "true"))
	int64 ZeusEntityId = 0;

	float PendingMoveForward = 0.0f;
	float PendingMoveRight = 0.0f;

	bool bPendingJumpPressed = false;
	bool bPendingJumpReleased = false;
	bool bPreviousJumpHeld = false;
	/** Último estado IsFalling enviado ao servidor. Usado para forçar um envio
	 *  imediato em transições walking↔falling, evitando que o rate limit segure
	 *  a transição até o próximo heartbeat. */
	bool bPreviousFalling = false;

	float SendAccumulatorSec = 0.0f;
	float TimeSinceLastSendSec = 0.0f;
	int32 InputSequence = 0;
};
