package handlers

import (
	"strconv"
	"strings"
	"time"

	"server/internal/middleware"
	"server/internal/models"
	internalWs "server/internal/websocket"

	"github.com/gofiber/fiber/v2"
	"gorm.io/gorm"
)

func chatMessageDTO(m models.ChatMessage, staffName string) map[string]any {
	return map[string]any{
		"id":          m.ID,
		"staffId":     m.StaffID,
		"studentRkId": m.StudentRkID,
		"senderRole":  m.SenderRole,
		"body":        m.Body,
		"createdAt":   m.CreatedAt,
		"readAt":      m.ReadAt,
		"staffName":   staffName,
	}
}

// GET /api/chat/students?q=
func SearchChatStudentsHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		q := strings.TrimSpace(c.Query("q", ""))
		limit := 30
		query := db.Model(&models.Student{})
		if q != "" {
			like := "%" + q + "%"
			query = query.Where("full_name LIKE ? OR student_code LIKE ? OR email LIKE ?", like, like, like)
		}
		var students []models.Student
		query.Order("full_name asc").Limit(limit).Find(&students)
		out := make([]fiber.Map, 0, len(students))
		for _, s := range students {
			online := internalWs.Hub.IsStudentOnline(s.RkID)
			out = append(out, fiber.Map{
				"studentRkId": s.RkID,
				"fullName":    s.FullName,
				"studentCode": s.StudentCode,
				"email":       s.Email,
				"online":      online,
			})
		}
		return c.JSON(fiber.Map{"data": out})
	}
}

// GET /api/chat/conversations
func ListChatConversationsHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		staffID := middleware.StaffIDFromCtx(c)
		type row struct {
			StudentRkID int64
			LastAt      time.Time
			Unread      int64
		}
		var rows []row
		db.Raw(`
			SELECT student_rk_id AS student_rk_id, MAX(created_at) AS last_at,
			SUM(CASE WHEN sender_role = 'student' AND read_at IS NULL THEN 1 ELSE 0 END) AS unread
			FROM chat_messages WHERE staff_id = ? GROUP BY student_rk_id ORDER BY last_at DESC LIMIT 50
		`, staffID).Scan(&rows)

		out := make([]fiber.Map, 0, len(rows))
		for _, r := range rows {
			var st models.Student
			db.Where("rk_id = ?", r.StudentRkID).First(&st)
			var last models.ChatMessage
			db.Where("staff_id = ? AND student_rk_id = ?", staffID, r.StudentRkID).Order("id desc").First(&last)
			out = append(out, fiber.Map{
				"studentRkId": r.StudentRkID,
				"fullName":    st.FullName,
				"studentCode": st.StudentCode,
				"lastMessage": last.Body,
				"lastAt":      r.LastAt,
				"unread":      r.Unread,
			})
		}
		return c.JSON(fiber.Map{"data": out})
	}
}

// GET /api/chat/messages/:studentRkId
func ListChatMessagesHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		staffID := middleware.StaffIDFromCtx(c)
		studentRkID, _ := strconv.ParseInt(c.Params("studentRkId"), 10, 64)
		if studentRkID <= 0 {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid student id"})
		}
		var staff models.StaffAccount
		_ = db.First(&staff, staffID).Error
		var msgs []models.ChatMessage
		db.Where("staff_id = ? AND student_rk_id = ?", staffID, studentRkID).Order("id asc").Limit(200).Find(&msgs)
		now := time.Now()
		db.Model(&models.ChatMessage{}).
			Where("staff_id = ? AND student_rk_id = ? AND sender_role = 'student' AND read_at IS NULL", staffID, studentRkID).
			Update("read_at", now)
		out := make([]fiber.Map, 0, len(msgs))
		for _, m := range msgs {
			out = append(out, chatMessageDTO(m, staff.FullName))
		}
		return c.JSON(fiber.Map{"data": out})
	}
}

// POST /api/chat/messages
func SendChatMessageHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		staffID := middleware.StaffIDFromCtx(c)
		var req struct {
			StudentRkID int64  `json:"studentRkId"`
			Body        string `json:"body"`
		}
		if err := c.BodyParser(&req); err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid payload"})
		}
		body := strings.TrimSpace(req.Body)
		if body == "" || req.StudentRkID <= 0 {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "studentRkId and body required"})
		}
		var staff models.StaffAccount
		if err := db.First(&staff, staffID).Error; err != nil {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "staff not found"})
		}
		msg := models.ChatMessage{
			StaffID:     staffID,
			StudentRkID: req.StudentRkID,
			SenderRole:  "staff",
			Body:        body,
		}
		if err := db.Create(&msg).Error; err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
		}
		dto := chatMessageDTO(msg, staff.FullName)
		internalWs.Hub.PushChatToStudent(req.StudentRkID, dto)
		return c.JSON(fiber.Map{"ok": true, "data": dto})
	}
}

// GET /api/student/chat/conversations?studentRkId=
func StudentListChatConversationsHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		studentRkID, _ := strconv.ParseInt(c.Query("studentRkId"), 10, 64)
		if studentRkID <= 0 {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "studentRkId required"})
		}
		type row struct {
			StaffID uint
			LastAt  time.Time
			Unread  int64
		}
		var rows []row
		db.Raw(`
			SELECT staff_id AS staff_id, MAX(created_at) AS last_at,
			SUM(CASE WHEN sender_role = 'staff' AND read_at IS NULL THEN 1 ELSE 0 END) AS unread
			FROM chat_messages WHERE student_rk_id = ? GROUP BY staff_id ORDER BY last_at DESC LIMIT 50
		`, studentRkID).Scan(&rows)

		out := make([]fiber.Map, 0, len(rows))
		for _, r := range rows {
			var staff models.StaffAccount
			name, email := "", ""
			if db.First(&staff, r.StaffID).Error == nil {
				name = staff.FullName
				email = staff.Email
				if name == "" {
					name = email
				}
			}
			var last models.ChatMessage
			db.Where("staff_id = ? AND student_rk_id = ?", r.StaffID, studentRkID).Order("id desc").First(&last)
			out = append(out, fiber.Map{
				"staffId":     r.StaffID,
				"staffName":   name,
				"staffEmail":  email,
				"lastMessage": last.Body,
				"lastAt":      r.LastAt,
				"unread":      r.Unread,
			})
		}
		return c.JSON(fiber.Map{"data": out})
	}
}

// GET /api/student/chat/messages?studentRkId=&staffId=
func StudentListChatHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		studentRkID, _ := strconv.ParseInt(c.Query("studentRkId"), 10, 64)
		staffID, _ := strconv.ParseUint(c.Query("staffId", "0"), 10, 64)
		if studentRkID <= 0 {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "studentRkId required"})
		}
		query := db.Where("student_rk_id = ?", studentRkID)
		if staffID > 0 {
			query = query.Where("staff_id = ?", staffID)
		}
		var msgs []models.ChatMessage
		query.Order("id asc").Limit(200).Find(&msgs)
		now := time.Now()
		readQ := db.Model(&models.ChatMessage{}).
			Where("student_rk_id = ? AND sender_role = 'staff' AND read_at IS NULL", studentRkID)
		if staffID > 0 {
			readQ = readQ.Where("staff_id = ?", staffID)
		}
		readQ.Update("read_at", now)

		staffNames := map[uint]string{}
		out := make([]fiber.Map, 0, len(msgs))
		for _, m := range msgs {
			name := staffNames[m.StaffID]
			if name == "" {
				var st models.StaffAccount
				if db.First(&st, m.StaffID).Error == nil {
					name = st.FullName
					if name == "" {
						name = st.Email
					}
				}
				staffNames[m.StaffID] = name
			}
			out = append(out, chatMessageDTO(m, name))
		}
		var lastStaffID uint
		for i := len(msgs) - 1; i >= 0; i-- {
			if msgs[i].SenderRole == "staff" {
				lastStaffID = msgs[i].StaffID
				break
			}
		}
		return c.JSON(fiber.Map{"data": out, "replyStaffId": lastStaffID})
	}
}

// POST /api/student/chat/messages
func StudentSendChatHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		var req struct {
			StudentRkID int64  `json:"studentRkId"`
			StaffID     uint   `json:"staffId"`
			Body        string `json:"body"`
		}
		if err := c.BodyParser(&req); err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid payload"})
		}
		body := strings.TrimSpace(req.Body)
		if body == "" || req.StudentRkID <= 0 {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid payload"})
		}
		staffID := req.StaffID
		if staffID == 0 {
			var last models.ChatMessage
			if err := db.Where("student_rk_id = ? AND sender_role = 'staff'", req.StudentRkID).
				Order("id desc").First(&last).Error; err != nil {
				return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "chưa có giáo viên nhắn tin"})
			}
			staffID = last.StaffID
		}
		msg := models.ChatMessage{
			StaffID:     staffID,
			StudentRkID: req.StudentRkID,
			SenderRole:  "student",
			Body:        body,
		}
		if err := db.Create(&msg).Error; err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
		}
		var staff models.StaffAccount
		_ = db.First(&staff, staffID).Error
		dto := chatMessageDTO(msg, staff.FullName)
		internalWs.Hub.PushChatToStaff(staffID, dto)
		return c.JSON(fiber.Map{"ok": true, "data": dto})
	}
}
