// Package handlers provides HTTP request handlers for the API.
package handlers

import (
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/gorilla/websocket"

	"github.com/Wikid82/charon/backend/internal/logger"
	"github.com/Wikid82/charon/backend/internal/services"
)

// CerberusLogsHandler handles WebSocket connections for streaming security logs.
type CerberusLogsHandler struct {
	watcher *services.LogWatcher
}

// NewCerberusLogsHandler creates a new handler for Cerberus security log streaming.
func NewCerberusLogsHandler(watcher *services.LogWatcher) *CerberusLogsHandler {
	return &CerberusLogsHandler{watcher: watcher}
}

// LiveLogs handles WebSocket connections for Cerberus security log streaming.
// It upgrades the HTTP connection to WebSocket, subscribes to the LogWatcher,
// and streams SecurityLogEntry as JSON to connected clients.
//
// Query parameters for filtering:
//   - source: filter by source (waf, crowdsec, ratelimit, acl, normal)
//   - blocked_only: only show blocked requests (true/false)
//   - level: filter by log level (info, warn, error)
//   - ip: filter by client IP (partial match)
//   - host: filter by host (partial match)
func (h *CerberusLogsHandler) LiveLogs(c *gin.Context) {
	logger.Log().Info("Cerberus logs WebSocket connection attempt")

	// Upgrade HTTP connection to WebSocket
	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		logger.Log().WithError(err).Error("Failed to upgrade Cerberus logs WebSocket")
		return
	}
	defer func() {
		if err := conn.Close(); err != nil {
			logger.Log().WithError(err).Debug("Failed to close Cerberus logs WebSocket connection")
		}
	}()

	// Generate unique subscriber ID for logging
	subscriberID := uuid.New().String()
	logger.Log().WithField("subscriber_id", subscriberID).Info("Cerberus logs WebSocket connected")

	// Parse query filters
	sourceFilter := strings.ToLower(c.Query("source"))     // waf, crowdsec, ratelimit, acl, normal
	levelFilter := strings.ToLower(c.Query("level"))       // info, warn, error
	ipFilter := c.Query("ip")                              // Partial match on client IP
	hostFilter := strings.ToLower(c.Query("host"))         // Partial match on host
	blockedOnly := c.Query("blocked_only") == "true"       // Only show blocked requests

	// Subscribe to log watcher
	logChan := h.watcher.Subscribe()
	defer h.watcher.Unsubscribe(logChan)

	// Channel to detect client disconnect
	done := make(chan struct{})
	go func() {
		defer close(done)
		for {
			if _, _, err := conn.ReadMessage(); err != nil {
				return
			}
		}
	}()

	// Keep-alive ticker
	ticker := time.NewTicker(30 * time.Second)
	defer ticker.Stop()

	for {
		select {
		case entry, ok := <-logChan:
			if !ok {
				// Channel closed, log watcher stopped
				return
			}

			// Apply source filter
			if sourceFilter != "" && !strings.EqualFold(entry.Source, sourceFilter) {
				continue
			}

			// Apply level filter
			if levelFilter != "" && !strings.EqualFold(entry.Level, levelFilter) {
				continue
			}

			// Apply IP filter (partial match)
			if ipFilter != "" && !strings.Contains(entry.ClientIP, ipFilter) {
				continue
			}

			// Apply host filter (partial match, case-insensitive)
			if hostFilter != "" && !strings.Contains(strings.ToLower(entry.Host), hostFilter) {
				continue
			}

			// Apply blocked_only filter
			if blockedOnly && !entry.Blocked {
				continue
			}

			// Send to WebSocket client
			if err := conn.WriteJSON(entry); err != nil {
				logger.Log().WithError(err).WithField("subscriber_id", subscriberID).Debug("Failed to write Cerberus log to WebSocket")
				return
			}

		case <-ticker.C:
			// Send ping to keep connection alive
			if err := conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
				logger.Log().WithError(err).WithField("subscriber_id", subscriberID).Debug("Failed to send ping to Cerberus logs WebSocket")
				return
			}

		case <-done:
			// Client disconnected
			logger.Log().WithField("subscriber_id", subscriberID).Info("Cerberus logs WebSocket client disconnected")
			return
		}
	}
}
