package ostate

import (
	"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
	"github.com/tech/sendico/pkg/mlogger"

	"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/xerr"
	"go.uber.org/zap"
)

type svc struct {
	logger mlogger.Logger
}

func (s *svc) CanTransitionAggregate(from, to agg.State) bool {
	return canTransitionAggregate(from, to)
}

func (s *svc) CanTransitionStep(from, to agg.StepState) bool {
	return canTransitionStep(from, to)
}

func (s *svc) EnsureAggregateTransition(from, to agg.State) error {
	logger := s.logger
	logger.Debug("Starting Ensure aggregate transition",
		zap.String("from", string(from)),
		zap.String("to", string(to)),
	)
	if !isKnownAggregateState(from) {
		err := xerr.Wrapf(ErrUnknownAggregateState, "%q", from)
		logger.Warn("Failed to ensure aggregate transition", zap.Error(err))
		return err
	}
	if !isKnownAggregateState(to) {
		err := xerr.Wrapf(ErrUnknownAggregateState, "%q", to)
		logger.Warn("Failed to ensure aggregate transition", zap.Error(err))
		return err
	}
	if canTransitionAggregate(from, to) {
		logger.Debug("Completed Ensure aggregate transition", zap.Bool("allowed", true))
		return nil
	}
	err := xerr.Wrapf(ErrAggregateTransitionNotAllowed, "%s -> %s", from, to)
	logger.Warn("Failed to ensure aggregate transition", zap.Error(err))
	return err
}

func (s *svc) EnsureStepTransition(from, to agg.StepState) error {
	logger := s.logger
	logger.Debug("Starting Ensure step transition",
		zap.String("from", string(from)),
		zap.String("to", string(to)),
	)
	if !isKnownStepState(from) {
		err := xerr.Wrapf(ErrUnknownStepState, "%q", from)
		logger.Warn("Failed to ensure step transition", zap.Error(err))
		return err
	}
	if !isKnownStepState(to) {
		err := xerr.Wrapf(ErrUnknownStepState, "%q", to)
		logger.Warn("Failed to ensure step transition", zap.Error(err))
		return err
	}
	if canTransitionStep(from, to) {
		logger.Debug("Completed Ensure step transition", zap.Bool("allowed", true))
		return nil
	}
	err := xerr.Wrapf(ErrStepTransitionNotAllowed, "%s -> %s", from, to)
	logger.Warn("Failed to ensure step transition", zap.Error(err))
	return err
}

func (s *svc) IsAggregateTerminal(state agg.State) bool {
	_, ok := aggregateTerminalStates[state]
	return ok
}

func (s *svc) IsStepTerminal(state agg.StepState) bool {
	_, ok := stepTerminalStates[state]
	return ok
}

func canTransitionAggregate(from, to agg.State) bool {
	if from == to {
		return isKnownAggregateState(from)
	}
	allowed, ok := aggregateTransitions[from]
	if !ok {
		return false
	}
	_, ok = allowed[to]
	return ok
}

func canTransitionStep(from, to agg.StepState) bool {
	if from == to {
		return isKnownStepState(from)
	}
	allowed, ok := stepTransitions[from]
	if !ok {
		return false
	}
	_, ok = allowed[to]
	return ok
}

func isKnownAggregateState(state agg.State) bool {
	_, ok := aggregateTransitions[state]
	return ok
}

func isKnownStepState(state agg.StepState) bool {
	_, ok := stepTransitions[state]
	return ok
}
