package orchestrator

import (
	"context"
	"sort"
	"strings"

	"github.com/tech/sendico/payments/orchestrator/storage/model"
	"github.com/tech/sendico/pkg/merrors"
)

func selectPlanTemplate(ctx context.Context, templates PlanTemplateStore, sourceRail, destRail model.Rail, network string) (*model.PaymentPlanTemplate, error) {
	if templates == nil {
		return nil, merrors.InvalidArgument("plan builder: plan templates store is required")
	}
	enabled := true
	result, err := templates.List(ctx, &model.PaymentPlanTemplateFilter{
		FromRail:  sourceRail,
		ToRail:    destRail,
		IsEnabled: &enabled,
	})
	if err != nil {
		return nil, err
	}
	if result == nil || len(result.Items) == 0 {
		return nil, merrors.InvalidArgument("plan builder: plan template missing")
	}

	candidates := make([]*model.PaymentPlanTemplate, 0, len(result.Items))
	for _, tpl := range result.Items {
		if tpl == nil || !tpl.IsEnabled {
			continue
		}
		if tpl.FromRail != sourceRail || tpl.ToRail != destRail {
			continue
		}
		if !templateMatchesNetwork(tpl, network) {
			continue
		}
		if err := validatePlanTemplate(tpl); err != nil {
			return nil, err
		}
		candidates = append(candidates, tpl)
	}
	if len(candidates) == 0 {
		return nil, merrors.InvalidArgument("plan builder: plan template missing")
	}

	sort.Slice(candidates, func(i, j int) bool {
		pi := templatePriority(candidates[i], network)
		pj := templatePriority(candidates[j], network)
		if pi != pj {
			return pi < pj
		}
		if candidates[i].Network != candidates[j].Network {
			return candidates[i].Network < candidates[j].Network
		}
		return candidates[i].ID.Hex() < candidates[j].ID.Hex()
	})

	return candidates[0], nil
}

func templateMatchesNetwork(template *model.PaymentPlanTemplate, network string) bool {
	if template == nil {
		return false
	}
	templateNetwork := strings.ToUpper(strings.TrimSpace(template.Network))
	net := strings.ToUpper(strings.TrimSpace(network))
	if templateNetwork == "" {
		return true
	}
	if net == "" {
		return false
	}
	return strings.EqualFold(templateNetwork, net)
}

func templatePriority(template *model.PaymentPlanTemplate, network string) int {
	if template == nil {
		return 2
	}
	templateNetwork := strings.ToUpper(strings.TrimSpace(template.Network))
	net := strings.ToUpper(strings.TrimSpace(network))
	if net != "" && strings.EqualFold(templateNetwork, net) {
		return 0
	}
	if templateNetwork == "" {
		return 1
	}
	return 2
}

func validatePlanTemplate(template *model.PaymentPlanTemplate) error {
	if template == nil {
		return merrors.InvalidArgument("plan builder: plan template is required")
	}
	if len(template.Steps) == 0 {
		return merrors.InvalidArgument("plan builder: plan template steps are required")
	}
	seen := map[string]struct{}{}
	for _, step := range template.Steps {
		id := strings.TrimSpace(step.StepID)
		if id == "" {
			return merrors.InvalidArgument("plan builder: plan template step id is required")
		}
		if _, exists := seen[id]; exists {
			return merrors.InvalidArgument("plan builder: plan template step id must be unique")
		}
		seen[id] = struct{}{}
		if strings.TrimSpace(step.Operation) == "" {
			return merrors.InvalidArgument("plan builder: plan template operation is required")
		}
	}
	for _, step := range template.Steps {
		for _, dep := range step.DependsOn {
			if _, ok := seen[strings.TrimSpace(dep)]; !ok {
				return merrors.InvalidArgument("plan builder: plan template dependency missing")
			}
		}
		for _, dep := range step.CommitAfter {
			if _, ok := seen[strings.TrimSpace(dep)]; !ok {
				return merrors.InvalidArgument("plan builder: plan template commit dependency missing")
			}
		}
	}
	return nil
}
