package handlers

import (
	"database/sql"
	"encoding/json"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"

	"switchboard-core/events"
	"switchboard-core/models"
	"switchboard-core/notifications"
	"switchboard-core/store"
)

// ── Handler ─────────────────────────────────

// NotificationHandler handles notification CRUD endpoints.
type NotificationHandler struct {
	stores store.Stores
	hub    *events.Hub
}

// NewNotificationHandler creates a new notification handler.
func NewNotificationHandler(s store.Stores, hub *events.Hub) *NotificationHandler {
	return &NotificationHandler{stores: s, hub: hub}
}

// ── GET /api/v1/notifications ───────────────
// Returns paginated notifications for the authenticated user.
// Query params: ?limit=20&offset=0&unread_only=false

func (h *NotificationHandler) List(c *gin.Context) {
	userID := getUserID(c)
	if userID == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
		return
	}

	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
	offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0"))
	unreadOnly := c.Query("unread_only") == "true"

	if limit <= 0 || limit > 100 {
		limit = 20
	}
	if offset < 0 {
		offset = 0
	}

	items, total, err := h.stores.Notifications.ListByUser(
		c.Request.Context(), userID, limit, offset, unreadOnly)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "query failed"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"data":   items,
		"total":  total,
		"limit":  limit,
		"offset": offset,
	})
}

// ── GET /api/v1/notifications/unread-count ──

func (h *NotificationHandler) UnreadCount(c *gin.Context) {
	userID := getUserID(c)
	if userID == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
		return
	}

	count, err := h.stores.Notifications.UnreadCount(c.Request.Context(), userID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "query failed"})
		return
	}

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

// ── PATCH /api/v1/notifications/:id/read ────

func (h *NotificationHandler) MarkRead(c *gin.Context) {
	userID := getUserID(c)
	if userID == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
		return
	}

	id := c.Param("id")
	if id == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "notification id required"})
		return
	}

	if err := h.stores.Notifications.MarkRead(c.Request.Context(), id, userID); err != nil {
		if err == sql.ErrNoRows {
			c.JSON(http.StatusNotFound, gin.H{"error": "notification not found"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": "update failed"})
		return
	}

	// Sync badge across tabs
	if h.hub != nil {
		payload, _ := json.Marshal(map[string]string{"id": id})
		h.hub.PublishToUser(userID, events.Event{
			Label:   "notification.read",
			Payload: payload,
			Ts:      time.Now().UnixMilli(),
		})
	}

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

// ── POST /api/v1/notifications/mark-all-read ─

func (h *NotificationHandler) MarkAllRead(c *gin.Context) {
	userID := getUserID(c)
	if userID == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
		return
	}

	if err := h.stores.Notifications.MarkAllRead(c.Request.Context(), userID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "update failed"})
		return
	}

	// Sync badge across tabs
	if h.hub != nil {
		payload, _ := json.Marshal(map[string]string{"action": "mark_all_read"})
		h.hub.PublishToUser(userID, events.Event{
			Label:   "notification.read",
			Payload: payload,
			Ts:      time.Now().UnixMilli(),
		})
	}

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

// ── DELETE /api/v1/notifications/:id ────────

func (h *NotificationHandler) Delete(c *gin.Context) {
	userID := getUserID(c)
	if userID == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
		return
	}

	id := c.Param("id")
	if id == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "notification id required"})
		return
	}

	if err := h.stores.Notifications.Delete(c.Request.Context(), id, userID); err != nil {
		if err == sql.ErrNoRows {
			c.JSON(http.StatusNotFound, gin.H{"error": "notification not found"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": "delete failed"})
		return
	}

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

// ── Notification Preferences (v0.20.0 Phase 3) ──

// GET /api/v1/notifications/preferences
func (h *NotificationHandler) ListPreferences(c *gin.Context) {
	userID := getUserID(c)
	if userID == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
		return
	}

	if h.stores.NotifPrefs == nil {
		c.JSON(http.StatusOK, gin.H{"data": []models.NotificationPreference{}})
		return
	}

	prefs, err := h.stores.NotifPrefs.ListForUser(c.Request.Context(), userID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "query failed"})
		return
	}
	if prefs == nil {
		prefs = []models.NotificationPreference{}
	}
	c.JSON(http.StatusOK, gin.H{"data": prefs})
}

// PUT /api/v1/notifications/preferences/:type
func (h *NotificationHandler) SetPreference(c *gin.Context) {
	userID := getUserID(c)
	if userID == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
		return
	}

	notifType := c.Param("type")
	if notifType == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "type is required"})
		return
	}

	var req struct {
		InApp *bool `json:"in_app"`
		Email *bool `json:"email"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	if h.stores.NotifPrefs == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "preferences not available"})
		return
	}

	// Load existing or create new
	existing, _ := h.stores.NotifPrefs.Get(c.Request.Context(), userID, notifType)
	pref := &models.NotificationPreference{
		UserID: userID,
		Type:   notifType,
		InApp:  true,  // default
		Email:  false, // default
	}
	if existing != nil {
		pref = existing
	}
	if req.InApp != nil {
		pref.InApp = *req.InApp
	}
	if req.Email != nil {
		pref.Email = *req.Email
	}

	if err := h.stores.NotifPrefs.Upsert(c.Request.Context(), pref); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "save failed"})
		return
	}

	c.JSON(http.StatusOK, pref)
}

// DELETE /api/v1/notifications/preferences/:type
func (h *NotificationHandler) DeletePreference(c *gin.Context) {
	userID := getUserID(c)
	if userID == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
		return
	}

	notifType := c.Param("type")
	if notifType == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "type is required"})
		return
	}

	if h.stores.NotifPrefs == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "preferences not available"})
		return
	}

	if err := h.stores.NotifPrefs.Delete(c.Request.Context(), userID, notifType); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "delete failed"})
		return
	}

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

// ── Admin Broadcast (v0.28.6) ───────────────

// POST /api/v1/admin/notifications/broadcast
// Admin-only: sends a system.announcement notification to all active users.
func (h *NotificationHandler) Broadcast(c *gin.Context) {
	var req struct {
		Title   string `json:"title" binding:"required"`
		Message string `json:"message" binding:"required"`
		Level   string `json:"level"` // info | warning | critical (default: info)
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	level := req.Level
	if level == "" {
		level = "info"
	}
	if level != "info" && level != "warning" && level != "critical" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "level must be info, warning, or critical"})
		return
	}

	// Get all active user IDs
	userIDs, err := h.stores.Users.ListActiveUserIDs(c.Request.Context())
	if err != nil {
		log.Printf("[broadcast] failed to list active users: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list users"})
		return
	}

	if len(userIDs) == 0 {
		c.JSON(http.StatusOK, gin.H{"message": "no active users", "count": 0})
		return
	}

	// Fan out via notification service
	svc := notifications.Default()
	if svc == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "notification service not available"})
		return
	}

	template := models.Notification{
		Type:  models.NotifTypeAnnouncement,
		Title: req.Title,
		Body:  req.Message,
	}
	svc.NotifyMany(c.Request.Context(), userIDs, template)

	// Also emit a real-time WS broadcast for immediate visibility
	if h.hub != nil {
		payload, _ := json.Marshal(map[string]string{
			"title":   req.Title,
			"message": req.Message,
			"level":   level,
		})
		for _, uid := range userIDs {
			h.hub.PublishToUser(uid, events.Event{
				Label:   "system.broadcast",
				Payload: payload,
				Ts:      time.Now().UnixMilli(),
			})
		}
	}

	log.Printf("[broadcast] admin sent announcement to %d users: %s", len(userIDs), req.Title)
	c.JSON(http.StatusOK, gin.H{"message": "broadcast sent", "count": len(userIDs)})
}
