// © 2025 Naked People Team. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "StatsManager.generated.h"

class UClothingManager;
class ANakedDesireCharacter;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FAttributeUpdateSignature, float, CurrentValue, float, MaxValue);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FObserversChangedSignature);


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class NAKEDDESIRE_API UStatsManager : public UActorComponent
{
	GENERATED_BODY()

	float Embarrassment = 0.0f;
	float MaxEmbarrassment = 100.0f;
	float Energy = 1000.0f;
	float MaxEnergy = 1000.0f;

public:
	UStatsManager();

	UPROPERTY(EditDefaultsOnly, Category = "Embarrassment")
	float EmbarrassmentGainRate = 10.0f;

	UPROPERTY(EditDefaultsOnly, Category = "Embarrassment")
	float EmbarrassmentDecayRate = 1.0f;

	// Crowd scaling: N active observers multiply embarrassment gain by 1 + ObserverDensityScale * ln(N).
	// A single observer (N=1) is the x1 baseline; extra observers add with diminishing returns.
	UPROPERTY(EditDefaultsOnly, Category = "Embarrassment")
	float ObserverDensityScale = 0.5f;

	virtual void BeginPlay() override;
	virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
	void Init(UClothingManager* InClothingManager);

	float Stamina = 100.0f;
	float MaxStamina = 100.0f;

	// Called by NPCAIController when an NPC gains or loses sight of the player. Observer is the
	// observing pawn; its live location is re-traced each tick to weight embarrassment by the
	// body parts that observer can actually see (GDD §7.1). Weight is the observer's NPC-type
	// multiplier (§10.2) — Walkers read low, Stalkers high; ignored on the un-observe path.
	void SetObserved(bool bObserved, AActor* Observer, float Weight = 1.0f);

	// Number of NPCs currently perceiving the player (used by commission objectives, §13.4).
	UFUNCTION(BlueprintPure)
	int32 GetObserverCount() const;

	// Fires whenever the observer set changes (an NPC gains or loses sight of the player).
	UPROPERTY(BlueprintAssignable)
	FObserversChangedSignature OnObserversChanged;

	UFUNCTION(BlueprintCallable)
	void IncreaseEmbarrassment(float Amount);
	void DecreaseEmbarrassment(float Amount);
	void DecreaseStamina(float Amount);
	void IncreaseStamina(float Amount);
	void DecreaseEnergy(float Amount);
	void RestoreEnergy();

	// Restore every attribute to its full / baseline value (Embarrassment cleared, Energy and
	// Stamina refilled). Called when the player is sent home after a session loss (GDD §4.4).
	UFUNCTION(BlueprintCallable)
	void ResetStats();

	UPROPERTY(BlueprintAssignable)
	FAttributeUpdateSignature EmbarrassmentUpdate;
	
	UPROPERTY(BlueprintAssignable)
	FAttributeUpdateSignature StaminaUpdate;
	
	UPROPERTY(BlueprintAssignable)
	FAttributeUpdateSignature EnergyUpdate;
	
private:
	// Re-traces every current observer against the player's live coverage and returns the
	// normalized embarrassment-gain factor (0 when nothing revealing is currently observed).
	float ComputeObservedExposureRate();

	UPROPERTY()
	TObjectPtr<UClothingManager> ClothingManager;

	UPROPERTY()
	TObjectPtr<ANakedDesireCharacter> OwnerCharacter;

	// One perceiving NPC and its observation weight (§10.2). Weak so a destroyed observer drops
	// out without dangling.
	struct FObserverEntry
	{
		TWeakObjectPtr<AActor> Actor;
		float Weight = 1.0f;
	};

	// NPCs currently perceiving the player (added/removed via SetObserved). Stale entries are
	// compacted during the tick.
	TArray<FObserverEntry> Observers;
};
