package rest

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/explorer/backend/auth"
	"github.com/golang-jwt/jwt/v4"
)

// handleAuthNonce handles POST /api/v1/auth/nonce
func (s *Server) handleAuthNonce(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}
	if !s.requireDB(w) {
		return
	}

	var req auth.NonceRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "bad_request", "Invalid request body")
		return
	}

	// Generate nonce
	nonceResp, err := s.walletAuth.GenerateNonce(r.Context(), req.Address)
	if err != nil {
		if errors.Is(err, auth.ErrWalletAuthStorageNotInitialized) {
			writeError(w, http.StatusServiceUnavailable, "service_unavailable", err.Error())
			return
		}
		writeError(w, http.StatusBadRequest, "bad_request", err.Error())
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(nonceResp)
}

// handleAuthWallet handles POST /api/v1/auth/wallet
func (s *Server) handleAuthWallet(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}
	if !s.requireDB(w) {
		return
	}

	var req auth.WalletAuthRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "bad_request", "Invalid request body")
		return
	}

	// Authenticate wallet
	authResp, err := s.walletAuth.AuthenticateWallet(r.Context(), &req)
	if err != nil {
		if errors.Is(err, auth.ErrWalletAuthStorageNotInitialized) {
			writeError(w, http.StatusServiceUnavailable, "service_unavailable", err.Error())
			return
		}
		writeError(w, http.StatusUnauthorized, "unauthorized", err.Error())
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(authResp)
}

type userAuthRequest struct {
	Email    string `json:"email"`
	Username string `json:"username"`
	Password string `json:"password"`
}

type accessProduct struct {
	Slug               string   `json:"slug"`
	Name               string   `json:"name"`
	Provider           string   `json:"provider"`
	VMID               int      `json:"vmid"`
	HTTPURL            string   `json:"http_url"`
	WSURL              string   `json:"ws_url,omitempty"`
	DefaultTier        string   `json:"default_tier"`
	RequiresApproval   bool     `json:"requires_approval"`
	BillingModel       string   `json:"billing_model"`
	Description        string   `json:"description"`
	UseCases           []string `json:"use_cases"`
	ManagementFeatures []string `json:"management_features"`
}

type userSessionClaims struct {
	UserID   string `json:"user_id"`
	Email    string `json:"email"`
	Username string `json:"username"`
	jwt.RegisteredClaims
}

type createAPIKeyRequest struct {
	Name         string   `json:"name"`
	Tier         string   `json:"tier"`
	ProductSlug  string   `json:"product_slug"`
	ExpiresDays  int      `json:"expires_days"`
	MonthlyQuota int      `json:"monthly_quota"`
	Scopes       []string `json:"scopes"`
}

type createSubscriptionRequest struct {
	ProductSlug string `json:"product_slug"`
	Tier        string `json:"tier"`
}

type accessUsageSummary struct {
	ProductSlug  string `json:"product_slug"`
	ActiveKeys   int    `json:"active_keys"`
	RequestsUsed int    `json:"requests_used"`
	MonthlyQuota int    `json:"monthly_quota"`
}

type accessAuditEntry = auth.APIKeyUsageLog

type adminSubscriptionActionRequest struct {
	SubscriptionID string `json:"subscription_id"`
	Status         string `json:"status"`
	Notes          string `json:"notes"`
}

type internalValidateAPIKeyRequest struct {
	APIKey       string `json:"api_key"`
	MethodName   string `json:"method_name"`
	RequestCount int    `json:"request_count"`
	LastIP       string `json:"last_ip"`
}

var rpcAccessProducts = []accessProduct{
	{
		Slug:               "core-rpc",
		Name:               "Core RPC",
		Provider:           "besu-core",
		VMID:               2101,
		HTTPURL:            "https://rpc-http-prv.d-bis.org",
		WSURL:              "wss://rpc-ws-prv.d-bis.org",
		DefaultTier:        "enterprise",
		RequiresApproval:   true,
		BillingModel:       "contract",
		Description:        "Private Chain 138 Core RPC for operator-grade administration and sensitive workloads.",
		UseCases:           []string{"core deployments", "operator automation", "private infrastructure integration"},
		ManagementFeatures: []string{"dedicated API key", "higher rate ceiling", "operator-oriented access controls"},
	},
	{
		Slug:               "alltra-rpc",
		Name:               "Alltra RPC",
		Provider:           "alltra",
		VMID:               2102,
		HTTPURL:            "http://192.168.11.212:8545",
		WSURL:              "ws://192.168.11.212:8546",
		DefaultTier:        "pro",
		RequiresApproval:   false,
		BillingModel:       "subscription",
		Description:        "Dedicated Alltra-managed RPC lane for partner traffic, subscription access, and API-key-gated usage.",
		UseCases:           []string{"tenant RPC access", "managed partner workloads", "metered commercial usage"},
		ManagementFeatures: []string{"subscription-ready key issuance", "rate governance", "partner-specific traffic lane"},
	},
	{
		Slug:               "thirdweb-rpc",
		Name:               "Thirdweb RPC",
		Provider:           "thirdweb",
		VMID:               2103,
		HTTPURL:            "http://192.168.11.217:8545",
		WSURL:              "ws://192.168.11.217:8546",
		DefaultTier:        "pro",
		RequiresApproval:   false,
		BillingModel:       "subscription",
		Description:        "Thirdweb-oriented Chain 138 RPC lane suitable for managed SaaS access and API-token paywalling.",
		UseCases:           []string{"thirdweb integrations", "commercial API access", "managed dApp traffic"},
		ManagementFeatures: []string{"API token issuance", "usage tiering", "future paywall/subscription hooks"},
	},
}

func (s *Server) generateUserJWT(user *auth.User) (string, time.Time, error) {
	expiresAt := time.Now().Add(7 * 24 * time.Hour)
	claims := userSessionClaims{
		UserID:   user.ID,
		Email:    user.Email,
		Username: user.Username,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(expiresAt),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Subject:   user.ID,
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString(s.jwtSecret)
	if err != nil {
		return "", time.Time{}, err
	}
	return tokenString, expiresAt, nil
}

func (s *Server) validateUserJWT(tokenString string) (*userSessionClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &userSessionClaims{}, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method")
		}
		return s.jwtSecret, nil
	})
	if err != nil {
		return nil, err
	}
	claims, ok := token.Claims.(*userSessionClaims)
	if !ok || !token.Valid {
		return nil, fmt.Errorf("invalid token")
	}
	return claims, nil
}

func extractBearerToken(r *http.Request) string {
	authHeader := r.Header.Get("Authorization")
	if authHeader == "" {
		return ""
	}
	parts := strings.SplitN(authHeader, " ", 2)
	if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") {
		return ""
	}
	return strings.TrimSpace(parts[1])
}

func (s *Server) requireUserSession(w http.ResponseWriter, r *http.Request) (*userSessionClaims, bool) {
	token := extractBearerToken(r)
	if token == "" {
		writeError(w, http.StatusUnauthorized, "unauthorized", "User session required")
		return nil, false
	}
	claims, err := s.validateUserJWT(token)
	if err != nil {
		writeError(w, http.StatusUnauthorized, "unauthorized", "Invalid or expired session token")
		return nil, false
	}
	return claims, true
}

func isEmailInCSVAllowlist(email string, raw string) bool {
	if strings.TrimSpace(email) == "" || strings.TrimSpace(raw) == "" {
		return false
	}
	for _, candidate := range strings.Split(raw, ",") {
		if strings.EqualFold(strings.TrimSpace(candidate), strings.TrimSpace(email)) {
			return true
		}
	}
	return false
}

func (s *Server) isAccessAdmin(claims *userSessionClaims) bool {
	return claims != nil && isEmailInCSVAllowlist(claims.Email, os.Getenv("ACCESS_ADMIN_EMAILS"))
}

func (s *Server) requireInternalAccessSecret(w http.ResponseWriter, r *http.Request) bool {
	configured := strings.TrimSpace(os.Getenv("ACCESS_INTERNAL_SECRET"))
	if configured == "" {
		writeError(w, http.StatusServiceUnavailable, "service_unavailable", "Internal access secret is not configured")
		return false
	}
	presented := strings.TrimSpace(r.Header.Get("X-Access-Internal-Secret"))
	if presented == "" || presented != configured {
		writeError(w, http.StatusUnauthorized, "unauthorized", "Internal access secret required")
		return false
	}
	return true
}

func (s *Server) handleAuthRegister(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}
	if !s.requireDB(w) {
		return
	}

	var req userAuthRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "bad_request", "Invalid request body")
		return
	}
	if strings.TrimSpace(req.Email) == "" || strings.TrimSpace(req.Username) == "" || len(req.Password) < 8 {
		writeError(w, http.StatusBadRequest, "bad_request", "Email, username, and an 8+ character password are required")
		return
	}

	user, err := s.userAuth.RegisterUser(r.Context(), strings.TrimSpace(req.Email), strings.TrimSpace(req.Username), req.Password)
	if err != nil {
		writeError(w, http.StatusBadRequest, "bad_request", err.Error())
		return
	}
	token, expiresAt, err := s.generateUserJWT(user)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "internal_error", "Failed to create session")
		return
	}

	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"user": map[string]any{
			"id":       user.ID,
			"email":    user.Email,
			"username": user.Username,
		},
		"token":      token,
		"expires_at": expiresAt,
	})
}

func (s *Server) handleAuthLogin(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}
	if !s.requireDB(w) {
		return
	}

	var req userAuthRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "bad_request", "Invalid request body")
		return
	}
	user, err := s.userAuth.AuthenticateUser(r.Context(), strings.TrimSpace(req.Email), req.Password)
	if err != nil {
		writeError(w, http.StatusUnauthorized, "unauthorized", err.Error())
		return
	}
	token, expiresAt, err := s.generateUserJWT(user)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "internal_error", "Failed to create session")
		return
	}

	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"user": map[string]any{
			"id":       user.ID,
			"email":    user.Email,
			"username": user.Username,
		},
		"token":      token,
		"expires_at": expiresAt,
	})
}

func (s *Server) handleAccessProducts(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}
	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"products": rpcAccessProducts,
		"note":     "Products are ready for auth, API key, and subscription gating. Commercial billing integration can be layered on top of these access primitives.",
	})
}

func (s *Server) handleAccessMe(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}
	claims, ok := s.requireUserSession(w, r)
	if !ok {
		return
	}
	subscriptions, _ := s.userAuth.ListSubscriptions(r.Context(), claims.UserID)
	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"user": map[string]any{
			"id":       claims.UserID,
			"email":    claims.Email,
			"username": claims.Username,
			"is_admin": s.isAccessAdmin(claims),
		},
		"subscriptions": subscriptions,
	})
}

func (s *Server) handleAccessAPIKeys(w http.ResponseWriter, r *http.Request) {
	if !s.requireDB(w) {
		return
	}
	claims, ok := s.requireUserSession(w, r)
	if !ok {
		return
	}

	switch r.Method {
	case http.MethodGet:
		keys, err := s.userAuth.ListAPIKeys(r.Context(), claims.UserID)
		if err != nil {
			writeError(w, http.StatusInternalServerError, "internal_error", err.Error())
			return
		}
		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(map[string]any{"api_keys": keys})
	case http.MethodPost:
		var req createAPIKeyRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			writeError(w, http.StatusBadRequest, "bad_request", "Invalid request body")
			return
		}
		if strings.TrimSpace(req.Name) == "" {
			writeError(w, http.StatusBadRequest, "bad_request", "Key name is required")
			return
		}
		tier := strings.ToLower(strings.TrimSpace(req.Tier))
		if tier == "" {
			tier = "free"
		}
		productSlug := strings.TrimSpace(req.ProductSlug)
		product := findAccessProduct(productSlug)
		if productSlug != "" && product == nil {
			writeError(w, http.StatusBadRequest, "bad_request", "Unknown product")
			return
		}
		subscriptions, err := s.userAuth.ListSubscriptions(r.Context(), claims.UserID)
		if err != nil {
			writeError(w, http.StatusInternalServerError, "internal_error", err.Error())
			return
		}
		var subscriptionStatus string
		for _, subscription := range subscriptions {
			if subscription.ProductSlug == productSlug {
				subscriptionStatus = subscription.Status
				break
			}
		}
		if product != nil {
			if subscriptionStatus == "" {
				status := "active"
				if product.RequiresApproval {
					status = "pending"
				}
				_, err := s.userAuth.UpsertProductSubscription(
					r.Context(),
					claims.UserID,
					productSlug,
					tier,
					status,
					defaultQuotaForTier(tier),
					product.RequiresApproval,
					"",
					"",
				)
				if err != nil {
					writeError(w, http.StatusInternalServerError, "internal_error", err.Error())
					return
				}
				subscriptionStatus = status
			}
			if subscriptionStatus != "active" {
				writeError(w, http.StatusForbidden, "subscription_required", "Product access is pending approval or inactive")
				return
			}
		}
		fullName := req.Name
		if productSlug != "" {
			fullName = fmt.Sprintf("%s [%s]", req.Name, productSlug)
		}
		monthlyQuota := req.MonthlyQuota
		if monthlyQuota <= 0 {
			monthlyQuota = defaultQuotaForTier(tier)
		}
		scopes := req.Scopes
		if len(scopes) == 0 {
			scopes = defaultScopesForProduct(productSlug)
		}
		apiKey, err := s.userAuth.GenerateScopedAPIKey(
			r.Context(),
			claims.UserID,
			fullName,
			tier,
			productSlug,
			scopes,
			monthlyQuota,
			product == nil || !product.RequiresApproval,
			req.ExpiresDays,
		)
		if err != nil {
			writeError(w, http.StatusBadRequest, "bad_request", err.Error())
			return
		}
		keys, _ := s.userAuth.ListAPIKeys(r.Context(), claims.UserID)
		var latest any
		if len(keys) > 0 {
			latest = keys[0]
		}
		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(map[string]any{
			"api_key": apiKey,
			"record":  latest,
		})
	default:
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
	}
}

func (s *Server) handleAccessInternalValidateAPIKey(w http.ResponseWriter, r *http.Request) {
	if !s.requireDB(w) {
		return
	}
	if r.Method != http.MethodPost && r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}
	if !s.requireInternalAccessSecret(w, r) {
		return
	}

	req, err := parseInternalValidateAPIKeyRequest(r)
	if err != nil {
		writeError(w, http.StatusBadRequest, "bad_request", err.Error())
		return
	}
	if strings.TrimSpace(req.APIKey) == "" {
		writeError(w, http.StatusBadRequest, "bad_request", "API key is required")
		return
	}

	info, err := s.userAuth.ValidateAPIKeyDetailed(
		r.Context(),
		strings.TrimSpace(req.APIKey),
		strings.TrimSpace(req.MethodName),
		req.RequestCount,
		strings.TrimSpace(req.LastIP),
	)
	if err != nil {
		writeError(w, http.StatusUnauthorized, "unauthorized", err.Error())
		return
	}

	w.Header().Set("X-Validated-Product", info.ProductSlug)
	w.Header().Set("X-Validated-Tier", info.Tier)
	w.Header().Set("X-Validated-User", info.UserID)
	w.Header().Set("X-Validated-Scopes", strings.Join(info.Scopes, ","))
	if info.MonthlyQuota > 0 {
		remaining := info.MonthlyQuota - info.RequestsUsed
		if remaining < 0 {
			remaining = 0
		}
		w.Header().Set("X-Quota-Remaining", strconv.Itoa(remaining))
	}

	if r.Method == http.MethodGet {
		w.WriteHeader(http.StatusOK)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"valid": true,
		"key":   info,
	})
}

func parseInternalValidateAPIKeyRequest(r *http.Request) (internalValidateAPIKeyRequest, error) {
	var req internalValidateAPIKeyRequest

	if r.Method == http.MethodGet {
		req.APIKey = firstNonEmpty(
			r.Header.Get("X-API-Key"),
			extractBearerToken(r),
			r.URL.Query().Get("api_key"),
		)
		req.MethodName = firstNonEmpty(
			r.Header.Get("X-Access-Method"),
			r.URL.Query().Get("method_name"),
			r.Method,
		)
		req.LastIP = firstNonEmpty(
			r.Header.Get("X-Real-IP"),
			r.Header.Get("X-Forwarded-For"),
			r.URL.Query().Get("last_ip"),
		)
		req.RequestCount = 1
		if rawCount := firstNonEmpty(r.Header.Get("X-Access-Request-Count"), r.URL.Query().Get("request_count")); rawCount != "" {
			parsed, err := strconv.Atoi(strings.TrimSpace(rawCount))
			if err != nil {
				return req, fmt.Errorf("invalid request_count")
			}
			req.RequestCount = parsed
		}
		return req, nil
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		if errors.Is(err, io.EOF) {
			return req, fmt.Errorf("invalid request body")
		}
		return req, fmt.Errorf("invalid request body")
	}
	if strings.TrimSpace(req.MethodName) == "" {
		req.MethodName = r.Method
	}
	return req, nil
}

func firstNonEmpty(values ...string) string {
	for _, value := range values {
		trimmed := strings.TrimSpace(value)
		if trimmed != "" {
			return trimmed
		}
	}
	return ""
}

func findAccessProduct(slug string) *accessProduct {
	for _, product := range rpcAccessProducts {
		if product.Slug == slug {
			copy := product
			return &copy
		}
	}
	return nil
}

func defaultQuotaForTier(tier string) int {
	switch tier {
	case "enterprise":
		return 1000000
	case "pro":
		return 100000
	default:
		return 10000
	}
}

func defaultScopesForProduct(productSlug string) []string {
	switch productSlug {
	case "core-rpc":
		return []string{"rpc:read", "rpc:write", "rpc:admin"}
	case "alltra-rpc", "thirdweb-rpc":
		return []string{"rpc:read", "rpc:write"}
	default:
		return []string{"rpc:read"}
	}
}

func (s *Server) handleAccessSubscriptions(w http.ResponseWriter, r *http.Request) {
	if !s.requireDB(w) {
		return
	}
	claims, ok := s.requireUserSession(w, r)
	if !ok {
		return
	}

	switch r.Method {
	case http.MethodGet:
		subscriptions, err := s.userAuth.ListSubscriptions(r.Context(), claims.UserID)
		if err != nil {
			writeError(w, http.StatusInternalServerError, "internal_error", err.Error())
			return
		}
		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(map[string]any{"subscriptions": subscriptions})
	case http.MethodPost:
		var req createSubscriptionRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			writeError(w, http.StatusBadRequest, "bad_request", "Invalid request body")
			return
		}
		product := findAccessProduct(strings.TrimSpace(req.ProductSlug))
		if product == nil {
			writeError(w, http.StatusBadRequest, "bad_request", "Unknown product")
			return
		}
		tier := strings.ToLower(strings.TrimSpace(req.Tier))
		if tier == "" {
			tier = product.DefaultTier
		}
		status := "active"
		notes := "Self-service activation"
		if product.RequiresApproval {
			status = "pending"
			notes = "Awaiting manual approval for restricted product"
		}
		subscription, err := s.userAuth.UpsertProductSubscription(
			r.Context(),
			claims.UserID,
			product.Slug,
			tier,
			status,
			defaultQuotaForTier(tier),
			product.RequiresApproval,
			"",
			notes,
		)
		if err != nil {
			writeError(w, http.StatusInternalServerError, "internal_error", err.Error())
			return
		}
		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(map[string]any{"subscription": subscription})
	default:
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
	}
}

func (s *Server) handleAccessAdminSubscriptions(w http.ResponseWriter, r *http.Request) {
	if !s.requireDB(w) {
		return
	}
	claims, ok := s.requireUserSession(w, r)
	if !ok {
		return
	}
	if !s.isAccessAdmin(claims) {
		writeError(w, http.StatusForbidden, "forbidden", "Access admin privileges required")
		return
	}

	switch r.Method {
	case http.MethodGet:
		status := strings.TrimSpace(r.URL.Query().Get("status"))
		subscriptions, err := s.userAuth.ListAllSubscriptions(r.Context(), status)
		if err != nil {
			writeError(w, http.StatusInternalServerError, "internal_error", err.Error())
			return
		}
		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(map[string]any{"subscriptions": subscriptions})
	case http.MethodPost:
		var req adminSubscriptionActionRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			writeError(w, http.StatusBadRequest, "bad_request", "Invalid request body")
			return
		}
		status := strings.ToLower(strings.TrimSpace(req.Status))
		switch status {
		case "active", "suspended", "revoked":
		default:
			writeError(w, http.StatusBadRequest, "bad_request", "Status must be active, suspended, or revoked")
			return
		}
		if strings.TrimSpace(req.SubscriptionID) == "" {
			writeError(w, http.StatusBadRequest, "bad_request", "Subscription id is required")
			return
		}
		subscription, err := s.userAuth.UpdateSubscriptionStatus(
			r.Context(),
			strings.TrimSpace(req.SubscriptionID),
			status,
			claims.Email,
			strings.TrimSpace(req.Notes),
		)
		if err != nil {
			writeError(w, http.StatusBadRequest, "bad_request", err.Error())
			return
		}
		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(map[string]any{"subscription": subscription})
	default:
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
	}
}

func (s *Server) handleAccessUsage(w http.ResponseWriter, r *http.Request) {
	if !s.requireDB(w) {
		return
	}
	claims, ok := s.requireUserSession(w, r)
	if !ok {
		return
	}
	if r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}

	keys, err := s.userAuth.ListAPIKeys(r.Context(), claims.UserID)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "internal_error", err.Error())
		return
	}
	grouped := map[string]*accessUsageSummary{}
	for _, key := range keys {
		slug := key.ProductSlug
		if slug == "" {
			slug = "unscoped"
		}
		if _, ok := grouped[slug]; !ok {
			grouped[slug] = &accessUsageSummary{ProductSlug: slug}
		}
		summary := grouped[slug]
		if !key.Revoked {
			summary.ActiveKeys++
		}
		summary.RequestsUsed += key.RequestsUsed
		summary.MonthlyQuota += key.MonthlyQuota
	}

	summaries := make([]accessUsageSummary, 0, len(grouped))
	for _, summary := range grouped {
		summaries = append(summaries, *summary)
	}
	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{"usage": summaries})
}

func (s *Server) handleAccessAudit(w http.ResponseWriter, r *http.Request) {
	if !s.requireDB(w) {
		return
	}
	claims, ok := s.requireUserSession(w, r)
	if !ok {
		return
	}
	if r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}

	limit := 20
	if rawLimit := strings.TrimSpace(r.URL.Query().Get("limit")); rawLimit != "" {
		parsed, err := strconv.Atoi(rawLimit)
		if err != nil || parsed < 1 || parsed > 200 {
			writeError(w, http.StatusBadRequest, "bad_request", "Limit must be between 1 and 200")
			return
		}
		limit = parsed
	}

	entries, err := s.userAuth.ListUsageLogs(r.Context(), claims.UserID, limit)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "internal_error", err.Error())
		return
	}

	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{"entries": entries})
}

func (s *Server) handleAccessAdminAudit(w http.ResponseWriter, r *http.Request) {
	if !s.requireDB(w) {
		return
	}
	claims, ok := s.requireUserSession(w, r)
	if !ok {
		return
	}
	if !s.isAccessAdmin(claims) {
		writeError(w, http.StatusForbidden, "forbidden", "Access admin privileges required")
		return
	}
	if r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}

	limit := 50
	if rawLimit := strings.TrimSpace(r.URL.Query().Get("limit")); rawLimit != "" {
		parsed, err := strconv.Atoi(rawLimit)
		if err != nil || parsed < 1 || parsed > 500 {
			writeError(w, http.StatusBadRequest, "bad_request", "Limit must be between 1 and 500")
			return
		}
		limit = parsed
	}
	productSlug := strings.TrimSpace(r.URL.Query().Get("product"))
	if productSlug != "" && findAccessProduct(productSlug) == nil {
		writeError(w, http.StatusBadRequest, "bad_request", "Unknown product")
		return
	}

	entries, err := s.userAuth.ListAllUsageLogs(r.Context(), productSlug, limit)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "internal_error", err.Error())
		return
	}

	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{"entries": entries})
}

func (s *Server) handleAccessAPIKeyAction(w http.ResponseWriter, r *http.Request) {
	if !s.requireDB(w) {
		return
	}
	claims, ok := s.requireUserSession(w, r)
	if !ok {
		return
	}
	if r.Method != http.MethodPost && r.Method != http.MethodDelete {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}

	path := strings.TrimPrefix(r.URL.Path, "/api/v1/access/api-keys/")
	parts := strings.Split(strings.Trim(path, "/"), "/")
	if len(parts) == 0 || parts[0] == "" {
		writeError(w, http.StatusBadRequest, "bad_request", "API key id is required")
		return
	}
	keyID := parts[0]

	if err := s.userAuth.RevokeAPIKey(r.Context(), claims.UserID, keyID); err != nil {
		writeError(w, http.StatusBadRequest, "bad_request", err.Error())
		return
	}

	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"revoked":    true,
		"api_key_id": keyID,
	})
}
