package handlers

import (
	"errors"
	"strconv"
	"time"

	"server/internal/middleware"
	"server/internal/models"

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

// GET /classes/:rkId/seating-layout
func GetClassSeatingLayoutHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		classRkID, err := strconv.ParseInt(c.Params("rkId"), 10, 64)
		if err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid class ID"})
		}
		var layout models.ClassSeatingLayout
		if err := db.Where("class_rk_id = ?", classRkID).First(&layout).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return c.JSON(fiber.Map{"layoutJson": nil})
			}
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
		}
		return c.JSON(fiber.Map{"layoutJson": layout.LayoutJSON})
	}
}

// PUT /classes/:rkId/seating-layout
func PutClassSeatingLayoutHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		classRkID, err := strconv.ParseInt(c.Params("rkId"), 10, 64)
		if err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid class ID"})
		}
		var body struct {
			LayoutJSON string `json:"layoutJson"`
		}
		if err := c.BodyParser(&body); err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid body"})
		}
		if body.LayoutJSON == "" {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "layoutJson is required"})
		}
		layout := models.ClassSeatingLayout{
			ClassRkID:  classRkID,
			LayoutJSON: body.LayoutJSON,
			UpdatedAt:  time.Now(),
		}
		result := db.Clauses(clause.OnConflict{
			Columns:   []clause.Column{{Name: "class_rk_id"}},
			DoUpdates: clause.AssignmentColumns([]string{"layout_json", "updated_at"}),
		}).Create(&layout)
		if result.Error != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()})
		}
		return c.JSON(fiber.Map{"ok": true})
	}
}

// DELETE /classes/:rkId/seating-layout
func DeleteClassSeatingLayoutHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		classRkID, err := strconv.ParseInt(c.Params("rkId"), 10, 64)
		if err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid class ID"})
		}
		db.Where("class_rk_id = ?", classRkID).Delete(&models.ClassSeatingLayout{})
		return c.JSON(fiber.Map{"ok": true})
	}
}

// GET /exam-rooms/:id/seating-layout
func GetExamSeatingLayoutHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		examID, err := strconv.ParseUint(c.Params("id"), 10, 64)
		if err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid exam room ID"})
		}
		var layout models.ExamSeatingLayout
		if err := db.Where("exam_room_id = ?", examID).First(&layout).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return c.JSON(fiber.Map{"layoutJson": nil})
			}
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
		}
		return c.JSON(fiber.Map{"layoutJson": layout.LayoutJSON})
	}
}

// PUT /exam-rooms/:id/seating-layout
func PutExamSeatingLayoutHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		examID, err := strconv.ParseUint(c.Params("id"), 10, 64)
		if err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid exam room ID"})
		}
		var body struct {
			LayoutJSON string `json:"layoutJson"`
		}
		if err := c.BodyParser(&body); err != nil || body.LayoutJSON == "" {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "layoutJson is required"})
		}
		layout := models.ExamSeatingLayout{
			ExamRoomID: uint(examID),
			LayoutJSON: body.LayoutJSON,
			UpdatedAt:  time.Now(),
		}
		result := db.Clauses(clause.OnConflict{
			Columns:   []clause.Column{{Name: "exam_room_id"}},
			DoUpdates: clause.AssignmentColumns([]string{"layout_json", "updated_at"}),
		}).Create(&layout)
		if result.Error != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()})
		}
		return c.JSON(fiber.Map{"ok": true})
	}
}

// DELETE /exam-rooms/:id/seating-layout
func DeleteExamSeatingLayoutHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		examID, err := strconv.ParseUint(c.Params("id"), 10, 64)
		if err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid exam room ID"})
		}
		db.Where("exam_room_id = ?", examID).Delete(&models.ExamSeatingLayout{})
		return c.JSON(fiber.Map{"ok": true})
	}
}

// GET /staff/my-classes
func GetMyClassesHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		staffID := middleware.StaffIDFromCtx(c)
		type classRow struct {
			RkID      int64  `json:"rkId"`
			Name      string `json:"name"`
			ClassCode string `json:"classCode"`
		}
		var rows []classRow
		db.Table("staff_my_classes").
			Select("classes.rk_id, classes.name, classes.class_code").
			Joins("JOIN classes ON classes.rk_id = staff_my_classes.class_rk_id").
			Where("staff_my_classes.staff_id = ?", staffID).
			Scan(&rows)
		if rows == nil {
			rows = []classRow{}
		}
		return c.JSON(fiber.Map{"data": rows})
	}
}

// POST /staff/my-classes/:classRkId
func AddMyClassHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		staffID := middleware.StaffIDFromCtx(c)
		classRkID, err := strconv.ParseInt(c.Params("classRkId"), 10, 64)
		if err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid class ID"})
		}
		rec := models.StaffMyClass{StaffID: staffID, ClassRkID: classRkID}
		db.Clauses(clause.OnConflict{DoNothing: true}).Create(&rec)
		return c.JSON(fiber.Map{"ok": true})
	}
}

// DELETE /staff/my-classes/:classRkId
func RemoveMyClassHandler(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		staffID := middleware.StaffIDFromCtx(c)
		classRkID, err := strconv.ParseInt(c.Params("classRkId"), 10, 64)
		if err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid class ID"})
		}
		db.Where("staff_id = ? AND class_rk_id = ?", staffID, classRkID).Delete(&models.StaffMyClass{})
		return c.JSON(fiber.Map{"ok": true})
	}
}
