package api

import (
	"context"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"

	limiter "github.com/ulule/limiter/v3"
	memory "github.com/ulule/limiter/v3/drivers/store/memory"
	stdmiddleware "github.com/ulule/limiter/v3/drivers/middleware/stdlib"
)

// SybilProtector mantém IPs bloqueados temporariamente
var sybilBlocklist = struct {
	mu    sync.RWMutex
	block map[string]time.Time
}{block: make(map[string]time.Time)}

// isBlocked verifica se um IP está na lista de bloqueio
func isBlocked(ip string) bool {
	sybilBlocklist.mu.RLock()
	defer sybilBlocklist.mu.RUnlock()
	expire, exists := sybilBlocklist.block[ip]
	return exists && time.Now().Before(expire)
}

// blockIP adiciona um IP à blocklist por X segundos
func blockIP(ip string, duration time.Duration) {
	sybilBlocklist.mu.Lock()
	defer sybilBlocklist.mu.Unlock()
	sybilBlocklist.block[ip] = time.Now().Add(duration)
}

// extractIP extrai IP puro do RemoteAddr
func extractIP(addr string) string {
	ip, _, err := net.SplitHostPort(addr)
	if err != nil {
		return addr
	}
	return ip
}

// NewRateLimiterMiddleware cria middleware de rate limiting e proteção anti-Sybil
func NewRateLimiterMiddleware() func(http.Handler) http.Handler {
	rate := limiter.Rate{
		Period: 1 * time.Second,
		Limit:  5,
	}
	store := memory.NewStore()
	limiterInstance := limiter.New(store, rate)
	middleware := stdmiddleware.NewMiddleware(limiterInstance)

	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// Ignorar WebSocket
			if strings.HasPrefix(r.URL.Path, "/ws") {
				next.ServeHTTP(w, r)
				return
			}

			ip := extractIP(r.RemoteAddr)
			if isBlocked(ip) {
				w.WriteHeader(http.StatusTooManyRequests)
				WriteError(w, http.StatusTooManyRequests, "IP bloqueado temporariamente por abuso")
				return
			}

			ctx := context.WithValue(r.Context(), "real-ip", ip)
			rec := &responseRecorder{ResponseWriter: w, status: 200}
			middleware.Handler(next).ServeHTTP(rec, r.WithContext(ctx))

			if rec.status == http.StatusTooManyRequests {
				blockIP(ip, 30*time.Second)
			}
		})
	}
}

// responseRecorder intercepta status code para detectar excesso de requisições
type responseRecorder struct {
	http.ResponseWriter
	status int
}

func (rr *responseRecorder) WriteHeader(code int) {
	rr.status = code
	rr.ResponseWriter.WriteHeader(code)
}