package erecon

import (
	"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
	"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/ostate"
)

func reduceAggregateState(payment *agg.Payment, event *normalizedEvent, sm ostate.StateMachine) (bool, error) {
	if payment == nil || sm == nil {
		return false, nil
	}

	target := deriveAggregateTarget(payment, event, sm)
	return applyAggregateTarget(payment, target, sm)
}

func deriveAggregateTarget(payment *agg.Payment, event *normalizedEvent, sm ostate.StateMachine) agg.State {
	if payment == nil {
		return agg.StateUnspecified
	}
	if event != nil && event.forceAggregateFailedValue() {
		return agg.StateFailed
	}

	hasNeedsAttention := false
	hasWork := false
	allTerminalSuccessOrSkipped := len(payment.StepExecutions) > 0

	for i := range payment.StepExecutions {
		state := payment.StepExecutions[i].State
		switch state {
		case agg.StepStateCompleted, agg.StepStateSkipped:
			hasWork = true
		case agg.StepStatePending, agg.StepStateUnspecified:
			allTerminalSuccessOrSkipped = false
		case agg.StepStateNeedsAttention:
			hasWork = true
			hasNeedsAttention = true
			allTerminalSuccessOrSkipped = false
		case agg.StepStateRunning, agg.StepStateFailed:
			hasWork = true
			allTerminalSuccessOrSkipped = false
		default:
			allTerminalSuccessOrSkipped = false
		}
	}

	if allTerminalSuccessOrSkipped {
		return agg.StateSettled
	}
	if hasNeedsAttention || (event != nil && event.forceAggregateNeedsAttentionValue()) {
		return agg.StateNeedsAttention
	}
	if hasWork {
		return agg.StateExecuting
	}
	if sm.IsAggregateTerminal(payment.State) {
		return payment.State
	}
	return agg.StateCreated
}

func applyAggregateTarget(payment *agg.Payment, target agg.State, sm ostate.StateMachine) (bool, error) {
	if payment == nil || sm == nil {
		return false, nil
	}
	current := payment.State
	if current == target {
		return false, nil
	}
	if sm.IsAggregateTerminal(current) {
		return false, nil
	}

	original := current
	for i := 0; i < 6 && current != target; i++ {
		if sm.EnsureAggregateTransition(current, target) == nil {
			current = target
			break
		}
		next, ok := nextAggregateHop(current, target)
		if !ok {
			break
		}
		if sm.EnsureAggregateTransition(current, next) != nil {
			break
		}
		current = next
	}
	if current != target {
		return false, nil
	}
	payment.State = current
	return payment.State != original, nil
}

func nextAggregateHop(current, target agg.State) (agg.State, bool) {
	switch current {
	case agg.StateUnspecified:
		return agg.StateCreated, true
	case agg.StateCreated:
		if target == agg.StateFailed {
			return agg.StateFailed, true
		}
		if target == agg.StateExecuting {
			return agg.StateExecuting, true
		}
		return agg.StateExecuting, true
	case agg.StateExecuting:
		return target, true
	case agg.StateNeedsAttention:
		if target == agg.StateCreated {
			return agg.StateExecuting, true
		}
		return target, true
	default:
		return agg.StateUnspecified, false
	}
}
