package api

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

type ServerInfo struct {
	Port         int      `json:"port"`
	Host         string   `json:"host"`
	LocalIPs     []string `json:"local_ips"`
	SuggestedURL string   `json:"suggested_url"`
	DashboardURL string   `json:"dashboard_url"`
	WebSocketURL string   `json:"websocket_url"`
}

func GetServerInfo(w http.ResponseWriter, r *http.Request, publicURLOverride string) {
	host := r.Host
	if idx := strings.Index(host, ":"); idx > 0 {
		host = host[:idx]
	}

	localIPs := listLocalIPv4()
	suggestedHost := host
	if isLoopbackHost(host) && len(localIPs) > 0 {
		suggestedHost = localIPs[0]
	}

	port := 8989
	if idx := strings.LastIndex(r.Host, ":"); idx > 0 {
		if p := r.Host[idx+1:]; p != "" {
			port = parsePort(p)
		}
	}

	suggestedURL := "http://" + net.JoinHostPort(suggestedHost, itoa(port))
	if strings.TrimSpace(publicURLOverride) != "" {
		suggestedURL = strings.TrimSpace(publicURLOverride)
	}
	info := ServerInfo{
		Port:         port,
		Host:         host,
		LocalIPs:     localIPs,
		SuggestedURL: suggestedURL,
		DashboardURL: suggestedURL,
		WebSocketURL: strings.Replace(strings.Replace(suggestedURL, "https://", "wss://", 1), "http://", "ws://", 1) + "/ws/agent",
	}

	writeJSON(w, info)
}

func listLocalIPv4() []string {
	var ips []string
	ifaces, err := net.Interfaces()
	if err != nil {
		return ips
	}
	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			continue
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				continue
			}
			ip = ip.To4()
			if ip == nil {
				continue
			}
			ips = append(ips, ip.String())
		}
	}
	return ips
}

func isLoopbackHost(host string) bool {
	return host == "localhost" || host == "127.0.0.1" || host == "::1"
}

func parsePort(s string) int {
	n := 0
	for _, ch := range s {
		if ch < '0' || ch > '9' {
			return 8989
		}
		n = n*10 + int(ch-'0')
	}
	if n == 0 {
		return 8989
	}
	return n
}

func itoa(n int) string {
	if n == 0 {
		return "0"
	}
	var buf [16]byte
	i := len(buf)
	for n > 0 {
		i--
		buf[i] = byte('0' + n%10)
		n /= 10
	}
	return string(buf[i:])
}
