// Package middleware provides HTTP middleware for the API
package middleware

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

	"omixlab.com/mosis-portal/internal/auth"
	"omixlab.com/mosis-portal/internal/database"
)

type contextKey string

const (
	DeveloperContextKey contextKey = "developer"
	ClaimsContextKey    contextKey = "claims"
)

// AuthMiddleware handles JWT and API key authentication
type AuthMiddleware struct {
	jwtManager *auth.JWTManager
	db         *database.DB
}

// NewAuthMiddleware creates a new auth middleware
func NewAuthMiddleware(jwtManager *auth.JWTManager, db *database.DB) *AuthMiddleware {
	return &AuthMiddleware{
		jwtManager: jwtManager,
		db:         db,
	}
}

// RequireAuth requires a valid JWT or API key
func (m *AuthMiddleware) RequireAuth(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Try Bearer token first
		authHeader := r.Header.Get("Authorization")
		if strings.HasPrefix(authHeader, "Bearer ") {
			token := strings.TrimPrefix(authHeader, "Bearer ")
			claims, err := m.jwtManager.ValidateAccessToken(token)
			if err != nil {
				http.Error(w, "Invalid or expired token", http.StatusUnauthorized)
				return
			}

			// Add claims to context
			ctx := context.WithValue(r.Context(), ClaimsContextKey, claims)
			next.ServeHTTP(w, r.WithContext(ctx))
			return
		}

		// Try API key
		apiKey := r.Header.Get("X-API-Key")
		if apiKey != "" {
			developer, err := m.db.ValidateAPIKey(r.Context(), apiKey)
			if err != nil {
				http.Error(w, "Invalid API key", http.StatusUnauthorized)
				return
			}

			// Add developer to context
			ctx := context.WithValue(r.Context(), DeveloperContextKey, developer)
			next.ServeHTTP(w, r.WithContext(ctx))
			return
		}

		http.Error(w, "Authorization required", http.StatusUnauthorized)
	})
}

// OptionalAuth adds developer info to context if authenticated, but doesn't require it
func (m *AuthMiddleware) OptionalAuth(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Try Bearer token
		authHeader := r.Header.Get("Authorization")
		if strings.HasPrefix(authHeader, "Bearer ") {
			token := strings.TrimPrefix(authHeader, "Bearer ")
			claims, err := m.jwtManager.ValidateAccessToken(token)
			if err == nil {
				ctx := context.WithValue(r.Context(), ClaimsContextKey, claims)
				next.ServeHTTP(w, r.WithContext(ctx))
				return
			}
		}

		// Try API key
		apiKey := r.Header.Get("X-API-Key")
		if apiKey != "" {
			developer, err := m.db.ValidateAPIKey(r.Context(), apiKey)
			if err == nil {
				ctx := context.WithValue(r.Context(), DeveloperContextKey, developer)
				next.ServeHTTP(w, r.WithContext(ctx))
				return
			}
		}

		// Continue without authentication
		next.ServeHTTP(w, r)
	})
}

// GetClaims retrieves JWT claims from context
func GetClaims(ctx context.Context) *auth.Claims {
	claims, ok := ctx.Value(ClaimsContextKey).(*auth.Claims)
	if !ok {
		return nil
	}
	return claims
}

// GetDeveloperID retrieves the developer ID from context (from JWT or API key)
func GetDeveloperID(ctx context.Context) string {
	// First check JWT claims
	claims := GetClaims(ctx)
	if claims != nil {
		return claims.Subject
	}

	// Then check developer from API key
	developer, ok := ctx.Value(DeveloperContextKey).(*database.Developer)
	if ok && developer != nil {
		return developer.ID
	}

	return ""
}
