package handler

import (
	"net/http"
	"slices"
	"strings"
	"time"
	"xboard-go/internal/database"
	"xboard-go/internal/model"
	"xboard-go/internal/service"

	"github.com/gin-gonic/gin"
)

func UserKnowledgeFetch(c *gin.Context) {
	language := strings.TrimSpace(c.DefaultQuery("language", "zh-CN"))
	query := database.DB.Model(&model.Knowledge{}).Where("`show` = ?", 1).Order("sort ASC, id ASC")
	if language != "" {
		query = query.Where("language = ? OR language = ''", language)
	}

	var articles []model.Knowledge
	if err := query.Find(&articles).Error; err != nil {
		Fail(c, 500, "failed to fetch knowledge articles")
		return
	}
	Success(c, articles)
}

func UserKnowledgeCategories(c *gin.Context) {
	var categories []string
	if err := database.DB.Model(&model.Knowledge{}).
		Where("`show` = ?", 1).
		Distinct().
		Order("category ASC").
		Pluck("category", &categories).Error; err != nil {
		Fail(c, 500, "failed to fetch knowledge categories")
		return
	}

	filtered := make([]string, 0, len(categories))
	for _, category := range categories {
		category = strings.TrimSpace(category)
		if category != "" && !slices.Contains(filtered, category) {
			filtered = append(filtered, category)
		}
	}
	Success(c, filtered)
}

func UserTicketFetch(c *gin.Context) {
	user, ok := currentUser(c)
	if !ok {
		Fail(c, http.StatusUnauthorized, "user not found")
		return
	}

	if ticketID := strings.TrimSpace(c.Query("id")); ticketID != "" {
		var ticket model.Ticket
		if err := database.DB.Where("id = ? AND user_id = ?", ticketID, user.ID).First(&ticket).Error; err != nil {
			Fail(c, 404, "ticket not found")
			return
		}

		var messages []model.TicketMessage
		_ = database.DB.Where("ticket_id = ?", ticket.ID).Order("id ASC").Find(&messages).Error

		payload := gin.H{
			"id":           ticket.ID,
			"user_id":      ticket.UserID,
			"subject":      ticket.Subject,
			"level":        ticket.Level,
			"status":       ticket.Status,
			"reply_status": ticket.ReplyStatus,
			"created_at":   ticket.CreatedAt,
			"updated_at":   ticket.UpdatedAt,
			"message":      buildTicketMessages(messages, user.ID),
		}
		Success(c, payload)
		return
	}

	var tickets []model.Ticket
	if err := database.DB.Where("user_id = ?", user.ID).Order("updated_at DESC, id DESC").Find(&tickets).Error; err != nil {
		Fail(c, 500, "failed to fetch tickets")
		return
	}
	Success(c, tickets)
}

func UserTicketSave(c *gin.Context) {
	user, ok := currentUser(c)
	if !ok {
		Fail(c, http.StatusUnauthorized, "user not found")
		return
	}

	var req struct {
		Subject string `json:"subject" binding:"required"`
		Level   int    `json:"level"`
		Message string `json:"message" binding:"required"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, 400, "invalid ticket payload")
		return
	}

	now := time.Now().Unix()
	ticket := model.Ticket{
		UserID:      user.ID,
		Subject:     strings.TrimSpace(req.Subject),
		Level:       req.Level,
		Status:      0,
		ReplyStatus: 1,
		CreatedAt:   now,
		UpdatedAt:   now,
	}

	if err := database.DB.Create(&ticket).Error; err != nil {
		Fail(c, 500, "failed to create ticket")
		return
	}

	message := model.TicketMessage{
		UserID:    user.ID,
		TicketID:  ticket.ID,
		Message:   strings.TrimSpace(req.Message),
		CreatedAt: now,
		UpdatedAt: now,
	}
	if err := database.DB.Create(&message).Error; err != nil {
		Fail(c, 500, "failed to save ticket message")
		return
	}

	SuccessMessage(c, "ticket created", true)
}

func UserTicketReply(c *gin.Context) {
	user, ok := currentUser(c)
	if !ok {
		Fail(c, http.StatusUnauthorized, "user not found")
		return
	}

	var req struct {
		ID      int    `json:"id" binding:"required"`
		Message string `json:"message" binding:"required"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, 400, "invalid ticket reply payload")
		return
	}

	var ticket model.Ticket
	if err := database.DB.Where("id = ? AND user_id = ?", req.ID, user.ID).First(&ticket).Error; err != nil {
		Fail(c, 404, "ticket not found")
		return
	}
	if ticket.Status != 0 {
		Fail(c, 400, "ticket is closed")
		return
	}

	now := time.Now().Unix()
	reply := model.TicketMessage{
		UserID:    user.ID,
		TicketID:  ticket.ID,
		Message:   strings.TrimSpace(req.Message),
		CreatedAt: now,
		UpdatedAt: now,
	}
	if err := database.DB.Create(&reply).Error; err != nil {
		Fail(c, 500, "failed to save ticket reply")
		return
	}

	_ = database.DB.Model(&model.Ticket{}).
		Where("id = ?", ticket.ID).
		Updates(map[string]any{"reply_status": 1, "updated_at": now}).Error

	SuccessMessage(c, "ticket replied", true)
}

func UserTicketClose(c *gin.Context) {
	updateTicketStatus(c, 1, "ticket closed")
}

func UserTicketWithdraw(c *gin.Context) {
	updateTicketStatus(c, 1, "ticket withdrawn")
}

func UserGetActiveSession(c *gin.Context) {
	user, ok := currentUser(c)
	if !ok {
		Fail(c, http.StatusUnauthorized, "user not found")
		return
	}

	authToken, _ := c.Get("auth_token")
	currentToken, _ := authToken.(string)
	sessions := service.GetUserSessions(user.ID, currentToken)
	payload := make([]gin.H, 0, len(sessions))
	currentSessionID := currentSessionID(c)

	for _, session := range sessions {
		payload = append(payload, gin.H{
			"id":           session.ID,
			"name":         session.Name,
			"user_agent":   session.UserAgent,
			"ip":           firstString(session.IP, "-"),
			"created_at":   session.CreatedAt,
			"last_used_at": session.LastUsedAt,
			"expires_at":   session.ExpiresAt,
			"is_current":   session.ID == currentSessionID,
		})
	}

	Success(c, payload)
}

func UserRemoveActiveSession(c *gin.Context) {
	user, ok := currentUser(c)
	if !ok {
		Fail(c, http.StatusUnauthorized, "user not found")
		return
	}

	var req struct {
		SessionID string `json:"session_id" binding:"required"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, 400, "session_id is required")
		return
	}

	if !service.RemoveUserSession(user.ID, req.SessionID) {
		Fail(c, 404, "session not found")
		return
	}

	SuccessMessage(c, "session removed", true)
}

func updateTicketStatus(c *gin.Context, status int, message string) {
	user, ok := currentUser(c)
	if !ok {
		Fail(c, http.StatusUnauthorized, "user not found")
		return
	}

	var req struct {
		ID int `json:"id" binding:"required"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, 400, "ticket id is required")
		return
	}

	now := time.Now().Unix()
	result := database.DB.Model(&model.Ticket{}).
		Where("id = ? AND user_id = ?", req.ID, user.ID).
		Updates(map[string]any{
			"status":     status,
			"updated_at": now,
		})
	if result.Error != nil {
		Fail(c, 500, "failed to update ticket")
		return
	}
	if result.RowsAffected == 0 {
		Fail(c, 404, "ticket not found")
		return
	}

	SuccessMessage(c, message, true)
}

func buildTicketMessages(messages []model.TicketMessage, currentUserID int) []gin.H {
	payload := make([]gin.H, 0, len(messages))
	for _, message := range messages {
		payload = append(payload, gin.H{
			"id":         message.ID,
			"user_id":    message.UserID,
			"message":    message.Message,
			"created_at": message.CreatedAt,
			"updated_at": message.UpdatedAt,
			"is_me":      message.UserID == currentUserID,
		})
	}
	return payload
}

func currentSessionID(c *gin.Context) string {
	value, exists := c.Get("session")
	if !exists {
		return ""
	}
	session, ok := value.(service.SessionRecord)
	if !ok {
		return ""
	}
	return session.ID
}

func firstString(values ...string) string {
	for _, value := range values {
		if strings.TrimSpace(value) != "" {
			return value
		}
	}
	return ""
}
