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


#include "Commission.h"

#include "CommissionObjective.h"

void UCommission::Accept(ANakedDesireCharacter* InPlayer)
{
	if (State != ECommissionState::Offered)
		return;

	Player = InPlayer;
	SetState(ECommissionState::Accepted);
	ArmObjectives();

	// Arming evaluates immediately, so a commission already satisfied on accept completes now.
	if (AreAllObjectivesSatisfied())
		Complete();
}

void UCommission::Abandon()
{
	if (State != ECommissionState::Accepted)
		return;

	DisarmObjectives();
	SetState(ECommissionState::Offered);
}

void UCommission::Expire()
{
	if (State != ECommissionState::Accepted)
		return;

	DisarmObjectives();
	SetState(ECommissionState::Expired);
}

void UCommission::RestoreState(ECommissionState SavedState, ANakedDesireCharacter* InPlayer)
{
	Player = InPlayer;

	if (SavedState == ECommissionState::Accepted)
	{
		SetState(ECommissionState::Accepted);
		ArmObjectives();
		if (AreAllObjectivesSatisfied())
			Complete();
	}
	else
	{
		SetState(SavedState);
	}
}

void UCommission::Complete()
{
	if (State != ECommissionState::Accepted)
		return; // already resolved — guards against double completion / double payout

	DisarmObjectives();
	SetState(ECommissionState::Completed);
	OnCompleted.Broadcast(this);
}

bool UCommission::AreAllObjectivesSatisfied() const
{
	if (Objectives.Num() == 0)
		return false; // a commission with no objectives never auto-completes

	for (const UCommissionObjective* Objective : Objectives)
	{
		if (!Objective || !Objective->IsSatisfied())
			return false;
	}
	return true;
}

void UCommission::ArmObjectives()
{
	// Subscribe to all objectives up front; activation differs by mode.
	for (UCommissionObjective* Objective : Objectives)
	{
		if (Objective)
			Objective->OnStateChanged.AddUObject(this, &UCommission::HandleObjectiveStateChanged);
	}

	if (bSequentialObjectives)
	{
		ActiveObjectiveIndex = INDEX_NONE;
		AdvanceSequential();
	}
	else
	{
		for (UCommissionObjective* Objective : Objectives)
		{
			if (Objective)
				Objective->Activate(Player);
		}
	}
}

void UCommission::DisarmObjectives()
{
	for (UCommissionObjective* Objective : Objectives)
	{
		if (!Objective)
			continue;

		Objective->OnStateChanged.RemoveAll(this);
		Objective->Deactivate();
	}

	ActiveObjectiveIndex = INDEX_NONE;
}

void UCommission::AdvanceSequential()
{
	for (int32 Index = 0; Index < Objectives.Num(); ++Index)
	{
		UCommissionObjective* Objective = Objectives[Index];
		if (!Objective)
			continue;

		if (!Objective->IsSatisfied())
		{
			// Activate the first unsatisfied step (only if it isn't already the armed one — Activate
			// resets progress, so re-activating the live objective would wipe its hold timer).
			if (ActiveObjectiveIndex != Index)
			{
				ActiveObjectiveIndex = Index;
				Objective->Activate(Player);
			}
			return;
		}
	}

	ActiveObjectiveIndex = INDEX_NONE;
	Complete();
}

void UCommission::SetState(ECommissionState NewState)
{
	State = NewState;
	OnStateChanged.Broadcast(this);
}

void UCommission::HandleObjectiveStateChanged(UCommissionObjective* Objective)
{
	if (State != ECommissionState::Accepted)
		return;

	if (bSequentialObjectives)
		AdvanceSequential(); // the armed step finished -> activate the next, or complete
	else if (AreAllObjectivesSatisfied())
		Complete();
}