#include "ZeusPlayerState.h"

#include "AbilitySystemComponent.h"
#include "Components/ActorComponent.h"
#include "Misc/ConfigCacheIni.h"
#include "Net/UnrealNetwork.h"
#include "ZMMO.h"
#include "ZeusAbilitySystemComponent.h"
#include "ZeusAttributeSet.h"
#include "ZeusGASComponent.h"

AZeusPlayerState::AZeusPlayerState()
{
	// Component Registry: instancia cada classe declarada em
	// DefaultGame.ini::[/Script/ZMMO.ZeusPlayerState]+ComponentClasses=...
	//
	// Le manualmente via GConfig para evitar timing do UPROPERTY(Config),
	// que requer LoadConfig() ja ter rodado no CDO antes da instance ser
	// construida. Pra propriedades em arrays config com `+` syntax, lendo
	// direto e mais previsivel.
	if (ComponentClasses.Num() == 0 && GConfig)
	{
		TArray<FString> ClassPaths;
		GConfig->GetArray(TEXT("/Script/ZMMO.ZeusPlayerState"),
		                  TEXT("ComponentClasses"),
		                  ClassPaths,
		                  GGameIni);
		for (const FString& Path : ClassPaths)
		{
			if (UClass* C = LoadClass<UActorComponent>(nullptr, *Path))
			{
				ComponentClasses.Add(C);
			}
			else
			{
				UE_LOG(LogZMMO, Warning,
					TEXT("ZeusPlayerState: ComponentClasses path nao resolveu: %s"), *Path);
			}
		}
	}

	UE_LOG(LogZMMO, Log,
		TEXT("AZeusPlayerState ctor: %d componente(s) no registry (instance=%s)"),
		ComponentClasses.Num(), *GetName());

	int32 SubobjectIndex = 0;
	for (const TSubclassOf<UActorComponent>& CompClassRef : ComponentClasses)
	{
		UClass* CompClass = CompClassRef.Get();
		if (!CompClass)
		{
			UE_LOG(LogZMMO, Warning,
				TEXT("ZeusPlayerState: ComponentClass null em ComponentClasses[%d]"),
				SubobjectIndex);
			++SubobjectIndex;
			continue;
		}
		const FName CompName = MakeUniqueObjectName(this, CompClass,
			FName(*FString::Printf(TEXT("ZeusComp_%d_%s"), SubobjectIndex, *CompClass->GetName())));
		// Overload runtime de CreateDefaultSubobject retorna UObject* (sem template) —
		// precisa cast para UActorComponent.
		UActorComponent* Comp = Cast<UActorComponent>(CreateDefaultSubobject(CompName, CompClass, CompClass,
			/*bIsRequired*/ false, /*bIsTransient*/ false));
		if (Comp)
		{
			UE_LOG(LogZMMO, Log,
				TEXT("ZeusPlayerState: componente registrado [%d] %s"),
				SubobjectIndex, *CompClass->GetName());
		}
		else
		{
			UE_LOG(LogZMMO, Warning,
				TEXT("ZeusPlayerState: CreateDefaultSubobject falhou para %s"),
				*CompClass->GetName());
		}
		++SubobjectIndex;
	}
}

void AZeusPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(AZeusPlayerState, ZeusEntityId);
	DOREPLIFETIME(AZeusPlayerState, CharId);
	DOREPLIFETIME(AZeusPlayerState, CharName);
	DOREPLIFETIME(AZeusPlayerState, BaseLevel);
	DOREPLIFETIME(AZeusPlayerState, ClassId);
	DOREPLIFETIME(AZeusPlayerState, GuildName);
}

void AZeusPlayerState::SetPublicIdentity(int64 InEntityId, const FString& InCharId,
                                          int32 InBaseLevel, int32 InClassId)
{
	ZeusEntityId = InEntityId;
	CharId = InCharId;
	BaseLevel = InBaseLevel;
	ClassId = InClassId;
}

void AZeusPlayerState::SetCharInfo(const FString& InCharName, const FString& InGuildName)
{
	CharName = InCharName;
	GuildName = InGuildName;
}

UAbilitySystemComponent* AZeusPlayerState::GetAbilitySystemComponent() const
{
	// ASC vive como subobject do UZeusGASComponent (que esta no Component
	// Registry). UE5 GAS nodes BP ("Get Ability System Component", "Apply
	// Gameplay Effect To Target", etc.) chamam isto via IAbilitySystemInterface.
	// O retorno e o ponteiro upcastado pra UAbilitySystemComponent (base) —
	// mas em runtime e' UZeusAbilitySystemComponent (cast tipado via
	// GetZeusAbilitySystemComponent abaixo).
	return GetZeusAbilitySystemComponent();
}

UZeusAbilitySystemComponent* AZeusPlayerState::GetZeusAbilitySystemComponent() const
{
	if (UZeusGASComponent* GAS = GetZeusComponent<UZeusGASComponent>())
	{
		return GAS->AbilitySystemComponent.Get();
	}
	return nullptr;
}

UZeusAttributeSet* AZeusPlayerState::GetZeusAttributeSet() const
{
	if (UZeusGASComponent* GAS = GetZeusComponent<UZeusGASComponent>())
	{
		return GAS->AttributeSet.Get();
	}
	return nullptr;
}
