package erecon

import (
	"strings"

	"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
	"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/xerr"
)

func findStepIndex(payment *agg.Payment, event *normalizedEvent) (int, error) {
	if payment == nil {
		return -1, ErrStepNotFound
	}
	if event == nil {
		return -1, ErrStepNotFound
	}

	if stepRef := strings.TrimSpace(event.stepRef); stepRef != "" {
		for i := range payment.StepExecutions {
			if strings.EqualFold(strings.TrimSpace(payment.StepExecutions[i].StepRef), stepRef) {
				return i, nil
			}
		}
		return -1, xerr.Wrapf(ErrStepNotFound, "step_ref=%s", stepRef)
	}

	matches := make([]int, 0, 1)
	for i := range payment.StepExecutions {
		if stepMatchesAnyRef(payment.StepExecutions[i], event.matchRefs) {
			matches = append(matches, i)
		}
	}
	switch len(matches) {
	case 0:
		return -1, ErrStepNotFound
	case 1:
		return matches[0], nil
	default:
		return -1, ErrAmbiguousStepMatch
	}
}

func stepMatchesAnyRef(step agg.StepExecution, refs []agg.ExternalRef) bool {
	if len(refs) == 0 || len(step.ExternalRefs) == 0 {
		return false
	}
	for i := range refs {
		if hasExternalRef(step.ExternalRefs, refs[i]) {
			return true
		}
	}
	return false
}

func hasExternalRef(existing []agg.ExternalRef, ref agg.ExternalRef) bool {
	kind := strings.TrimSpace(ref.Kind)
	value := strings.TrimSpace(ref.Ref)
	gatewayID := strings.TrimSpace(ref.GatewayInstanceID)
	if kind == "" || value == "" {
		return false
	}
	for i := range existing {
		candidate := existing[i]
		if !strings.EqualFold(strings.TrimSpace(candidate.Kind), kind) {
			continue
		}
		if !strings.EqualFold(strings.TrimSpace(candidate.Ref), value) {
			continue
		}
		if gatewayID != "" && strings.TrimSpace(candidate.GatewayInstanceID) != "" && !strings.EqualFold(strings.TrimSpace(candidate.GatewayInstanceID), gatewayID) {
			continue
		}
		return true
	}
	return false
}

func mergeExternalRefs(existing []agg.ExternalRef, additions []agg.ExternalRef) ([]agg.ExternalRef, bool) {
	if len(additions) == 0 {
		return cloneRefs(existing), false
	}
	out := cloneRefs(existing)
	changed := false
	for i := range additions {
		ref := additions[i]
		if hasExternalRef(out, ref) {
			continue
		}
		out = append(out, agg.ExternalRef{
			GatewayInstanceID: strings.TrimSpace(ref.GatewayInstanceID),
			Kind:              strings.TrimSpace(ref.Kind),
			Ref:               strings.TrimSpace(ref.Ref),
		})
		changed = true
	}
	return out, changed
}
