package handlers

import (
	"encoding/json"
	"net/http"

	"github.com/go-playground/validator/v10"
	"github.com/eduard256/Strix/internal/camera/discovery"
	"github.com/eduard256/Strix/internal/models"
	"github.com/eduard256/Strix/internal/utils/logger"
	"github.com/eduard256/Strix/pkg/sse"
)

// DiscoverHandler handles stream discovery requests
type DiscoverHandler struct {
	scanner   *discovery.Scanner
	sseServer *sse.Server
	validator *validator.Validate
	secrets   *logger.SecretStore
	logger    interface{ Debug(string, ...any); Error(string, error, ...any); Info(string, ...any) }
}

// NewDiscoverHandler creates a new discover handler
func NewDiscoverHandler(
	scanner *discovery.Scanner,
	sseServer *sse.Server,
	secrets *logger.SecretStore,
	logger interface{ Debug(string, ...any); Error(string, error, ...any); Info(string, ...any) },
) *DiscoverHandler {
	return &DiscoverHandler{
		scanner:   scanner,
		sseServer: sseServer,
		secrets:   secrets,
		validator: validator.New(),
		logger:    logger,
	}
}

// ServeHTTP handles discovery requests
func (h *DiscoverHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// Parse request body
	var req models.StreamDiscoveryRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		h.logger.Error("failed to decode discovery request", err)
		h.sendErrorResponse(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	// Set defaults
	if req.ModelLimit <= 0 {
		req.ModelLimit = 6
	}
	if req.Timeout <= 0 {
		req.Timeout = 240 // 4 minutes
	}
	if req.MaxStreams <= 0 {
		req.MaxStreams = 10
	}

	// Validate request
	if err := h.validator.Struct(req); err != nil {
		h.logger.Error("discovery request validation failed", err)
		h.sendErrorResponse(w, "Validation failed: "+err.Error(), http.StatusBadRequest)
		return
	}

	// Register password as a secret so it gets masked in all log output.
	// The secret is automatically unregistered when the request completes.
	if req.Password != "" {
		h.secrets.Add(req.Password)
		defer h.secrets.Remove(req.Password)
	}

	h.logger.Info("stream discovery requested",
		"target", req.Target,
		"model", req.Model,
		"timeout", req.Timeout,
		"max_streams", req.MaxStreams,
		"remote_addr", r.RemoteAddr,
	)

	// Check if SSE is supported
	flusher, ok := w.(http.Flusher)
	if !ok {
		h.logger.Info("SSE not supported by client", "remote_addr", r.RemoteAddr)
		h.sendErrorResponse(w, "SSE not supported", http.StatusInternalServerError)
		return
	}

	// Set SSE headers
	w.Header().Set("Content-Type", "text/event-stream")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("Connection", "keep-alive")
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("X-Accel-Buffering", "no") // Disable Nginx buffering

	// Flush headers
	flusher.Flush()

	// Create SSE stream writer
	streamWriter, err := h.sseServer.NewStreamWriter(w, r)
	if err != nil {
		h.logger.Error("failed to create SSE stream", err)
		return
	}
	defer streamWriter.Close()

	// Perform discovery
	result, err := h.scanner.Scan(r.Context(), req, streamWriter)
	if err != nil {
		h.logger.Error("discovery failed", err)
		_ = streamWriter.SendError(err)
		return
	}

	h.logger.Info("discovery completed",
		"target", req.Target,
		"tested", result.TotalTested,
		"found", result.TotalFound,
		"duration", result.Duration,
	)
}

// sendErrorResponse sends an error response for non-SSE requests
func (h *DiscoverHandler) sendErrorResponse(w http.ResponseWriter, message string, statusCode int) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(statusCode)

	response := map[string]interface{}{
		"error":   true,
		"message": message,
		"code":    statusCode,
	}

	_ = json.NewEncoder(w).Encode(response)
}