// Copyright IBM Corp. 2020, 2026
// SPDX-License-Identifier: BUSL-1.1

package endpoint

import (
	"fmt"
	"net"
	"strings"
)

// getOpts iterates the inbound Options and returns a struct
func getOpts(opt ...Option) (options, error) {
	opts := getDefaultOptions()
	for _, o := range opt {
		if o == nil {
			continue
		}
		if err := o(&opts); err != nil {
			return options{}, err
		}
	}
	return opts, nil
}

// Option - how Options are passed as arguments
type Option func(*options) error

// options = how options are represented
type options struct {
	withIpAddrs  []string
	withDnsNames []string
	withMatchers []matcher
}

func getDefaultOptions() options {
	return options{}
}

// WithIpAddrs contains IP addresses to add into the endpoint possibilities.
// If an IP cannot be parsed, this function will error.
func WithIpAddrs(with []string) Option {
	return func(o *options) error {
		for _, addr := range with {
			ip := net.ParseIP(addr)
			if ip == nil {
				return fmt.Errorf("input '%s' is not parseable as an ip address", addr)
			}
			o.withIpAddrs = append(o.withIpAddrs, addr)
		}
		return nil
	}
}

// WithDnsNames contains DNS names to add into the endpoint possibilities
func WithDnsNames(with []string) Option {
	return func(o *options) error {
		o.withDnsNames = with
		return nil
	}
}

// WithPreferenceOrder contains the preference order specification. If one of
// the preferences cannot be parsed, this function will error. Internally it
// builds up a set of matchers.
func WithPreferenceOrder(with []string) Option {
	return func(o *options) error {
		for _, input := range with {
			var m matcher
			switch {
			case strings.HasPrefix(input, "cidr:"):
				// Make sure ParseCIDR won't choke on a bare address
				cidr := strings.TrimPrefix(input, "cidr:")
				if !strings.Contains(cidr, "/") {
					// See if it seems like an IPv6 address vs. IPv4; colons are
					// a good way to check this
					if strings.Contains(cidr, ":") {
						cidr = fmt.Sprintf("%s/128", cidr)
					} else {
						cidr = fmt.Sprintf("%s/32", cidr)
					}
				}
				_, ipNet, err := net.ParseCIDR(cidr)
				if err != nil {
					return fmt.Errorf("error parsing cidr %s: %w", cidr, err)
				}
				m = cidrMatcher{
					ipNet: ipNet,
				}

			case strings.HasPrefix(input, "dns:"):
				pattern := strings.TrimPrefix(input, "dns:")
				if pattern == "" {
					return fmt.Errorf("empty dns pattern provided")
				}
				m = dnsMatcher{
					pattern: pattern,
				}

			default:
				return fmt.Errorf("preference string %q is not supported", input)
			}
			if m != nil {
				o.withMatchers = append(o.withMatchers, m)
			}
		}
		return nil
	}
}
