package gateway

import (
	"crypto/subtle"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"

	httperrors "github.com/explorer/backend/libs/go-http-errors"
	httpmiddleware "github.com/explorer/backend/libs/go-http-middleware"
)

// Gateway represents the API gateway
type Gateway struct {
	apiURL      *url.URL
	rateLimiter *RateLimiter
	auth        *AuthMiddleware
}

// NewGateway creates a new API gateway
func NewGateway(apiURL string) (*Gateway, error) {
	parsedURL, err := url.Parse(apiURL)
	if err != nil {
		return nil, fmt.Errorf("invalid API URL: %w", err)
	}

	return &Gateway{
		apiURL:      parsedURL,
		rateLimiter: NewRateLimiter(),
		auth:        NewAuthMiddleware(),
	}, nil
}

// Start starts the gateway server
func (g *Gateway) Start(port int) error {
	mux := http.NewServeMux()

	// Proxy to API server
	proxy := httputil.NewSingleHostReverseProxy(g.apiURL)

	mux.HandleFunc("/", g.handleRequest(proxy))

	addr := fmt.Sprintf(":%d", port)
	log.Printf("Starting API Gateway on %s", addr)
	return http.ListenAndServe(addr, mux)
}

// handleRequest handles incoming requests with middleware
func (g *Gateway) handleRequest(proxy *httputil.ReverseProxy) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// Add security headers
		g.addSecurityHeaders(w)

		// Authentication
		if !g.auth.Authenticate(r) {
			httperrors.WriteJSON(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unauthorized")
			return
		}

		// Rate limiting
		if !g.rateLimiter.Allow(r) {
			httperrors.WriteJSON(w, http.StatusTooManyRequests, "RATE_LIMIT_EXCEEDED", "Rate limit exceeded")
			return
		}

		// Add headers
		if clientIP := httpmiddleware.ClientIP(r); clientIP != "" {
			r.Header.Set("X-Forwarded-For", clientIP)
		}
		if apiKey := g.auth.GetAPIKey(r); apiKey != "" {
			r.Header.Set("X-API-Key", apiKey)
		}

		// Add branding header
		w.Header().Set("X-Explorer-Name", "SolaceScan")
		w.Header().Set("X-Explorer-Version", "1.0.0")

		// Proxy request
		proxy.ServeHTTP(w, r)
	}
}

// addSecurityHeaders adds security headers to responses
func (g *Gateway) addSecurityHeaders(w http.ResponseWriter) {
	w.Header().Set("X-Content-Type-Options", "nosniff")
	w.Header().Set("X-Frame-Options", "DENY")
	w.Header().Set("X-XSS-Protection", "1; mode=block")
	w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
	w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
	// CSP will be set per route if needed
	w.Header().Set("Permissions-Policy", "geolocation=(), microphone=(), camera=()")
}

// RateLimiter handles rate limiting
type RateLimiter struct {
	// Simple in-memory rate limiter (should use Redis in production)
	mu     sync.Mutex
	limits map[string]*limitEntry
}

type limitEntry struct {
	count   int
	resetAt time.Time
}

const gatewayRequestsPerMinute = 120

func NewRateLimiter() *RateLimiter {
	return &RateLimiter{
		limits: make(map[string]*limitEntry),
	}
}

func (rl *RateLimiter) Allow(r *http.Request) bool {
	clientIP := httpmiddleware.ClientIP(r)
	if clientIP == "" {
		clientIP = r.RemoteAddr
	}

	now := time.Now()

	rl.mu.Lock()
	defer rl.mu.Unlock()

	entry, ok := rl.limits[clientIP]
	if !ok || now.After(entry.resetAt) {
		rl.limits[clientIP] = &limitEntry{
			count:   1,
			resetAt: now.Add(time.Minute),
		}
		return true
	}

	if entry.count >= gatewayRequestsPerMinute {
		return false
	}

	entry.count++
	return true
}

// AuthMiddleware handles authentication
type AuthMiddleware struct {
	allowAnonymous bool
	apiKeys        []string
}

func NewAuthMiddleware() *AuthMiddleware {
	return &AuthMiddleware{
		allowAnonymous: parseBoolEnv("GATEWAY_ALLOW_ANONYMOUS"),
		apiKeys:        splitNonEmptyEnv("GATEWAY_API_KEYS"),
	}
}

func (am *AuthMiddleware) Authenticate(r *http.Request) bool {
	apiKey := am.GetAPIKey(r)
	if apiKey == "" {
		return am.allowAnonymous
	}
	if len(am.apiKeys) == 0 {
		return am.allowAnonymous
	}

	for _, allowedKey := range am.apiKeys {
		if subtle.ConstantTimeCompare([]byte(apiKey), []byte(allowedKey)) == 1 {
			return true
		}
	}

	return false
}

func (am *AuthMiddleware) GetAPIKey(r *http.Request) string {
	// Check header first
	if key := r.Header.Get("X-API-Key"); key != "" {
		return key
	}
	// Check query parameter
	if key := r.URL.Query().Get("api_key"); key != "" {
		return key
	}
	return ""
}

func parseBoolEnv(key string) bool {
	value := strings.TrimSpace(os.Getenv(key))
	return strings.EqualFold(value, "1") ||
		strings.EqualFold(value, "true") ||
		strings.EqualFold(value, "yes") ||
		strings.EqualFold(value, "on")
}

func splitNonEmptyEnv(key string) []string {
	raw := strings.TrimSpace(os.Getenv(key))
	if raw == "" {
		return nil
	}

	parts := strings.Split(raw, ",")
	values := make([]string, 0, len(parts))
	for _, part := range parts {
		trimmed := strings.TrimSpace(part)
		if trimmed != "" {
			values = append(values, trimmed)
		}
	}

	return values
}
