package handlers

import (
	"strconv"
	"strings"
	"time"

	internalDb "server/internal/db"
	"server/internal/models"
	"server/internal/qldt"
	"server/internal/syncjobs"

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

// Helper convert string to int
func atoiDefault(s string, def int) int {
	if s == "" {
		return def
	}
	v, err := strconv.Atoi(s)
	if err != nil {
		return def
	}
	return v
}

// Handler bắt đầu đồng bộ lớp học
func StartClassesSyncHandler(db *gorm.DB, client *qldt.Client, job *syncjobs.ClassesSyncJob, token string) fiber.Handler {
	return func(c *fiber.Ctx) error {
		if token == "" {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Missing token QLDT_TOKEN"})
		}
		started := job.Start(db, client, token)
		if !started {
			return c.Status(fiber.StatusConflict).JSON(fiber.Map{"error": "Sync classes is already running"})
		}
		return c.JSON(fiber.Map{"ok": true, "message": "Classes sync job started"})
	}
}

// Handler lấy status đồng bộ lớp học
func GetClassesSyncStatusHandler(job *syncjobs.ClassesSyncJob) fiber.Handler {
	return func(c *fiber.Ctx) error {
		return c.JSON(job.Status())
	}
}

// Handler bắt đầu đồng bộ toàn bộ sinh viên
func StartStudentsSyncHandler(db *gorm.DB, client *qldt.Client, job *syncjobs.StudentsSyncJob, token string) fiber.Handler {
	return func(c *fiber.Ctx) error {
		if token == "" {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Missing token QLDT_TOKEN"})
		}
		started := job.Start(db, client, token)
		if !started {
			return c.Status(fiber.StatusConflict).JSON(fiber.Map{"error": "Sync students is already running"})
		}
		return c.JSON(fiber.Map{"ok": true, "message": "Students sync job started"})
	}
}

// Handler lấy status đồng bộ sinh viên
func GetStudentsSyncStatusHandler(job *syncjobs.StudentsSyncJob) fiber.Handler {
	return func(c *fiber.Ctx) error {
		return c.JSON(job.Status())
	}
}

type courseRefItem struct {
	CourseRkID int64  `json:"courseRkId"`
	CourseName string `json:"courseName"`
}

type classItem struct {
	RkID           int64            `json:"rkId"`
	Name           string           `json:"name"`
	ClassCode      string           `json:"classCode"`
	Type           string           `json:"type"`
	StudentCount   int              `json:"studentCount"`
	SpecializeRkID *int64           `json:"specializeRkId"`
	SpecializeName *string          `json:"specializeName"`
	SystemRkID     *int64           `json:"systemRkId"`
	SystemCode     *string          `json:"systemCode"`
	SystemName     *string          `json:"systemName"`
	IsStudying     bool             `json:"isStudying"`
	Courses        []courseRefItem  `json:"courses"`
}

// Handler danh sách lớp học
func ListClassesHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		page := atoiDefault(c.Query("page"), 1)
		pageSize := atoiDefault(c.Query("pageSize"), 20)
		if page < 1 {
			page = 1
		}
		if pageSize < 1 {
			pageSize = 20
		}

		queryDB := db.Model(&models.Class{})

		if sys := c.Query("systemRkId"); sys != "" {
			if id, err := strconv.ParseInt(sys, 10, 64); err == nil {
				queryDB = queryDB.Where("system_rk_id = ?", id)
			}
		}

		if s := strings.TrimSpace(c.Query("q")); s != "" {
			pat := "%" + s + "%"
			queryDB = queryDB.Where("(name LIKE ? OR class_code LIKE ? OR type LIKE ? OR specialize_name LIKE ? OR system_name LIKE ?)", pat, pat, pat, pat, pat)
		}

		studyingOnly := c.Query("studyingOnly")
		if studyingOnly == "1" || studyingOnly == "true" {
			queryDB = queryDB.Where("is_studying = ?", true)
		}

		var total int64
		if err := queryDB.Count(&total).Error; err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to count classes: " + err.Error()})
		}

		var rows []models.Class
		if err := queryDB.Order("rk_id desc").Offset((page - 1) * pageSize).Limit(pageSize).Find(&rows).Error; err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to query classes: " + err.Error()})
		}

		out := make([]classItem, 0, len(rows))
		for _, r := range rows {
			out = append(out, classItem{
				RkID:           r.RkID,
				Name:           r.Name,
				ClassCode:      r.ClassCode,
				Type:           r.Type,
				StudentCount:   r.StudentCount,
				SpecializeRkID: r.SpecializeRkID,
				SpecializeName: r.SpecializeName,
				SystemRkID:     r.SystemRkID,
				SystemCode:     r.SystemCode,
				SystemName:     r.SystemName,
				IsStudying:     r.IsStudying,
				Courses:        []courseRefItem{},
			})
		}

		return c.JSON(fiber.Map{
			"data":     out,
			"total":    total,
			"page":     page,
			"pageSize": pageSize,
		})
	}
}

// GET /api/classes/:rkId - Lấy thông tin chi tiết một lớp học
func GetClassHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		rkID, err := strconv.ParseInt(c.Params("rkId"), 10, 64)
		if err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid class rkId"})
		}

		var row models.Class
		if err := db.Where("rk_id = ?", rkID).First(&row).Error; err != nil {
			return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Class not found"})
		}

		coursesOut := []courseRefItem{}
		if cached, err := internalDb.ListClassCourses(db, rkID); err == nil {
			for _, co := range cached {
				coursesOut = append(coursesOut, courseRefItem{
					CourseRkID: co.ID,
					CourseName: co.Name,
				})
			}
		}

		out := classItem{
			RkID:           row.RkID,
			Name:           row.Name,
			ClassCode:      row.ClassCode,
			Type:           row.Type,
			StudentCount:   row.StudentCount,
			SpecializeRkID: row.SpecializeRkID,
			SpecializeName: row.SpecializeName,
			SystemRkID:     row.SystemRkID,
			SystemCode:     row.SystemCode,
			SystemName:     row.SystemName,
			IsStudying:     row.IsStudying,
			Courses:        coursesOut,
		}

		return c.JSON(out)
	}
}

// Handler cập nhật trạng thái đang học (isStudying) của lớp học
func PatchClassStudyingHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		rkIDStr := c.Params("rkId")
		rkID, err := strconv.ParseInt(rkIDStr, 10, 64)
		if err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid class rkId"})
		}

		var body struct {
			IsStudying bool `json:"isStudying"`
		}
		if err := c.BodyParser(&body); err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid body payload"})
		}

		var cl models.Class
		if err := db.Where("rk_id = ?", rkID).First(&cl).Error; err != nil {
			if gorm.ErrRecordNotFound == err {
				return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Class not found"})
			}
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
		}

		if body.IsStudying {
			// 1. Kiểm tra xem lớp đã cấu hình app được phép chưa
			var appConfig models.ClassAllowedApp
			errApp := db.Where("class_rk_id = ?", rkID).First(&appConfig).Error
			if errApp == gorm.ErrRecordNotFound || strings.TrimSpace(appConfig.Keywords) == "" {
				return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Không thể kích hoạt trạng thái đang học: Lớp chưa được cấu hình danh sách ứng dụng cho phép."})
			}

			// 2. Kiểm tra xem có trong giờ học không
			var schedules []models.ClassSchedule
			errSched := db.Where("class_rk_id = ?", rkID).Find(&schedules).Error
			inSchedule := false
			if errSched == nil && len(schedules) > 0 {
				now := time.Now()
				goWeekday := now.Weekday()
				var scheduleDay int
				if goWeekday == time.Sunday {
					scheduleDay = 6
				} else {
					scheduleDay = int(goWeekday) - 1
				}
				currMin := now.Hour()*60 + now.Minute()
				for _, s := range schedules {
					if s.DayOfWeek == scheduleDay {
						startMin := parseTimeMinutes(s.StartTime)
						endMin := parseTimeMinutes(s.EndTime)
						if currMin >= startMin && currMin <= endMin {
							inSchedule = true
							break
						}
					}
				}
			}

			if !inSchedule {
				return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Không thể kích hoạt trạng thái đang học: Hiện tại ngoài giờ học theo lịch đã cài đặt."})
			}
		}

		cl.IsStudying = body.IsStudying
		if err := db.Save(&cl).Error; err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to update class: " + err.Error()})
		}

		return c.JSON(fiber.Map{"ok": true, "data": cl})
	}
}

// Handler lấy danh sách sinh viên của lớp học cụ thể
func ListClassStudentsHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		rkIDStr := c.Params("rkId")
		classRkID, err := strconv.ParseInt(rkIDStr, 10, 64)
		if err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid class rkId"})
		}

		// Tìm danh sách student_rk_id thuộc lớp
		var links []models.ClassStudent
		if err := db.Where("class_rk_id = ?", classRkID).Find(&links).Error; err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to query class students: " + err.Error()})
		}

		var studentRkIDs []int64
		for _, link := range links {
			studentRkIDs = append(studentRkIDs, link.StudentRkID)
		}

		students := []models.Student{}
		if len(studentRkIDs) > 0 {
			if err := db.Where("rk_id IN ?", studentRkIDs).Order("full_name asc").Find(&students).Error; err != nil {
				return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to load students info: " + err.Error()})
			}
		}

		type studentRow struct {
			ID          uint    `json:"id"`
			RkID        int64   `json:"rkId"`
			StudentCode string  `json:"studentCode"`
			FullName    string  `json:"fullName"`
			Phone       *string `json:"phone"`
			Email       string  `json:"email"`
			Avatar      string  `json:"avatar"`
		}
		out := make([]studentRow, 0, len(students))
		for _, st := range students {
			row := studentRow{
				ID:          st.ID,
				RkID:        st.RkID,
				StudentCode: st.StudentCode,
				FullName:    st.FullName,
				Phone:       st.Phone,
				Email:       st.Email,
			}
			if st.Avatar != nil {
				row.Avatar = *st.Avatar
			}
			out = append(out, row)
		}

		return c.JSON(fiber.Map{"data": out, "total": len(out)})
	}
}

// Handler danh sách toàn bộ sinh viên (đã sync)
func ListAllStudentsHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		page := atoiDefault(c.Query("page"), 1)
		pageSize := atoiDefault(c.Query("pageSize"), 20)
		if page < 1 {
			page = 1
		}
		if pageSize < 1 {
			pageSize = 20
		}

		queryDB := db.Model(&models.Student{})

		if s := strings.TrimSpace(c.Query("q")); s != "" {
			pat := "%" + s + "%"
			queryDB = queryDB.Where("(full_name LIKE ? OR student_code LIKE ? OR email LIKE ? OR phone LIKE ?)", pat, pat, pat, pat)
		}

		var total int64
		if err := queryDB.Count(&total).Error; err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to count students: " + err.Error()})
		}

		var students []models.Student
		if err := queryDB.Order("id desc").Offset((page - 1) * pageSize).Limit(pageSize).Find(&students).Error; err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to load students: " + err.Error()})
		}

		return c.JSON(fiber.Map{
			"data":     students,
			"total":    total,
			"page":     page,
			"pageSize": pageSize,
		})
	}
}

// Handler lấy stats nhanh hiển thị ở Dashboard
func GetStatsHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		var totalClasses int64
		var activeClasses int64
		var totalStudents int64

		db.Model(&models.Class{}).Count(&totalClasses)
		db.Model(&models.Class{}).Where("is_studying = ?", true).Count(&activeClasses)
		db.Model(&models.Student{}).Count(&totalStudents)

		return c.JSON(fiber.Map{
			"totalClasses":  totalClasses,
			"activeClasses": activeClasses,
			"totalStudents": totalStudents,
		})
	}
}
