package handlers

import (
	"net/http"
	"time"

	"github.com/breakpilot/consent-service/internal/models"
	"github.com/breakpilot/consent-service/internal/services"

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

// SchoolHandlers contains all school-related HTTP handlers
type SchoolHandlers struct {
	schoolService     *services.SchoolService
	attendanceService *services.AttendanceService
	gradeService      *services.GradeService
}

// NewSchoolHandlers creates new school handlers
func NewSchoolHandlers(schoolService *services.SchoolService, attendanceService *services.AttendanceService, gradeService *services.GradeService) *SchoolHandlers {
	return &SchoolHandlers{
		schoolService:     schoolService,
		attendanceService: attendanceService,
		gradeService:      gradeService,
	}
}

// ========================================
// School Handlers
// ========================================

// CreateSchool creates a new school
// POST /api/v1/schools
func (h *SchoolHandlers) CreateSchool(c *gin.Context) {
	var req models.CreateSchoolRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	school, err := h.schoolService.CreateSchool(c.Request.Context(), req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, school)
}

// GetSchool retrieves a school by ID
// GET /api/v1/schools/:id
func (h *SchoolHandlers) GetSchool(c *gin.Context) {
	idStr := c.Param("id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid school ID"})
		return
	}

	school, err := h.schoolService.GetSchool(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, school)
}

// ListSchools lists all schools
// GET /api/v1/schools
func (h *SchoolHandlers) ListSchools(c *gin.Context) {
	schools, err := h.schoolService.ListSchools(c.Request.Context())
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, schools)
}

// ========================================
// School Year Handlers
// ========================================

// CreateSchoolYear creates a new school year
// POST /api/v1/schools/:id/years
func (h *SchoolHandlers) CreateSchoolYear(c *gin.Context) {
	schoolIDStr := c.Param("id")
	schoolID, err := uuid.Parse(schoolIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid school ID"})
		return
	}

	var req struct {
		Name      string `json:"name" binding:"required"`
		StartDate string `json:"start_date" binding:"required"`
		EndDate   string `json:"end_date" binding:"required"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	startDate, err := time.Parse("2006-01-02", req.StartDate)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid start date format"})
		return
	}

	endDate, err := time.Parse("2006-01-02", req.EndDate)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid end date format"})
		return
	}

	schoolYear, err := h.schoolService.CreateSchoolYear(c.Request.Context(), schoolID, req.Name, startDate, endDate)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, schoolYear)
}

// SetCurrentSchoolYear sets a school year as current
// PUT /api/v1/schools/:id/years/:yearId/current
func (h *SchoolHandlers) SetCurrentSchoolYear(c *gin.Context) {
	schoolIDStr := c.Param("id")
	schoolID, err := uuid.Parse(schoolIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid school ID"})
		return
	}

	yearIDStr := c.Param("yearId")
	yearID, err := uuid.Parse(yearIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid school year ID"})
		return
	}

	if err := h.schoolService.SetCurrentSchoolYear(c.Request.Context(), schoolID, yearID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "school year set as current"})
}

// ========================================
// Class Handlers
// ========================================

// CreateClass creates a new class
// POST /api/v1/schools/:id/classes
func (h *SchoolHandlers) CreateClass(c *gin.Context) {
	schoolIDStr := c.Param("id")
	schoolID, err := uuid.Parse(schoolIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid school ID"})
		return
	}

	var req models.CreateClassRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	class, err := h.schoolService.CreateClass(c.Request.Context(), schoolID, req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, class)
}

// GetClass retrieves a class by ID
// GET /api/v1/classes/:id
func (h *SchoolHandlers) GetClass(c *gin.Context) {
	idStr := c.Param("id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid class ID"})
		return
	}

	class, err := h.schoolService.GetClass(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, class)
}

// ListClasses lists all classes for a school in a school year
// GET /api/v1/schools/:id/classes?school_year_id=...
func (h *SchoolHandlers) ListClasses(c *gin.Context) {
	schoolIDStr := c.Param("id")
	schoolID, err := uuid.Parse(schoolIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid school ID"})
		return
	}

	schoolYearIDStr := c.Query("school_year_id")
	if schoolYearIDStr == "" {
		// Get current school year
		schoolYear, err := h.schoolService.GetCurrentSchoolYear(c.Request.Context(), schoolID)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "no current school year set"})
			return
		}
		schoolYearIDStr = schoolYear.ID.String()
	}

	schoolYearID, err := uuid.Parse(schoolYearIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid school year ID"})
		return
	}

	classes, err := h.schoolService.ListClasses(c.Request.Context(), schoolID, schoolYearID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, classes)
}

// ========================================
// Student Handlers
// ========================================

// CreateStudent creates a new student
// POST /api/v1/schools/:id/students
func (h *SchoolHandlers) CreateStudent(c *gin.Context) {
	schoolIDStr := c.Param("id")
	schoolID, err := uuid.Parse(schoolIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid school ID"})
		return
	}

	var req models.CreateStudentRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	student, err := h.schoolService.CreateStudent(c.Request.Context(), schoolID, req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, student)
}

// GetStudent retrieves a student by ID
// GET /api/v1/students/:id
func (h *SchoolHandlers) GetStudent(c *gin.Context) {
	idStr := c.Param("id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid student ID"})
		return
	}

	student, err := h.schoolService.GetStudent(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, student)
}

// ListStudentsByClass lists all students in a class
// GET /api/v1/classes/:id/students
func (h *SchoolHandlers) ListStudentsByClass(c *gin.Context) {
	classIDStr := c.Param("id")
	classID, err := uuid.Parse(classIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid class ID"})
		return
	}

	students, err := h.schoolService.ListStudentsByClass(c.Request.Context(), classID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, students)
}

// ========================================
// Subject Handlers
// ========================================

// CreateSubject creates a new subject
// POST /api/v1/schools/:id/subjects
func (h *SchoolHandlers) CreateSubject(c *gin.Context) {
	schoolIDStr := c.Param("id")
	schoolID, err := uuid.Parse(schoolIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid school ID"})
		return
	}

	var req struct {
		Name      string  `json:"name" binding:"required"`
		ShortName string  `json:"short_name" binding:"required"`
		Color     *string `json:"color"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	subject, err := h.schoolService.CreateSubject(c.Request.Context(), schoolID, req.Name, req.ShortName, req.Color)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, subject)
}

// ListSubjects lists all subjects for a school
// GET /api/v1/schools/:id/subjects
func (h *SchoolHandlers) ListSubjects(c *gin.Context) {
	schoolIDStr := c.Param("id")
	schoolID, err := uuid.Parse(schoolIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid school ID"})
		return
	}

	subjects, err := h.schoolService.ListSubjects(c.Request.Context(), schoolID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, subjects)
}

// ========================================
// Register Routes
// ========================================

// RegisterRoutes registers all school-related routes
func (h *SchoolHandlers) RegisterRoutes(r *gin.RouterGroup, authMiddleware gin.HandlerFunc) {
	// Public routes (for onboarding)
	r.GET("/onboarding/validate", h.ValidateOnboardingToken)

	// Protected routes
	protected := r.Group("")
	protected.Use(authMiddleware)

	// Schools
	protected.POST("/schools", h.CreateSchool)
	protected.GET("/schools", h.ListSchools)
	protected.GET("/schools/:id", h.GetSchool)
	protected.POST("/schools/:id/years", h.CreateSchoolYear)
	protected.PUT("/schools/:id/years/:yearId/current", h.SetCurrentSchoolYear)
	protected.POST("/schools/:id/classes", h.CreateClass)
	protected.GET("/schools/:id/classes", h.ListClasses)
	protected.POST("/schools/:id/students", h.CreateStudent)
	protected.POST("/schools/:id/subjects", h.CreateSubject)
	protected.GET("/schools/:id/subjects", h.ListSubjects)

	// Classes
	protected.GET("/classes/:id", h.GetClass)
	protected.GET("/classes/:id/students", h.ListStudentsByClass)
	protected.GET("/classes/:id/attendance", h.GetClassAttendance)
	protected.POST("/classes/:id/attendance", h.RecordBulkAttendance)
	protected.GET("/classes/:id/absence/pending", h.GetPendingAbsenceReports)
	protected.GET("/classes/:id/grades/:subjectId", h.GetClassGrades)
	protected.GET("/classes/:id/grades/:subjectId/stats", h.GetGradeStatistics)

	// Students
	protected.GET("/students/:id", h.GetStudent)
	protected.GET("/students/:id/attendance", h.GetStudentAttendance)
	protected.GET("/students/:id/grades", h.GetStudentGrades)

	// Attendance & Absence
	protected.POST("/attendance", h.RecordAttendance)
	protected.POST("/absence/report", h.ReportAbsence)
	protected.PUT("/absence/:id/confirm", h.ConfirmAbsence)

	// Grades
	protected.POST("/grades", h.CreateGrade)

	// Onboarding
	protected.POST("/onboarding/tokens", h.GenerateOnboardingToken)
	protected.POST("/onboarding/redeem", h.RedeemOnboardingToken)
}
