package middleware

import (
	"context"
	"log/slog"
	"net"
	"net/http"
	"strings"

	"yobble-gateway-go/internal/logger"
)

type contextKey string

const realIPContextKey contextKey = "realIP"

// RealIPMiddleware извлекает реальный IP-адрес клиента из заголовков и устанавливает его в контекст запроса.
func RealIPMiddleware(next http.Handler) http.Handler {
	op := "middleware.RealIPMiddleware"
	log := logger.NewLoggerWithOp(op)

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		realIP := r.Header.Get("X-Real-IP")
		if realIP == "" {
			forwardedFor := r.Header.Get("X-Forwarded-For")
			if forwardedFor != "" {
				parts := strings.Split(forwardedFor, ",")
				realIP = strings.TrimSpace(parts[0])
			}
		}

		if realIP != "" {
			_, port, err := net.SplitHostPort(r.RemoteAddr)
			if err == nil {
				r.RemoteAddr = net.JoinHostPort(realIP, port)
			} else {
				r.RemoteAddr = realIP
			}
			ctx := context.WithValue(r.Context(), realIPContextKey, realIP)
			r = r.WithContext(ctx)
			log.Debug("real IP extracted", slog.String("ip", realIP))
		}

		next.ServeHTTP(w, r)
	})
}

// GetRealIP извлекает реальный IP из контекста запроса.
func GetRealIP(r *http.Request) string {
	if ip, ok := r.Context().Value(realIPContextKey).(string); ok {
		return ip
	}
	return r.RemoteAddr
}

// RemoveTrailingSlashMiddleware перенаправляет запросы с завершающим слэшем (если это не просто '/')
// на тот же путь без завершающего слэша.
func RemoveTrailingSlashMiddleware(next http.Handler) http.Handler {
	op := "middleware.RemoveTrailingSlashMiddleware"
	log := logger.NewLoggerWithOp(op)

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		urlPath := r.URL.Path

		if urlPath != "" && urlPath != "/" && strings.HasSuffix(urlPath, "/") {
			newPath := strings.TrimSuffix(urlPath, "/")
			newURL := *r.URL
			newURL.Path = newPath
			log.Debug("redirecting trailing slash", slog.String("old_path", urlPath), slog.String("new_path", newPath))
			http.Redirect(w, r, newURL.String(), http.StatusMovedPermanently)
			return
		}
		next.ServeHTTP(w, r)
	})
}
