package services

import (
	"context"
	"time"

	"github.com/breakpilot/school-service/internal/models"
	"github.com/google/uuid"
	"github.com/jackc/pgx/v5/pgxpool"
)

// GradebookService handles gradebook-related operations
type GradebookService struct {
	db *pgxpool.Pool
}

// NewGradebookService creates a new GradebookService
func NewGradebookService(db *pgxpool.Pool) *GradebookService {
	return &GradebookService{db: db}
}

// Attendance Operations

// CreateAttendance creates or updates an attendance record
func (s *GradebookService) CreateAttendance(ctx context.Context, req *models.CreateAttendanceRequest) (*models.Attendance, error) {
	date, _ := time.Parse("2006-01-02", req.Date)
	periods := req.Periods
	if periods == 0 {
		periods = 1
	}

	var attendance models.Attendance
	err := s.db.QueryRow(ctx, `
		INSERT INTO attendance (student_id, date, status, periods, reason)
		VALUES ($1, $2, $3, $4, $5)
		ON CONFLICT (student_id, date) DO UPDATE SET
			status = EXCLUDED.status,
			periods = EXCLUDED.periods,
			reason = EXCLUDED.reason
		RETURNING id, student_id, date, status, periods, reason, created_at
	`, req.StudentID, date, req.Status, periods, req.Reason).Scan(
		&attendance.ID, &attendance.StudentID, &attendance.Date, &attendance.Status, &attendance.Periods, &attendance.Reason, &attendance.CreatedAt,
	)
	return &attendance, err
}

// GetClassAttendance returns attendance records for a class
func (s *GradebookService) GetClassAttendance(ctx context.Context, classID string, startDate, endDate *time.Time) ([]models.Attendance, error) {
	query := `
		SELECT a.id, a.student_id, a.date, a.status, a.periods, a.reason, a.created_at,
		       CONCAT(st.first_name, ' ', st.last_name) as student_name
		FROM attendance a
		JOIN students st ON a.student_id = st.id
		WHERE st.class_id = $1
	`
	args := []interface{}{classID}

	if startDate != nil {
		query += ` AND a.date >= $2`
		args = append(args, startDate)
	}
	if endDate != nil {
		query += ` AND a.date <= $` + string(rune('0'+len(args)+1))
		args = append(args, endDate)
	}

	query += ` ORDER BY a.date DESC, st.last_name, st.first_name`

	rows, err := s.db.Query(ctx, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var records []models.Attendance
	for rows.Next() {
		var a models.Attendance
		if err := rows.Scan(&a.ID, &a.StudentID, &a.Date, &a.Status, &a.Periods, &a.Reason, &a.CreatedAt, &a.StudentName); err != nil {
			return nil, err
		}
		records = append(records, a)
	}
	return records, nil
}

// GetStudentAttendance returns attendance records for a student
func (s *GradebookService) GetStudentAttendance(ctx context.Context, studentID string) ([]models.Attendance, error) {
	rows, err := s.db.Query(ctx, `
		SELECT id, student_id, date, status, periods, reason, created_at
		FROM attendance
		WHERE student_id = $1
		ORDER BY date DESC
	`, studentID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var records []models.Attendance
	for rows.Next() {
		var a models.Attendance
		if err := rows.Scan(&a.ID, &a.StudentID, &a.Date, &a.Status, &a.Periods, &a.Reason, &a.CreatedAt); err != nil {
			return nil, err
		}
		records = append(records, a)
	}
	return records, nil
}

// GetAttendanceSummary returns absence counts for a student
func (s *GradebookService) GetAttendanceSummary(ctx context.Context, studentID string, schoolYearID string) (int, int, error) {
	var excused, unexcused int
	err := s.db.QueryRow(ctx, `
		SELECT
			COALESCE(SUM(CASE WHEN status = 'absent_excused' THEN periods ELSE 0 END), 0) as excused,
			COALESCE(SUM(CASE WHEN status = 'absent_unexcused' THEN periods ELSE 0 END), 0) as unexcused
		FROM attendance a
		JOIN students st ON a.student_id = st.id
		JOIN classes c ON st.class_id = c.id
		WHERE a.student_id = $1 AND c.school_year_id = $2
	`, studentID, schoolYearID).Scan(&excused, &unexcused)
	return excused, unexcused, err
}

// DeleteAttendance deletes an attendance record
func (s *GradebookService) DeleteAttendance(ctx context.Context, attendanceID string) error {
	_, err := s.db.Exec(ctx, `DELETE FROM attendance WHERE id = $1`, attendanceID)
	return err
}

// Gradebook Entry Operations

// CreateGradebookEntry creates a new gradebook entry
func (s *GradebookService) CreateGradebookEntry(ctx context.Context, req *models.CreateGradebookEntryRequest) (*models.GradebookEntry, error) {
	date, _ := time.Parse("2006-01-02", req.Date)

	var studentID *uuid.UUID
	if req.StudentID != "" {
		id, _ := uuid.Parse(req.StudentID)
		studentID = &id
	}

	var entry models.GradebookEntry
	err := s.db.QueryRow(ctx, `
		INSERT INTO gradebook_entries (class_id, student_id, date, entry_type, content, is_visible_to_parents)
		VALUES ($1, $2, $3, $4, $5, $6)
		RETURNING id, class_id, student_id, date, entry_type, content, is_visible_to_parents, created_at
	`, req.ClassID, studentID, date, req.EntryType, req.Content, req.IsVisibleToParents).Scan(
		&entry.ID, &entry.ClassID, &entry.StudentID, &entry.Date, &entry.EntryType, &entry.Content, &entry.IsVisibleToParents, &entry.CreatedAt,
	)
	return &entry, err
}

// GetGradebookEntries returns entries for a class
func (s *GradebookService) GetGradebookEntries(ctx context.Context, classID string, entryType *string, startDate, endDate *time.Time) ([]models.GradebookEntry, error) {
	query := `
		SELECT ge.id, ge.class_id, ge.student_id, ge.date, ge.entry_type, ge.content, ge.is_visible_to_parents, ge.created_at,
		       COALESCE(CONCAT(st.first_name, ' ', st.last_name), '') as student_name
		FROM gradebook_entries ge
		LEFT JOIN students st ON ge.student_id = st.id
		WHERE ge.class_id = $1
	`
	args := []interface{}{classID}
	argCount := 1

	if entryType != nil {
		argCount++
		query += ` AND ge.entry_type = $` + string(rune('0'+argCount))
		args = append(args, *entryType)
	}
	if startDate != nil {
		argCount++
		query += ` AND ge.date >= $` + string(rune('0'+argCount))
		args = append(args, startDate)
	}
	if endDate != nil {
		argCount++
		query += ` AND ge.date <= $` + string(rune('0'+argCount))
		args = append(args, endDate)
	}

	query += ` ORDER BY ge.date DESC, ge.created_at DESC`

	rows, err := s.db.Query(ctx, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var entries []models.GradebookEntry
	for rows.Next() {
		var e models.GradebookEntry
		if err := rows.Scan(&e.ID, &e.ClassID, &e.StudentID, &e.Date, &e.EntryType, &e.Content, &e.IsVisibleToParents, &e.CreatedAt, &e.StudentName); err != nil {
			return nil, err
		}
		entries = append(entries, e)
	}
	return entries, nil
}

// GetStudentEntries returns gradebook entries for a specific student
func (s *GradebookService) GetStudentEntries(ctx context.Context, studentID string) ([]models.GradebookEntry, error) {
	rows, err := s.db.Query(ctx, `
		SELECT ge.id, ge.class_id, ge.student_id, ge.date, ge.entry_type, ge.content, ge.is_visible_to_parents, ge.created_at,
		       CONCAT(st.first_name, ' ', st.last_name) as student_name
		FROM gradebook_entries ge
		JOIN students st ON ge.student_id = st.id
		WHERE ge.student_id = $1
		ORDER BY ge.date DESC
	`, studentID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var entries []models.GradebookEntry
	for rows.Next() {
		var e models.GradebookEntry
		if err := rows.Scan(&e.ID, &e.ClassID, &e.StudentID, &e.Date, &e.EntryType, &e.Content, &e.IsVisibleToParents, &e.CreatedAt, &e.StudentName); err != nil {
			return nil, err
		}
		entries = append(entries, e)
	}
	return entries, nil
}

// DeleteGradebookEntry deletes a gradebook entry
func (s *GradebookService) DeleteGradebookEntry(ctx context.Context, entryID string) error {
	_, err := s.db.Exec(ctx, `DELETE FROM gradebook_entries WHERE id = $1`, entryID)
	return err
}

// BulkCreateAttendance creates attendance records for multiple students at once
func (s *GradebookService) BulkCreateAttendance(ctx context.Context, classID string, date string, records []struct {
	StudentID string
	Status    models.AttendanceStatus
	Periods   int
	Reason    string
}) error {
	parsedDate, _ := time.Parse("2006-01-02", date)

	for _, r := range records {
		periods := r.Periods
		if periods == 0 {
			periods = 1
		}

		_, err := s.db.Exec(ctx, `
			INSERT INTO attendance (student_id, date, status, periods, reason)
			VALUES ($1, $2, $3, $4, $5)
			ON CONFLICT (student_id, date) DO UPDATE SET
				status = EXCLUDED.status,
				periods = EXCLUDED.periods,
				reason = EXCLUDED.reason
		`, r.StudentID, parsedDate, r.Status, periods, r.Reason)
		if err != nil {
			return err
		}
	}
	return nil
}
