package cache

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	olriclib "github.com/olric-data/olric"
)

// ScanHandler handles cache SCAN/LIST requests for listing keys in a distributed map.
// It expects a JSON body with "dmap" (distributed map name) and optionally "match" (regex pattern).
// Returns all keys in the map, or only keys matching the pattern if provided.
//
// Request body:
//
//	{
//	  "dmap": "my-cache",
//	  "match": "user:*"  // Optional: regex pattern to filter keys
//	}
//
// Response:
//
//	{
//	  "keys": ["user:123", "user:456"],
//	  "count": 2,
//	  "dmap": "my-cache"
//	}
func (h *CacheHandlers) ScanHandler(w http.ResponseWriter, r *http.Request) {
	if h.olricClient == nil {
		writeError(w, http.StatusServiceUnavailable, "Olric cache client not initialized")
		return
	}

	if r.Method != http.MethodPost {
		writeError(w, http.StatusMethodNotAllowed, "method not allowed")
		return
	}

	r.Body = http.MaxBytesReader(w, r.Body, 10<<20) // 10MB
	var req ScanRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "invalid json body")
		return
	}

	if strings.TrimSpace(req.DMap) == "" {
		writeError(w, http.StatusBadRequest, "dmap is required")
		return
	}

	ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
	defer cancel()

	// Namespace isolation: prefix dmap with namespace
	namespace := getNamespaceFromContext(ctx)
	if namespace == "" {
		writeError(w, http.StatusUnauthorized, "namespace not found in context")
		return
	}
	namespacedDMap := fmt.Sprintf("%s:%s", namespace, req.DMap)

	olricCluster := h.olricClient.GetClient()
	dm, err := olricCluster.NewDMap(namespacedDMap)
	if err != nil {
		writeError(w, http.StatusInternalServerError, fmt.Sprintf("failed to create DMap: %v", err))
		return
	}

	var iterator olriclib.Iterator
	if req.Match != "" {
		iterator, err = dm.Scan(ctx, olriclib.Match(req.Match))
	} else {
		iterator, err = dm.Scan(ctx)
	}

	if err != nil {
		writeError(w, http.StatusInternalServerError, fmt.Sprintf("failed to scan: %v", err))
		return
	}
	defer iterator.Close()

	var keys []string
	for iterator.Next() {
		keys = append(keys, iterator.Key())
	}

	writeJSON(w, http.StatusOK, map[string]any{
		"keys":  keys,
		"count": len(keys),
		"dmap":  req.DMap,
	})
}

// HealthHandler handles health check requests for the Olric cache service.
// Returns 200 OK if the cache is healthy, or 503 Service Unavailable if not.
//
// Response (success):
//
//	{
//	  "status": "ok",
//	  "service": "olric"
//	}
//
// Response (failure):
//
//	{
//	  "error": "cache health check failed: ..."
//	}
func (h *CacheHandlers) HealthHandler(w http.ResponseWriter, r *http.Request) {
	if h.olricClient == nil {
		writeError(w, http.StatusServiceUnavailable, "Olric cache client not initialized")
		return
	}

	ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
	defer cancel()

	err := h.olricClient.Health(ctx)
	if err != nil {
		writeError(w, http.StatusServiceUnavailable, fmt.Sprintf("cache health check failed: %v", err))
		return
	}

	writeJSON(w, http.StatusOK, map[string]any{
		"status":  "ok",
		"service": "olric",
	})
}
