package httpapi

import (
	"log/slog"
	"net/http"

	"github.com/go-chi/chi/v5"

	"commilitia.net/cdrop/internal/db"
	"commilitia.net/cdrop/internal/jwtauth"
)

type deviceItem struct {
	Name     string `json:"name"`
	Type     string `json:"type"`
	Online   bool   `json:"online"`
	LastSeen int64  `json:"last_seen"`
}

// handleDevices returns the calling user's known devices with live online flags.
// Online = currently connected to /api/events; LastSeen = epoch from devices.last_seen.
func (s *Server) handleDevices(w http.ResponseWriter, r *http.Request) {
	claims, _ := jwtauth.ClaimsFromContext(r.Context())

	devs, err := s.queries.ListDevicesByUser(r.Context(), claims.UserID)
	if err != nil {
		slog.Error("list devices failed", "err", err, "user", claims.UserID)
		writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "db"})
		return
	}

	out := make([]deviceItem, 0, len(devs))
	for _, d := range devs {
		out = append(out, deviceItem{
			Name:     d.Name,
			Type:     d.Type,
			Online:   s.hub.Online(claims.UserID, d.Name),
			LastSeen: d.LastSeen,
		})
	}
	writeJSON(w, http.StatusOK, map[string]any{"devices": out})
}

// handleDeleteDevice 注销设备：删除 (user, name) 行 + Kick 在线 SSE + 立刻广播
// 新的 presence。请求方若注销的是自己当前设备，前端需同步调用 logout，否则
// 任何后续带 X-Device-Name 的认证请求都会被中间件再次 UPSERT 回来。
//
// 这是登录会话列表里「原生客户端（桌面 / iOS）」一栏的登出路径——它们用 IdP 令牌、
// 不入 web_sessions，故按设备名吊销（AUTH.md §4）。注销设备属敏感动作，与吊销网页
// 会话同口径要求最近一次 step-up（403 step_up_required，前端据此发起再认证）。
func (s *Server) handleDeleteDevice(w http.ResponseWriter, r *http.Request) {
	if s.cfg.StepUpEnabled && !s.sessionRecentlySteppedUp(r) {
		writeJSON(w, http.StatusForbidden, map[string]string{"error": "step_up_required"})
		return
	}
	claims, _ := jwtauth.ClaimsFromContext(r.Context())
	name := chi.URLParam(r, "name")
	if name == "" {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "missing device name"})
		return
	}

	rows, err := s.queries.DeleteDevice(r.Context(), db.DeleteDeviceParams{
		UserID: claims.UserID,
		Name:   name,
	})
	if err != nil {
		slog.Error("delete device failed", "err", err, "user", claims.UserID, "name", name)
		writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "db"})
		return
	}
	if rows == 0 {
		writeJSON(w, http.StatusNotFound, map[string]string{"error": "device not found"})
		return
	}

	s.hub.Kick(claims.UserID, name)
	s.hub.PublishPresence(r.Context(), claims.UserID)
	w.WriteHeader(http.StatusNoContent)
}
