package middleware

import (
	"net"
	"strings"
	"sync"

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

var (
	trustedMu      sync.RWMutex
	trustedProxies []*net.IPNet
	trustedIPs     []net.IP
)

// ConfigureTrustedProxies sets CIDRs/IPs allowed to set X-Forwarded-For / X-Real-IP.
// When empty, only the direct remote address is used.
func ConfigureTrustedProxies(cidrs []string) {
	trustedMu.Lock()
	defer trustedMu.Unlock()
	trustedProxies = nil
	trustedIPs = nil
	for _, cidr := range cidrs {
		cidr = strings.TrimSpace(cidr)
		if cidr == "" {
			continue
		}
		if strings.Contains(cidr, "/") {
			_, network, err := net.ParseCIDR(cidr)
			if err == nil {
				trustedProxies = append(trustedProxies, network)
			}
			continue
		}
		if ip := net.ParseIP(cidr); ip != nil {
			trustedIPs = append(trustedIPs, ip)
		}
	}
}

func isTrustedProxy(ip net.IP) bool {
	trustedMu.RLock()
	defer trustedMu.RUnlock()
	if len(trustedProxies) == 0 && len(trustedIPs) == 0 {
		return false
	}
	for _, tip := range trustedIPs {
		if tip.Equal(ip) {
			return true
		}
	}
	for _, network := range trustedProxies {
		if network.Contains(ip) {
			return true
		}
	}
	return false
}

func directRemoteIP(ctx *gin.Context) string {
	host, _, err := net.SplitHostPort(ctx.Request.RemoteAddr)
	if err != nil {
		return strings.TrimSpace(ctx.Request.RemoteAddr)
	}
	return host
}

// ClientIP returns the client IP. Forwarded headers are honored only when the
// direct remote address is a configured trusted proxy.
func ClientIP(ctx *gin.Context) string {
	remote := directRemoteIP(ctx)
	remoteIP := net.ParseIP(remote)
	if remoteIP == nil || !isTrustedProxy(remoteIP) {
		if remoteIP != nil {
			return remoteIP.String()
		}
		return ctx.ClientIP()
	}
	if xff := ctx.GetHeader("X-Forwarded-For"); xff != "" {
		parts := strings.Split(xff, ",")
		return strings.TrimSpace(parts[0])
	}
	if xri := ctx.GetHeader("X-Real-IP"); xri != "" {
		return strings.TrimSpace(xri)
	}
	return remoteIP.String()
}
