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

#pragma once

#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "CommissionObjective.generated.h"

class ANakedDesireCharacter;
class UCommissionObjective;
class UCommissionConstraint;

DECLARE_MULTICAST_DELEGATE_OneParam(FOnObjectiveStateChangedSignature, UCommissionObjective*);

/**
 * One typed objective step (GDD §13.4). Replaces the old Goal/Restriction split: a step owns its own
 * condition and, optionally, a sustained-hold requirement (RequiredHoldSeconds). Subclasses override
 * IsConditionMet() and call NotifyConditionChanged() when their inputs change; count-style objectives
 * can instead call MarkSatisfied() directly. The hold timer lives here, so "expose for N seconds" and
 * "expose once" differ only by a data value, not a class.
 */
UCLASS(Abstract, EditInlineNew, BlueprintType)
class NAKEDDESIRE_API UCommissionObjective : public UObject
{
	GENERATED_BODY()

public:
	FOnObjectiveStateChangedSignature OnStateChanged;

	void Activate(ANakedDesireCharacter* InPlayer);
	void Deactivate();

	bool IsSatisfied() const { return bSatisfied; }

	UFUNCTION(BlueprintPure)
	virtual FText GetDescription() const;

	// 0..1 for UI. Default: 1 when satisfied, partial while a hold timer runs, else 0.
	UFUNCTION(BlueprintPure)
	virtual float GetProgress() const;

	// Combined "while Y" text for every attached constraint (e.g. "while at Beach, during Night"),
	// or empty when the objective is unconstrained. For UI to show alongside GetDescription().
	UFUNCTION(BlueprintPure)
	FText GetConstraintsDescription() const;

protected:
	UPROPERTY()
	ANakedDesireCharacter* Player = nullptr;

	// Continuous seconds the condition must hold to satisfy. 0 = satisfied the instant it is first met.
	UPROPERTY(EditDefaultsOnly, meta = (ClampMin = 0))
	float RequiredHoldSeconds = 0.0f;

	// "While Y" gates: the objective only counts progress while every constraint reports IsMet().
	UPROPERTY(EditDefaultsOnly, Instanced)
	TArray<TObjectPtr<UCommissionConstraint>> Constraints;

	virtual void OnActivate() {}
	virtual void OnDeactivate() {}

	// Sustained objectives override this; the base ANDs it with the constraints to drive the hold timer.
	virtual bool IsConditionMet() const { return false; }

	// True when every attached constraint is currently satisfied (count-style subclasses gate on this too).
	bool AreConstraintsMet() const;

	// Re-check the condition and (re)arm or cancel the hold timer accordingly.
	void NotifyConditionChanged();

	void MarkSatisfied();

	bool bSatisfied = false;

private:
	void HandleConstraintChanged();
	void OnHoldElapsed();
	void ClearHoldTimer();

	FTimerHandle HoldTimerHandle;
	float HoldStartTime = 0.0f;
};