package monitoring

import (
	"net/http"
	"strconv"
	"time"

	"github.com/atlasos/calypso/internal/common/database"
	"github.com/atlasos/calypso/internal/common/logger"
	"github.com/atlasos/calypso/internal/iam"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

// Handler handles monitoring API requests
type Handler struct {
	alertService  *AlertService
	metricsService *MetricsService
	eventHub     *EventHub
	db           *database.DB
	logger       *logger.Logger
}

// NewHandler creates a new monitoring handler
func NewHandler(db *database.DB, log *logger.Logger, alertService *AlertService, metricsService *MetricsService, eventHub *EventHub) *Handler {
	return &Handler{
		alertService:   alertService,
		metricsService: metricsService,
		eventHub:       eventHub,
		db:             db,
		logger:         log,
	}
}

// ListAlerts lists alerts with optional filters
func (h *Handler) ListAlerts(c *gin.Context) {
	filters := &AlertFilters{}

	// Parse query parameters
	if severity := c.Query("severity"); severity != "" {
		filters.Severity = AlertSeverity(severity)
	}
	if source := c.Query("source"); source != "" {
		filters.Source = AlertSource(source)
	}
	if acknowledged := c.Query("acknowledged"); acknowledged != "" {
		ack, err := strconv.ParseBool(acknowledged)
		if err == nil {
			filters.IsAcknowledged = &ack
		}
	}
	if resourceType := c.Query("resource_type"); resourceType != "" {
		filters.ResourceType = resourceType
	}
	if resourceID := c.Query("resource_id"); resourceID != "" {
		filters.ResourceID = resourceID
	}
	if limitStr := c.Query("limit"); limitStr != "" {
		if limit, err := strconv.Atoi(limitStr); err == nil && limit > 0 {
			filters.Limit = limit
		}
	}

	alerts, err := h.alertService.ListAlerts(c.Request.Context(), filters)
	if err != nil {
		h.logger.Error("Failed to list alerts", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list alerts"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"alerts": alerts})
}

// GetAlert retrieves a single alert
func (h *Handler) GetAlert(c *gin.Context) {
	alertID := c.Param("id")

	alert, err := h.alertService.GetAlert(c.Request.Context(), alertID)
	if err != nil {
		h.logger.Error("Failed to get alert", "alert_id", alertID, "error", err)
		c.JSON(http.StatusNotFound, gin.H{"error": "alert not found"})
		return
	}

	c.JSON(http.StatusOK, alert)
}

// AcknowledgeAlert acknowledges an alert
func (h *Handler) AcknowledgeAlert(c *gin.Context) {
	alertID := c.Param("id")

	// Get current user
	user, exists := c.Get("user")
	if !exists {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
		return
	}

	authUser, ok := user.(*iam.User)
	if !ok {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user context"})
		return
	}

	if err := h.alertService.AcknowledgeAlert(c.Request.Context(), alertID, authUser.ID); err != nil {
		h.logger.Error("Failed to acknowledge alert", "alert_id", alertID, "error", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "alert acknowledged"})
}

// ResolveAlert resolves an alert
func (h *Handler) ResolveAlert(c *gin.Context) {
	alertID := c.Param("id")

	if err := h.alertService.ResolveAlert(c.Request.Context(), alertID); err != nil {
		h.logger.Error("Failed to resolve alert", "alert_id", alertID, "error", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "alert resolved"})
}

// GetMetrics retrieves current system metrics
func (h *Handler) GetMetrics(c *gin.Context) {
	metrics, err := h.metricsService.CollectMetrics(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to collect metrics", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to collect metrics"})
		return
	}

	c.JSON(http.StatusOK, metrics)
}

// WebSocketHandler handles WebSocket connections for event streaming
func (h *Handler) WebSocketHandler(c *gin.Context) {
	// Upgrade connection to WebSocket
	upgrader := websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool {
			// Allow all origins for now (should be restricted in production)
			return true
		},
	}

	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		h.logger.Error("Failed to upgrade WebSocket connection", "error", err)
		return
	}

	// Register client
	h.eventHub.register <- conn

	// Keep connection alive and handle ping/pong
	go func() {
		defer func() {
			h.eventHub.unregister <- conn
		}()

		conn.SetReadDeadline(time.Now().Add(60 * time.Second))
		conn.SetPongHandler(func(string) error {
			conn.SetReadDeadline(time.Now().Add(60 * time.Second))
			return nil
		})

		// Send ping every 30 seconds
		ticker := time.NewTicker(30 * time.Second)
		defer ticker.Stop()

		for {
			select {
			case <-ticker.C:
				if err := conn.WriteMessage(websocket.PingMessage, nil); err != nil {
					return
				}
			}
		}
	}()
}

