package handler

import (
	"net"
	"net/http"
	"net/url"
	"strings"
	"xboard-go/internal/service"

	"github.com/gin-gonic/gin"
)

func requestBaseURL(c *gin.Context) string {
	if c != nil {
		if origin := requestOrigin(c.Request); origin != "" {
			return origin
		}
	}
	if appURL := service.GetAppURL(); appURL != "" {
		return strings.TrimRight(appURL, "/")
	}
	return ""
}

func requestOrigin(r *http.Request) string {
	if r == nil {
		return ""
	}

	scheme, host := forwardedOrigin(r)
	if scheme != "" && host != "" {
		return scheme + "://" + host
	}

	if host == "" {
		host = firstHeaderValue(r.Header.Get("X-Forwarded-Host"))
	}
	if host == "" {
		host = strings.TrimSpace(r.Host)
	}
	if host == "" {
		host = originHostFallback(r)
	}
	if host == "" {
		return ""
	}

	if scheme == "" {
		scheme = firstHeaderValue(r.Header.Get("X-Forwarded-Proto"))
	}
	if scheme == "" {
		scheme = firstHeaderValue(r.Header.Get("X-Forwarded-Scheme"))
	}
	if scheme == "" {
		scheme = firstHeaderValue(r.Header.Get("X-Scheme"))
	}
	if scheme == "" && strings.EqualFold(strings.TrimSpace(r.Header.Get("Front-End-Https")), "on") {
		scheme = "https"
	}
	if scheme == "" && strings.EqualFold(strings.TrimSpace(r.Header.Get("X-Forwarded-Ssl")), "on") {
		scheme = "https"
	}
	if scheme == "" {
		switch strings.TrimSpace(firstHeaderValue(r.Header.Get("X-Forwarded-Port"))) {
		case "443":
			scheme = "https"
		case "80":
			scheme = "http"
		}
	}
	if scheme == "" {
		scheme = originSchemeFallback(r)
	}
	if scheme == "" {
		if r.TLS != nil {
			scheme = "https"
		} else {
			scheme = "http"
		}
	}

	if forwardedPort := strings.TrimSpace(firstHeaderValue(r.Header.Get("X-Forwarded-Port"))); forwardedPort != "" && !strings.Contains(host, ":") {
		defaultPort := map[string]string{
			"http":  "80",
			"https": "443",
		}[scheme]
		if forwardedPort != defaultPort {
			host = net.JoinHostPort(host, forwardedPort)
		}
	}

	return scheme + "://" + host
}

func forwardedOrigin(r *http.Request) (scheme string, host string) {
	forwarded := strings.TrimSpace(firstHeaderValue(r.Header.Get("Forwarded")))
	if forwarded == "" {
		return "", ""
	}

	for _, segment := range strings.Split(forwarded, ";") {
		parts := strings.SplitN(strings.TrimSpace(segment), "=", 2)
		if len(parts) != 2 {
			continue
		}
		key := strings.ToLower(strings.TrimSpace(parts[0]))
		value := strings.Trim(strings.TrimSpace(parts[1]), "\"")
		switch key {
		case "proto":
			if value != "" {
				scheme = value
			}
		case "host":
			if value != "" {
				host = value
			}
		}
	}

	return scheme, host
}

func firstHeaderValue(value string) string {
	if value == "" {
		return ""
	}
	parts := strings.Split(value, ",")
	return strings.TrimSpace(parts[0])
}

func originHostFallback(r *http.Request) string {
	for _, raw := range []string{r.Header.Get("Origin"), r.Header.Get("Referer")} {
		if parsed, err := url.Parse(strings.TrimSpace(raw)); err == nil && parsed.Host != "" {
			return parsed.Host
		}
	}
	return ""
}

func originSchemeFallback(r *http.Request) string {
	for _, raw := range []string{r.Header.Get("Origin"), r.Header.Get("Referer")} {
		if parsed, err := url.Parse(strings.TrimSpace(raw)); err == nil && parsed.Scheme != "" {
			return parsed.Scheme
		}
	}
	return ""
}
