package uri

import (
	"net/url"
	"strings"

	"github.com/pkg/errors"
)

// URI implements a parser for a URI format based on the the PKCS #11 URI Scheme
// defined in https://tools.ietf.org/html/rfc7512
//
// These URIs will be used to define the key names in a KMS.
type URI struct {
	*url.URL
	Values url.Values
}

// New creates a new URI from a scheme and key-value pairs.
func New(scheme string, values url.Values) *URI {
	return &URI{
		URL: &url.URL{
			Scheme: scheme,
			Opaque: strings.ReplaceAll(values.Encode(), "&", ";"),
		},
		Values: values,
	}
}

// NewFile creates an uri for a file.
func NewFile(path string) *URI {
	return &URI{
		URL: &url.URL{
			Scheme: "file",
			Path:   path,
		},
	}
}

// HasScheme returns true if the given uri has the given scheme, false otherwise.
func HasScheme(scheme, rawuri string) bool {
	u, err := url.Parse(rawuri)
	if err != nil {
		return false
	}
	return strings.EqualFold(u.Scheme, scheme)
}

// Parse returns the URI for the given string or an error.
func Parse(rawuri string) (*URI, error) {
	u, err := url.Parse(rawuri)
	if err != nil {
		return nil, errors.Wrapf(err, "error parsing %s", rawuri)
	}
	if u.Scheme == "" {
		return nil, errors.Errorf("error parsing %s: scheme is missing", rawuri)
	}
	v, err := url.ParseQuery(u.Opaque)
	if err != nil {
		return nil, errors.Wrapf(err, "error parsing %s", rawuri)
	}

	return &URI{
		URL:    u,
		Values: v,
	}, nil
}

// ParseWithScheme returns the URI for the given string only if it has the given
// scheme.
func ParseWithScheme(scheme, rawuri string) (*URI, error) {
	u, err := Parse(rawuri)
	if err != nil {
		return nil, err
	}
	if !strings.EqualFold(u.Scheme, scheme) {
		return nil, errors.Errorf("error parsing %s: scheme not expected", rawuri)
	}
	return u, nil
}

// Get returns the first value in the uri with the give n key, it will return
// empty string if that field is not present.
func (u *URI) Get(key string) string {
	return u.Values.Get(key)
}