package webconnectivity

//
// Input parsing
//

import (
	"errors"
	"net"
	"net/url"

	"github.com/ooni/probe-cli/v3/internal/runtimex"
)

// InputParser helps to print the experiment's input.
type InputParser struct {
	// List of accepted URL schemes.
	AcceptedSchemes []string

	// Whether to allow endpoints in input.
	AllowEndpoints bool

	// The default scheme to use if AllowEndpoints == true.
	DefaultScheme string
}

// Parse parses the experiment input and returns the resulting URL.
func (ip *InputParser) Parse(input string) (*url.URL, error) {
	// put this check at top-level such that we always see the crash if needed
	runtimex.PanicIfTrue(
		ip.AllowEndpoints && ip.DefaultScheme == "",
		"invalid configuration for InputParser.AllowEndpoints == true",
	)
	URL, err := url.Parse(input)
	if err != nil {
		return ip.maybeAllowEndpoints(URL, err)
	}
	for _, scheme := range ip.AcceptedSchemes {
		if URL.Scheme == scheme {
			// TODO: here you may want to perform additional parsing
			return URL, nil
		}
	}
	return nil, errors.New("cannot parse input")
}

// Conditionally allows endpoints when ip.AllowEndpoints is true.
func (ip *InputParser) maybeAllowEndpoints(URL *url.URL, err error) (*url.URL, error) {
	runtimex.PanicIfNil(err, "expected to be called with a non-nil error")
	if ip.AllowEndpoints && URL.Scheme != "" && URL.Opaque != "" && URL.User == nil &&
		URL.Host == "" && URL.Path == "" && URL.RawPath == "" &&
		URL.RawQuery == "" && URL.Fragment == "" && URL.RawFragment == "" {
		// See https://go.dev/play/p/Rk5pS_zGY5U
		//
		// Note that we know that `ip.DefaultScheme != ""` from the above runtime check.
		out := &url.URL{
			Scheme: ip.DefaultScheme,
			Host:   net.JoinHostPort(URL.Scheme, URL.Opaque),
		}
		return out, nil
	}
	return nil, err
}
