package hysteria2

import (
	"fmt"
	"net/url"
	"strconv"
	"strings"
)

// Endpoint is a parsed Hysteria 2 share link plus optional overrides.
type Endpoint struct {
	Server      string // host:port or multi-port host
	Auth        string
	SNI         string
	Insecure    bool
	PinSHA256   string
	Obfs        string // salamander | gecko
	ObfsPassword string
	Remark      string
	RawURI      string
}

// ParseEndpoint parses hysteria2:// / hy2:// into discrete fields.
func ParseEndpoint(raw string) (Endpoint, error) {
	normalized, remark, err := NormalizeShareLink(raw)
	if err != nil {
		return Endpoint{}, err
	}
	u, err := url.Parse(normalized)
	if err != nil {
		return Endpoint{}, err
	}
	ep := Endpoint{Remark: remark, RawURI: normalized}
	if u.User != nil {
		user := u.User.Username()
		if pass, ok := u.User.Password(); ok {
			ep.Auth = user + ":" + pass
		} else {
			ep.Auth, _ = url.QueryUnescape(user)
		}
	}
	ep.Server = u.Host
	if ep.Server == "" {
		return Endpoint{}, fmt.Errorf("hysteria2 URI missing host")
	}
	q := u.Query()
	ep.SNI = first(q, "sni", "peer")
	ep.PinSHA256 = first(q, "pinSHA256", "pinsha256")
	ep.Obfs = first(q, "obfs")
	ep.ObfsPassword = first(q, "obfs-password", "obfs_password")
	if v := first(q, "insecure"); v == "1" || strings.EqualFold(v, "true") {
		ep.Insecure = true
	}
	return ep, nil
}

func first(q url.Values, keys ...string) string {
	for _, k := range keys {
		if v := strings.TrimSpace(q.Get(k)); v != "" {
			return v
		}
	}
	return ""
}

// ApplyOverrides merges non-empty profile overrides into the endpoint.
func (ep *Endpoint) ApplyOverrides(sni, obfs, obfsPass, pin string, insecure *bool) {
	if sni != "" {
		ep.SNI = sni
	}
	if obfs != "" {
		ep.Obfs = obfs
	}
	if obfsPass != "" {
		ep.ObfsPassword = obfsPass
	}
	if pin != "" {
		ep.PinSHA256 = pin
	}
	if insecure != nil {
		ep.Insecure = *insecure
	}
}

// BoolPtr helper for optional bool overrides.
func BoolPtr(v bool) *bool { return &v }

// HasTLSExtras reports whether discrete TLS/obfs fields should be written.
func (ep Endpoint) HasTLSExtras() bool {
	return ep.SNI != "" || ep.Insecure || ep.PinSHA256 != "" || ep.Obfs != ""
}

// PortHint returns a single port for ping.
func (ep Endpoint) PortHint() string {
	_, port, err := HostPort(ep.RawURI)
	if err != nil || port == "" {
		return "443"
	}
	return port
}

// ParseBandwidthMbps tries to normalize user input like "100" or "100 mbps".
func ParseBandwidthMbps(raw string) string {
	raw = strings.TrimSpace(strings.ToLower(raw))
	if raw == "" || raw == "0" || raw == "auto" || raw == "bbr" {
		return ""
	}
	if strings.ContainsAny(raw, "bkmgt") {
		return raw
	}
	if _, err := strconv.Atoi(raw); err == nil {
		return raw + " mbps"
	}
	return raw
}
