package http

import (
	"context"
	"encoding/json"
	"log"
	"net/http"
	"strings"
	"time"
)

// CORSMiddleware handles Cross-Origin Resource Sharing
func CORSMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Set CORS headers
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		w.Header().Set("Access-Control-Max-Age", "86400")

		// Handle preflight OPTIONS requests
		if r.Method == "OPTIONS" {
			w.WriteHeader(http.StatusOK)
			return
		}

		next.ServeHTTP(w, r)
	})
}

// LogsMiddleware logs HTTP requests with timing information
func LogsMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		next.ServeHTTP(w, r)
		log.Printf("%s %s %s", r.Method, r.RequestURI, time.Since(start))
	})
}

// AuthMiddleware handles JWT token validation, API keys, and basic auth
func AuthMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Skip auth for swagger endpoints and health checks
		if strings.HasPrefix(r.URL.Path, "/swagger/") || r.URL.Path == "/health" {
			next.ServeHTTP(w, r)
			return
		}

		// Try Bearer token authentication first
		authHeader := r.Header.Get("Authorization")
		if authHeader != "" {
			if strings.HasPrefix(authHeader, "Bearer ") {
				token := strings.TrimPrefix(authHeader, "Bearer ")
				if token != "" {
					userID, err := validateJWTToken(token)
					if err == nil {
						ctx := context.WithValue(r.Context(), "user_id", userID)
						next.ServeHTTP(w, r.WithContext(ctx))
						return
					}
				}
			}
		}

		// Try API Key authentication
		apiKey := r.Header.Get("X-API-Key")
		if apiKey != "" {
			if isValidAPIKey(apiKey) {
				ctx := context.WithValue(r.Context(), "user_id", getUserIDFromAPIKey(apiKey))
				next.ServeHTTP(w, r.WithContext(ctx))
				return
			}
		}

		// Try Basic authentication
		username, password, ok := r.BasicAuth()
		if ok {
			if isValidCredentials(username, password) {
				ctx := context.WithValue(r.Context(), "username", username)
				next.ServeHTTP(w, r.WithContext(ctx))
				return
			}
		}

		// Fallback to cookie-based auth (existing behavior)
		token, err := r.Cookie("token")
		if err == nil && token.Value == "123" {
			next.ServeHTTP(w, r)
			return
		}

		// No valid authentication found
		response := map[string]interface{}{
			"error":   "Unauthorized",
			"message": "Valid authentication required. Use Bearer token, API key, or basic auth.",
			"code":    http.StatusUnauthorized,
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusUnauthorized)
		json.NewEncoder(w).Encode(response)
	})
}

// Helper functions for authentication validation
func validateJWTToken(token string) (string, error) {
	// TODO: Implement JWT validation logic with your preferred JWT library
	// For demonstration, accept a demo token
	if token == "valid-jwt-token" {
		return "user-123", nil
	}
	return "", http.ErrAbortHandler
}

func isValidAPIKey(apiKey string) bool {
	// TODO: Implement API key validation logic
	// For demonstration, accept a demo API key
	return apiKey == "demo-api-key-123"
}

func getUserIDFromAPIKey(apiKey string) string {
	// TODO: Implement logic to get user ID from API key
	// For demonstration, return a mock user ID
	return "api-user-123"
}

func isValidCredentials(username, password string) bool {
	// TODO: Implement credential validation logic
	// For demonstration, accept demo credentials
	return username == "admin" && password == "password"
}

// GetUserIDFromContext extracts user ID from request context
func GetUserIDFromContext(ctx context.Context) (string, bool) {
	userID, ok := ctx.Value("user_id").(string)
	return userID, ok
}

// GetUsernameFromContext extracts username from request context
func GetUsernameFromContext(ctx context.Context) (string, bool) {
	username, ok := ctx.Value("username").(string)
	return username, ok
}
