package xplan

import (
	"github.com/tech/sendico/pkg/discovery"
	"strings"

	"github.com/tech/sendico/payments/storage/model"
	paymenttypes "github.com/tech/sendico/pkg/payments/types"
)

func (e *expansion) appendMain(step Step) string {
	step = normalizeStep(step)
	if len(step.DependsOn) == 0 && strings.TrimSpace(e.lastMainRef) != "" {
		step.DependsOn = []string{e.lastMainRef}
	}
	if len(step.CommitAfter) == 0 && step.CommitPolicy != model.CommitPolicyUnspecified {
		step.CommitAfter = cloneStringSlice(step.DependsOn)
	}
	step.StepRef = e.nextRef(firstNonEmpty(step.StepRef, step.StepCode))
	if strings.TrimSpace(step.StepCode) == "" {
		step.StepCode = step.StepRef
	}
	e.steps = append(e.steps, step)
	e.lastMainRef = step.StepRef
	return step.StepRef
}

func (e *expansion) appendBranch(step Step) string {
	step = normalizeStep(step)
	if len(step.CommitAfter) == 0 && step.CommitPolicy != model.CommitPolicyUnspecified {
		step.CommitAfter = cloneStringSlice(step.DependsOn)
	}
	step.StepRef = e.nextRef(firstNonEmpty(step.StepRef, step.StepCode))
	if strings.TrimSpace(step.StepCode) == "" {
		step.StepCode = step.StepRef
	}
	e.steps = append(e.steps, step)
	return step.StepRef
}

func (e *expansion) nextRef(base string) string {
	token := sanitizeToken(base)
	if token == "" {
		token = "step"
	}
	count := e.refSeq[token]
	e.refSeq[token] = count + 1
	if count == 0 {
		return token
	}
	return token + "_" + itoa(count+1)
}

func (e *expansion) needsFeeStep(hop normalizedHop) bool {
	if e == nil || len(e.feeHops) == 0 {
		return false
	}
	key := observedKey(hop)
	if _, ok := e.feeHops[key]; !ok {
		return false
	}
	if _, emitted := e.feeEmitted[key]; emitted {
		return false
	}
	return true
}

func (e *expansion) markFeeEmitted(hop normalizedHop) {
	if e == nil {
		return
	}
	if e.feeEmitted == nil {
		e.feeEmitted = map[string]struct{}{}
	}
	e.feeEmitted[observedKey(hop)] = struct{}{}
}

func normalizeStep(step Step) Step {
	step.StepRef = strings.TrimSpace(step.StepRef)
	step.StepCode = strings.TrimSpace(step.StepCode)
	step.Gateway = strings.TrimSpace(step.Gateway)
	step.InstanceID = strings.TrimSpace(step.InstanceID)
	step.UserLabel = strings.TrimSpace(step.UserLabel)
	step.Comment = strings.TrimSpace(step.Comment)
	step.Visibility = model.NormalizeReportVisibility(step.Visibility)
	step.CommitPolicy = normalizeCommitPolicy(step.CommitPolicy)
	step.DependsOn = normalizeStringList(step.DependsOn)
	step.CommitAfter = normalizeStringList(step.CommitAfter)
	step.Metadata = normalizeMetadata(step.Metadata)
	return step
}

func normalizeCommitPolicy(policy model.CommitPolicy) model.CommitPolicy {
	switch strings.ToUpper(strings.TrimSpace(string(policy))) {
	case string(model.CommitPolicyImmediate):
		return model.CommitPolicyImmediate
	case string(model.CommitPolicyAfterSuccess):
		return model.CommitPolicyAfterSuccess
	case string(model.CommitPolicyAfterFailure):
		return model.CommitPolicyAfterFailure
	case string(model.CommitPolicyAfterCanceled):
		return model.CommitPolicyAfterCanceled
	default:
		return model.CommitPolicyUnspecified
	}
}

func defaultVisibilityForAction(action model.RailOperation, role paymenttypes.QuoteRouteHopRole) model.ReportVisibility {
	switch action {
	case discovery.RailOperationSend, discovery.RailOperationFXConvert, discovery.RailOperationObserveConfirm:
		if role == paymenttypes.QuoteRouteHopRoleDestination {
			return model.ReportVisibilityUser
		}
		return model.ReportVisibilityBackoffice
	default:
		return model.ReportVisibilityHidden
	}
}

func defaultUserLabel(
	action model.RailOperation,
	rail model.Rail,
	role paymenttypes.QuoteRouteHopRole,
	kind model.PaymentKind,
) string {
	if role != paymenttypes.QuoteRouteHopRoleDestination {
		return ""
	}
	switch action {
	case discovery.RailOperationSend:
		if kind == model.PaymentKindPayout && rail == discovery.RailCardPayout {
			return "Card payout submitted"
		}
		return "Transfer submitted"
	case discovery.RailOperationFXConvert:
		return "FX conversion submitted"
	case discovery.RailOperationObserveConfirm:
		if kind == model.PaymentKindPayout && rail == discovery.RailCardPayout {
			return "Card payout confirmed"
		}
		return "Transfer confirmed"
	default:
		return ""
	}
}
