package requestip

import (
	"net"
	"net/http"
	"strings"
)

// FromRequest returns the best-effort client IP/host for a request, preferring
// proxy headers before falling back to RemoteAddr.
//
// Header precedence:
// 1) X-Real-IP
// 2) X-Forwarded-For (first value)
// 3) Forwarded (for=...)
// 4) RemoteAddr (host part)
func FromRequest(r *http.Request) string {
	if r == nil {
		return ""
	}

	if v := firstAddressToken(r.Header.Get("X-Real-IP")); v != "" {
		return stripPort(v)
	}
	if v := firstAddressToken(r.Header.Get("X-Forwarded-For")); v != "" {
		return stripPort(v)
	}
	if v := forwardedForValue(r.Header.Get("Forwarded")); v != "" {
		return stripPort(v)
	}
	return stripPort(strings.TrimSpace(r.RemoteAddr))
}

func firstAddressToken(v string) string {
	if v == "" {
		return ""
	}
	part := strings.TrimSpace(strings.Split(v, ",")[0])
	part = strings.Trim(part, `"`)
	return strings.TrimSpace(part)
}

func forwardedForValue(v string) string {
	for _, part := range strings.Split(v, ",") {
		for _, kv := range strings.Split(part, ";") {
			k, raw, ok := strings.Cut(strings.TrimSpace(kv), "=")
			if !ok || !strings.EqualFold(strings.TrimSpace(k), "for") {
				continue
			}
			candidate := strings.Trim(strings.TrimSpace(raw), `"`)
			if candidate == "" {
				continue
			}
			return candidate
		}
	}
	return ""
}

func stripPort(addr string) string {
	addr = strings.TrimSpace(addr)
	if addr == "" {
		return ""
	}
	// RFC 7239 quoted values may wrap IPv6 with brackets.
	addr = strings.Trim(addr, "[]")
	host, _, err := net.SplitHostPort(addr)
	if err == nil {
		return host
	}
	return addr
}
