#pragma once

#include "AbilitySystemInterface.h"
#include "CoreMinimal.h"
#include "GameFramework/PlayerState.h"
#include "Templates/SubclassOf.h"
#include "ZeusPlayerState.generated.h"

class FLifetimeProperty;
class UActorComponent;
class UZeusAbilitySystemComponent;
class UZeusAttributeSet;

/**
 *  PlayerState do MMO. Pattern UE5 idiomatico: o GameMode atribui
 *  `PlayerStateClass`, engine cria 1 instancia por PlayerController.
 *
 *  Responsabilidade — estado **publico** do jogador (visivel pra outros):
 *   - Identidade: charId, entityId, name, guild
 *   - Progressao publica: baseLevel, classId
 *   - Estado: alive/dead, in-combat (futuro)
 *
 *  Dados PRIVADOS (HP/SP atuais, stats brutos, inventory, hotbar) vivem
 *  em componentes anexados via Component Registry (vide abaixo).
 *
 *  Por que componentes no PlayerState e nao no Pawn:
 *   - **Sobrevive ao despawn** (morte/respawn — Pawn destrui, PlayerState
 *     mantem). Padrao MMO classico (rathena, TrinityCore).
 *   - **Spectator-friendly**: dados acessiveis sem ter pawn.
 *   - **1:1 com player** (Pawn pode trocar — vehicle, mount, possession).
 *
 *  ## Component Registry (Open-Closed)
 *
 *  Cada modulo (ZeusGAS, ZeusInventory, ZeusSkills futuros) declara
 *  sua component class em DefaultGame.ini:
 *
 *      [/Script/ZMMO.ZeusPlayerState]
 *      +ComponentClasses=/Script/ZeusGAS.ZeusGASComponent
 *      +ComponentClasses=/Script/ZeusInventory.ZeusInventoryComponent
 *      +ComponentClasses=/Script/ZeusSkills.ZeusSkillComponent
 *
 *  O construtor de AZeusPlayerState le essa lista (Config) e instancia
 *  cada classe como subobject. PlayerState fica fechado pra modificacao,
 *  modulos abertos pra extensao — adicionar Inventory NAO requer
 *  recompilar/editar AZeusPlayerState.
 *
 *  Acesso: `PlayerState->FindComponentByClass<UZeusGASComponent>()`
 *  ou o helper `GetZeusComponent<T>()`.
 */
UCLASS(Config = Game, BlueprintType)
class ZMMO_API AZeusPlayerState : public APlayerState, public IAbilitySystemInterface
{
	GENERATED_BODY()

public:
	AZeusPlayerState();

	// === IAbilitySystemInterface (UE5 GAS) ===
	//
	// Padrao Lyra: PlayerState implementa a interface; o ASC vive como
	// subobject do UZeusGASComponent (Component Registry). Isso permite que
	// qualquer Actor que receba este PlayerState resolva o ASC via cast
	// pra IAbilitySystemInterface OU via node BP "Get Ability System
	// Component" (que faz o mesmo internamente).
	//
	// Acesso BP: "Get Ability System Component" + pin do PlayerState.
	// Acesso C++: `Cast<IAbilitySystemInterface>(PlayerState)->GetAbilitySystemComponent()`.
	virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;

	/// Versao tipada do ASC custom — retorna direto UZeusAbilitySystemComponent
	/// (os 12 overrides que falam com o servidor custom via opcodes ZeusNetwork).
	/// Prefira este sobre o getter da interface no BP — evita cast + expoe a API
	/// do ASC custom (RequestActivateAbility, etc.).
	UFUNCTION(BlueprintPure, Category = "Zeus|GAS")
	UZeusAbilitySystemComponent* GetZeusAbilitySystemComponent() const;

	/// Helper BP-friendly pro AttributeSet tipado (template C++ nao expoe pra BP).
	UFUNCTION(BlueprintPure, Category = "Zeus|GAS")
	UZeusAttributeSet* GetZeusAttributeSet() const;

	// === Identidade publica ===
	//
	// Todos os campos publicos sao Replicated — hoje o cliente UE5 roda em
	// Standalone (servidor C++ standalone, sem replicacao UE5 ativa), entao
	// a marca nao tem efeito em runtime; deixa a classe pronta caso o projeto
	// passe a usar dedicated UE5 no futuro.

	/** EntityId autoritativo do server. 0 ate o spawn local confirmar. */
	UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "Zeus|Identity")
	int64 ZeusEntityId = 0;

	/** CharId (BIGINT no DB; FString pra preservar 64 bits em Blueprint). */
	UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "Zeus|Identity")
	FString CharId;

	/** Nome do char vindo do DB (`characters.name`). Setado por
	 *  AZeusCharacter::HandleLocalSpawnReady com o payload do
	 *  S_SPAWN_PLAYER. UI prefere isto a APlayerState::GetPlayerName(). */
	UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "Zeus|Identity")
	FString CharName;

	// === Progressao publica ===

	UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "Zeus|Progress")
	int32 BaseLevel = 1;

	UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "Zeus|Progress")
	int32 ClassId = 0;

	// === Guild (futuro) ===

	UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "Zeus|Guild")
	FString GuildName;

	virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;

	// === Helpers ===

	/// Setado por AZeusCharacter::HandleLocalSpawnReady (pose/EntityId)
	/// e atualizado por handlers de snapshot/level up para refletir dados
	/// publicos do char no PlayerState.
	UFUNCTION(BlueprintCallable, Category = "Zeus|Identity")
	void SetPublicIdentity(int64 InEntityId, const FString& InCharId,
	                       int32 InBaseLevel, int32 InClassId);

	/// Setado pelo handler do S_CHAR_INFO (chega antes de S_SPAWN_PLAYER) —
	/// metadados publicos do char vindos do servidor.
	UFUNCTION(BlueprintCallable, Category = "Zeus|Identity")
	void SetCharInfo(const FString& InCharName, const FString& InGuildName);

	/// Template helper: PS->GetZeusComponent<UZeusGASComponent>()
	template <typename T>
	T* GetZeusComponent() const
	{
		return Cast<T>(FindComponentByClass(T::StaticClass()));
	}

protected:
	/**
	 *  Component Registry — config-driven. Modulos registram suas classes
	 *  via DefaultGame.ini com prefix `+`. Construtor instancia cada uma
	 *  como CreateDefaultSubobject.
	 *
	 *  TSubclassOf (nao TSoftClassPtr): config UE resolve classes nativas
	 *  diretamente. Modulos C++ precisam estar carregados antes do CDO ser
	 *  instanciado — garantido por LoadingPhase=PreDefault no .uproject.
	 */
	UPROPERTY(Config, EditDefaultsOnly, Category = "Zeus|Components")
	TArray<TSubclassOf<UActorComponent>> ComponentClasses;
};
