#pragma once

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

class FLifetimeProperty;
class UActorComponent;

/**
 *  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 (ZMMOAttributes, ZMMOInventory, ZMMOSkills futuros) declara
 *  sua component class em DefaultGame.ini:
 *
 *      [/Script/ZMMO.ZMMOPlayerState]
 *      +ComponentClasses=/Script/ZMMOAttributes.ZMMOAttributeComponent
 *      +ComponentClasses=/Script/ZMMOInventory.ZMMOInventoryComponent
 *      +ComponentClasses=/Script/ZMMOSkills.ZMMOSkillComponent
 *
 *  O construtor de AZMMOPlayerState 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 AZMMOPlayerState.
 *
 *  Acesso: `PlayerState->FindComponentByClass<UZMMOAttributeComponent>()`
 *  ou o helper `GetZMMOComponent<T>()`.
 */
UCLASS(Config = Game, BlueprintType)
class ZMMO_API AZMMOPlayerState : public APlayerState
{
	GENERATED_BODY()

public:
	AZMMOPlayerState();

	// === 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 = "ZMMO|Identity")
	int64 ZMMOEntityId = 0;

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

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

	// === Progressao publica ===

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

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

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

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

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

	// === Helpers ===

	/// Setado por AZMMOPlayerCharacter::HandleLocalSpawnReady (pose/EntityId)
	/// e atualizado por handlers de snapshot/level up para refletir dados
	/// publicos do char no PlayerState.
	UFUNCTION(BlueprintCallable, Category = "ZMMO|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 = "ZMMO|Identity")
	void SetCharInfo(const FString& InCharName, const FString& InGuildName);

	/// Template helper: PS->GetZMMOComponent<UZMMOAttributeComponent>()
	template <typename T>
	T* GetZMMOComponent() 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 = "ZMMO|Components")
	TArray<TSubclassOf<UActorComponent>> ComponentClasses;
};
