// Package api 提供 REST API 处理函数。
package api

import (
	"errors"
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"

	"github.com/hhs/camtalk/internal/auth"
	apperr "github.com/hhs/camtalk/internal/errors"
	"github.com/hhs/camtalk/internal/models"
	"github.com/hhs/camtalk/internal/session"
	"github.com/hhs/camtalk/internal/store"
	"github.com/hhs/camtalk/internal/trace"
)

// ConversationHandler 提供对话相关的 REST 端点。
type ConversationHandler struct {
	sessionMgr session.Manager
	tokenMgr   *auth.TokenManager
	msgRepo    store.MessageRepository // 可选，为 nil 时 fallback 到内存查询
}

// NewConversationHandler 创建 ConversationHandler。
// msgRepo 可选，为 nil 时消息查询走内存。
func NewConversationHandler(sessionMgr session.Manager, tokenMgr *auth.TokenManager, msgRepo store.MessageRepository) *ConversationHandler {
	return &ConversationHandler{
		sessionMgr: sessionMgr,
		tokenMgr:   tokenMgr,
		msgRepo:    msgRepo,
	}
}

// RegisterRoutes 注册对话相关路由到给定的路由组。所有端点需要认证。
func (h *ConversationHandler) RegisterRoutes(rg *gin.RouterGroup) {
	conv := rg.Group("/conversations", auth.AuthMiddleware(h.tokenMgr))
	{
		conv.GET("", h.List)
		conv.POST("", h.Create)
		conv.GET("/:id", h.Get)
		conv.PATCH("/:id", h.UpdateTitle)
		conv.DELETE("/:id", h.Delete)
		conv.GET("/:id/messages", h.GetMessages)
	}
}

// List GET /api/conversations — 获取当前用户的对话列表。
func (h *ConversationHandler) List(c *gin.Context) {
	log := trace.FromContext(c.Request.Context())
	userID := c.GetString(auth.ContextKeyUserID)

	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	size, _ := strconv.Atoi(c.DefaultQuery("size", "20"))

	if page <= 0 {
		page = 1
	}
	if size <= 0 || size > 100 {
		size = 20
	}

	summaries, total, err := h.sessionMgr.ListByUser(c.Request.Context(), userID, page, size)
	if err != nil {
		log.Errorw("list conversations failed",
			"user_id", userID,
			"error", err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"code":    apperr.CodeInternalError,
			"message": "failed to list conversations",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"conversations": summaries,
		"total":         total,
		"page":          page,
		"size":          size,
	})
}

// CreateConversationRequest POST /api/conversations 请求体。
type CreateConversationRequest struct {
	Config *models.SessionConfig `json:"config,omitempty"`
}

// Create POST /api/conversations — 创建新对话。
func (h *ConversationHandler) Create(c *gin.Context) {
	log := trace.FromContext(c.Request.Context())
	userID := c.GetString(auth.ContextKeyUserID)

	var req CreateConversationRequest
	_ = c.ShouldBindJSON(&req)

	cfg := models.DefaultConfig()
	if req.Config != nil {
		cfg = *req.Config
	}

	sessionID, err := h.sessionMgr.Create(c.Request.Context(), userID, cfg)
	if err != nil {
		log.Errorw("create conversation failed",
			"user_id", userID,
			"error", err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"code":    apperr.CodeInternalError,
			"message": "failed to create conversation",
		})
		return
	}

	sess, err := h.sessionMgr.Get(c.Request.Context(), sessionID)
	if err != nil {
		log.Errorw("retrieve created conversation failed",
			"session_id", sessionID,
			"error", err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"code":    apperr.CodeInternalError,
			"message": "failed to retrieve created conversation",
		})
		return
	}

	log.Infow("conversation created",
		"conversation_id", sess.ID,
		"user_id", userID)
	c.JSON(http.StatusCreated, gin.H{
		"id":          sess.ID,
		"title":       sess.Title,
		"created_at":  sess.CreatedAt,
		"updated_at":  sess.UpdatedAt,
	})
}

// Get GET /api/conversations/:id — 获取对话详情。
func (h *ConversationHandler) Get(c *gin.Context) {
	sessionID := c.Param("id")

	sess, err := h.getSessionForUser(c, sessionID)
	if err != nil {
		return // getSessionForUser 已写入响应
	}

	c.JSON(http.StatusOK, gin.H{
		"id":          sess.ID,
		"title":       sess.Title,
		"created_at":  sess.CreatedAt,
		"updated_at":  sess.UpdatedAt,
		"config":      sess.Config,
	})
}

// UpdateTitleRequest PATCH /api/conversations/:id 请求体。
type UpdateTitleRequest struct {
	Title string `json:"title"`
}

// UpdateTitle PATCH /api/conversations/:id — 更新对话标题。
func (h *ConversationHandler) UpdateTitle(c *gin.Context) {
	log := trace.FromContext(c.Request.Context())
	sessionID := c.Param("id")

	// 先校验归属
	if _, err := h.getSessionForUser(c, sessionID); err != nil {
		return
	}

	var req UpdateTitleRequest
	if err := c.ShouldBindJSON(&req); err != nil || req.Title == "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"code":    apperr.CodeInvalidInput,
			"message": "title is required",
		})
		return
	}

	if len([]rune(req.Title)) > 100 {
		c.JSON(http.StatusBadRequest, gin.H{
			"code":    apperr.CodeInvalidInput,
			"message": "title must be 100 characters or less",
		})
		return
	}

	if err := h.sessionMgr.UpdateTitle(c.Request.Context(), sessionID, req.Title); err != nil {
		if errors.Is(err, session.ErrSessionNotFound) {
			c.JSON(http.StatusNotFound, gin.H{
				"code":    apperr.CodeSessionNotFound,
				"message": "conversation not found",
			})
			return
		}
		log.Errorw("update title failed",
			"session_id", sessionID,
			"error", err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"code":    apperr.CodeInternalError,
			"message": "failed to update title",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "title updated",
	})
}

// Delete DELETE /api/conversations/:id — 删除对话。
func (h *ConversationHandler) Delete(c *gin.Context) {
	log := trace.FromContext(c.Request.Context())
	sessionID := c.Param("id")

	// 先校验归属
	if _, err := h.getSessionForUser(c, sessionID); err != nil {
		return
	}

	if err := h.sessionMgr.Destroy(c.Request.Context(), sessionID); err != nil {
		if errors.Is(err, session.ErrSessionNotFound) {
			c.JSON(http.StatusNotFound, gin.H{
				"code":    apperr.CodeSessionNotFound,
				"message": "conversation not found",
			})
			return
		}
		log.Errorw("delete conversation failed",
			"session_id", sessionID,
			"error", err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"code":    apperr.CodeInternalError,
			"message": "failed to delete conversation",
		})
		return
	}

	c.Status(http.StatusNoContent)
}

// GetMessages GET /api/conversations/:id/messages — 获取对话消息列表。
//
// 查询参数：
//   - limit: 返回消息数量上限，默认 50
//   - before: 消息 ID 游标（用于分页），返回此 ID 之前的消息
func (h *ConversationHandler) GetMessages(c *gin.Context) {
	log := trace.FromContext(c.Request.Context())
	sessionID := c.Param("id")

	// 先校验归属
	if _, err := h.getSessionForUser(c, sessionID); err != nil {
		return
	}

	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
	if limit <= 0 || limit > 200 {
		limit = 50
	}

	beforeID, _ := strconv.ParseInt(c.DefaultQuery("before", "0"), 10, 64)

	// 优先从 PostgreSQL 查询（支持持久化后的全量历史）
	if h.msgRepo != nil {
		messages, err := h.msgRepo.GetMessages(c.Request.Context(), sessionID, limit, beforeID)
		if err != nil {
			log.Errorw("get messages failed",
				"session_id", sessionID,
				"error", err)
			c.JSON(http.StatusInternalServerError, gin.H{
				"code":    apperr.CodeInternalError,
				"message": "failed to get messages",
			})
			return
		}
		count, _ := h.msgRepo.GetMessageCount(c.Request.Context(), sessionID)
		if messages == nil {
			messages = []store.StoredMessage{}
		}
		c.JSON(http.StatusOK, gin.H{
			"messages": messages,
			"total":    count,
		})
		return
	}

	// fallback：从内存查询
	allMessages, err := h.sessionMgr.GetHistory(c.Request.Context(), sessionID, 0)
	if err != nil {
		if errors.Is(err, session.ErrSessionNotFound) {
			c.JSON(http.StatusNotFound, gin.H{
				"code":    apperr.CodeSessionNotFound,
				"message": "conversation not found",
			})
			return
		}
		log.Errorw("get messages failed",
			"session_id", sessionID,
			"error", err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"code":    apperr.CodeInternalError,
			"message": "failed to get messages",
		})
		return
	}

	total := len(allMessages)

	// beforeID > 0 时表示偏移量（兼容旧接口语义）
	if beforeID > 0 && int(beforeID) <= total {
		allMessages = allMessages[:beforeID]
	}

	// 取最后 limit 条
	start := len(allMessages) - limit
	if start < 0 {
		start = 0
	}
	messages := allMessages[start:]

	if messages == nil {
		messages = []models.Message{}
	}
	c.JSON(http.StatusOK, gin.H{
		"messages": messages,
		"total":    total,
	})
}

// getSessionForUser 获取会话并校验当前用户是否有权限访问。
// 返回 404（而非 403）以避免信息泄露。
func (h *ConversationHandler) getSessionForUser(c *gin.Context, sessionID string) (*models.Session, error) {
	sess, err := h.sessionMgr.Get(c.Request.Context(), sessionID)
	if err != nil {
		if errors.Is(err, session.ErrSessionNotFound) {
			c.JSON(http.StatusNotFound, gin.H{
				"code":    apperr.CodeSessionNotFound,
				"message": "conversation not found",
			})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{
				"code":    apperr.CodeInternalError,
				"message": "internal server error",
			})
		}
		return nil, err
	}

	userID := c.GetString(auth.ContextKeyUserID)
	if sess.UserID != userID {
		c.JSON(http.StatusNotFound, gin.H{
			"code":    apperr.CodeSessionNotFound,
			"message": "conversation not found",
		})
		return nil, errors.New("forbidden")
	}

	return sess, nil
}
