package services

import (
	"context"
	"time"

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

// ExamService handles exam-related operations
type ExamService struct {
	db *pgxpool.Pool
}

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

// CreateExam creates a new exam
func (s *ExamService) CreateExam(ctx context.Context, teacherID string, req *models.CreateExamRequest) (*models.Exam, error) {
	var classID, subjectID *uuid.UUID
	if req.ClassID != "" {
		id, _ := uuid.Parse(req.ClassID)
		classID = &id
	}
	if req.SubjectID != "" {
		id, _ := uuid.Parse(req.SubjectID)
		subjectID = &id
	}

	var examDate *time.Time
	if req.ExamDate != "" {
		t, _ := time.Parse("2006-01-02", req.ExamDate)
		examDate = &t
	}

	var durationMinutes *int
	if req.DurationMinutes > 0 {
		durationMinutes = &req.DurationMinutes
	}

	var maxPoints *float64
	if req.MaxPoints > 0 {
		maxPoints = &req.MaxPoints
	}

	var exam models.Exam
	err := s.db.QueryRow(ctx, `
		INSERT INTO exams (teacher_id, class_id, subject_id, title, exam_type, topic, content, difficulty_level, duration_minutes, max_points, exam_date)
		VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
		RETURNING id, teacher_id, class_id, subject_id, title, exam_type, topic, content, source_file_path, difficulty_level, duration_minutes, max_points, is_template, parent_exam_id, status, exam_date, created_at, updated_at
	`, teacherID, classID, subjectID, req.Title, req.ExamType, req.Topic, req.Content, req.DifficultyLevel, durationMinutes, maxPoints, examDate).Scan(
		&exam.ID, &exam.TeacherID, &exam.ClassID, &exam.SubjectID, &exam.Title, &exam.ExamType, &exam.Topic, &exam.Content, &exam.SourceFilePath, &exam.DifficultyLevel, &exam.DurationMinutes, &exam.MaxPoints, &exam.IsTemplate, &exam.ParentExamID, &exam.Status, &exam.ExamDate, &exam.CreatedAt, &exam.UpdatedAt,
	)
	return &exam, err
}

// GetExams returns all exams for a teacher
func (s *ExamService) GetExams(ctx context.Context, teacherID string) ([]models.Exam, error) {
	rows, err := s.db.Query(ctx, `
		SELECT e.id, e.teacher_id, e.class_id, e.subject_id, e.title, e.exam_type, e.topic, e.content, e.source_file_path, e.difficulty_level, e.duration_minutes, e.max_points, e.is_template, e.parent_exam_id, e.status, e.exam_date, e.created_at, e.updated_at,
		       COALESCE(c.name, '') as class_name,
		       COALESCE(sub.name, '') as subject_name
		FROM exams e
		LEFT JOIN classes c ON e.class_id = c.id
		LEFT JOIN subjects sub ON e.subject_id = sub.id
		WHERE e.teacher_id = $1
		ORDER BY e.created_at DESC
	`, teacherID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var exams []models.Exam
	for rows.Next() {
		var e models.Exam
		if err := rows.Scan(&e.ID, &e.TeacherID, &e.ClassID, &e.SubjectID, &e.Title, &e.ExamType, &e.Topic, &e.Content, &e.SourceFilePath, &e.DifficultyLevel, &e.DurationMinutes, &e.MaxPoints, &e.IsTemplate, &e.ParentExamID, &e.Status, &e.ExamDate, &e.CreatedAt, &e.UpdatedAt, &e.ClassName, &e.SubjectName); err != nil {
			return nil, err
		}
		exams = append(exams, e)
	}
	return exams, nil
}

// GetExam returns a single exam
func (s *ExamService) GetExam(ctx context.Context, examID, teacherID string) (*models.Exam, error) {
	var exam models.Exam
	err := s.db.QueryRow(ctx, `
		SELECT e.id, e.teacher_id, e.class_id, e.subject_id, e.title, e.exam_type, e.topic, e.content, e.source_file_path, e.difficulty_level, e.duration_minutes, e.max_points, e.is_template, e.parent_exam_id, e.status, e.exam_date, e.created_at, e.updated_at,
		       COALESCE(c.name, '') as class_name,
		       COALESCE(sub.name, '') as subject_name
		FROM exams e
		LEFT JOIN classes c ON e.class_id = c.id
		LEFT JOIN subjects sub ON e.subject_id = sub.id
		WHERE e.id = $1 AND e.teacher_id = $2
	`, examID, teacherID).Scan(
		&exam.ID, &exam.TeacherID, &exam.ClassID, &exam.SubjectID, &exam.Title, &exam.ExamType, &exam.Topic, &exam.Content, &exam.SourceFilePath, &exam.DifficultyLevel, &exam.DurationMinutes, &exam.MaxPoints, &exam.IsTemplate, &exam.ParentExamID, &exam.Status, &exam.ExamDate, &exam.CreatedAt, &exam.UpdatedAt, &exam.ClassName, &exam.SubjectName,
	)
	return &exam, err
}

// UpdateExam updates an exam
func (s *ExamService) UpdateExam(ctx context.Context, examID, teacherID string, req *models.CreateExamRequest) (*models.Exam, error) {
	var classID, subjectID *uuid.UUID
	if req.ClassID != "" {
		id, _ := uuid.Parse(req.ClassID)
		classID = &id
	}
	if req.SubjectID != "" {
		id, _ := uuid.Parse(req.SubjectID)
		subjectID = &id
	}

	var examDate *time.Time
	if req.ExamDate != "" {
		t, _ := time.Parse("2006-01-02", req.ExamDate)
		examDate = &t
	}

	var exam models.Exam
	err := s.db.QueryRow(ctx, `
		UPDATE exams SET
			class_id = $3, subject_id = $4, title = $5, exam_type = $6, topic = $7, content = $8,
			difficulty_level = $9, duration_minutes = $10, max_points = $11, exam_date = $12, updated_at = NOW()
		WHERE id = $1 AND teacher_id = $2
		RETURNING id, teacher_id, class_id, subject_id, title, exam_type, topic, content, source_file_path, difficulty_level, duration_minutes, max_points, is_template, parent_exam_id, status, exam_date, created_at, updated_at
	`, examID, teacherID, classID, subjectID, req.Title, req.ExamType, req.Topic, req.Content, req.DifficultyLevel, req.DurationMinutes, req.MaxPoints, examDate).Scan(
		&exam.ID, &exam.TeacherID, &exam.ClassID, &exam.SubjectID, &exam.Title, &exam.ExamType, &exam.Topic, &exam.Content, &exam.SourceFilePath, &exam.DifficultyLevel, &exam.DurationMinutes, &exam.MaxPoints, &exam.IsTemplate, &exam.ParentExamID, &exam.Status, &exam.ExamDate, &exam.CreatedAt, &exam.UpdatedAt,
	)
	return &exam, err
}

// DeleteExam deletes an exam
func (s *ExamService) DeleteExam(ctx context.Context, examID, teacherID string) error {
	_, err := s.db.Exec(ctx, `DELETE FROM exams WHERE id = $1 AND teacher_id = $2`, examID, teacherID)
	return err
}

// CreateExamVariant creates a variant of an existing exam (for Nachschreiber)
func (s *ExamService) CreateExamVariant(ctx context.Context, parentExamID, teacherID string, newContent string, variationType string) (*models.Exam, error) {
	parentID, _ := uuid.Parse(parentExamID)

	// Get parent exam
	parent, err := s.GetExam(ctx, parentExamID, teacherID)
	if err != nil {
		return nil, err
	}

	title := parent.Title + " (Nachschreiber)"
	if variationType == "alternative" {
		title = parent.Title + " (Alternativ)"
	}

	var exam models.Exam
	err = s.db.QueryRow(ctx, `
		INSERT INTO exams (teacher_id, class_id, subject_id, title, exam_type, topic, content, difficulty_level, duration_minutes, max_points, is_template, parent_exam_id, status)
		SELECT teacher_id, class_id, subject_id, $3, exam_type, topic, $4, difficulty_level, duration_minutes, max_points, false, $2, 'draft'
		FROM exams WHERE id = $2 AND teacher_id = $1
		RETURNING id, teacher_id, class_id, subject_id, title, exam_type, topic, content, source_file_path, difficulty_level, duration_minutes, max_points, is_template, parent_exam_id, status, exam_date, created_at, updated_at
	`, teacherID, parentID, title, newContent).Scan(
		&exam.ID, &exam.TeacherID, &exam.ClassID, &exam.SubjectID, &exam.Title, &exam.ExamType, &exam.Topic, &exam.Content, &exam.SourceFilePath, &exam.DifficultyLevel, &exam.DurationMinutes, &exam.MaxPoints, &exam.IsTemplate, &exam.ParentExamID, &exam.Status, &exam.ExamDate, &exam.CreatedAt, &exam.UpdatedAt,
	)
	return &exam, err
}

// SaveExamResult saves or updates a student's exam result
func (s *ExamService) SaveExamResult(ctx context.Context, examID string, req *models.UpdateExamResultRequest) (*models.ExamResult, error) {
	var result models.ExamResult
	err := s.db.QueryRow(ctx, `
		INSERT INTO exam_results (exam_id, student_id, points_achieved, grade, notes, is_absent, needs_rewrite)
		VALUES ($1, $2, $3, $4, $5, $6, $7)
		ON CONFLICT (exam_id, student_id) DO UPDATE SET
			points_achieved = EXCLUDED.points_achieved,
			grade = EXCLUDED.grade,
			notes = EXCLUDED.notes,
			is_absent = EXCLUDED.is_absent,
			needs_rewrite = EXCLUDED.needs_rewrite,
			updated_at = NOW()
		RETURNING id, exam_id, student_id, points_achieved, grade, percentage, notes, is_absent, needs_rewrite, approved_by_teacher, approved_at, created_at, updated_at
	`, examID, req.StudentID, req.PointsAchieved, req.Grade, req.Notes, req.IsAbsent, req.NeedsRewrite).Scan(
		&result.ID, &result.ExamID, &result.StudentID, &result.PointsAchieved, &result.Grade, &result.Percentage, &result.Notes, &result.IsAbsent, &result.NeedsRewrite, &result.ApprovedByTeacher, &result.ApprovedAt, &result.CreatedAt, &result.UpdatedAt,
	)
	return &result, err
}

// GetExamResults returns all results for an exam
func (s *ExamService) GetExamResults(ctx context.Context, examID string) ([]models.ExamResult, error) {
	rows, err := s.db.Query(ctx, `
		SELECT er.id, er.exam_id, er.student_id, er.points_achieved, er.grade, er.percentage, er.notes, er.is_absent, er.needs_rewrite, er.approved_by_teacher, er.approved_at, er.created_at, er.updated_at,
		       CONCAT(s.first_name, ' ', s.last_name) as student_name
		FROM exam_results er
		JOIN students s ON er.student_id = s.id
		WHERE er.exam_id = $1
		ORDER BY s.last_name, s.first_name
	`, examID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var results []models.ExamResult
	for rows.Next() {
		var r models.ExamResult
		if err := rows.Scan(&r.ID, &r.ExamID, &r.StudentID, &r.PointsAchieved, &r.Grade, &r.Percentage, &r.Notes, &r.IsAbsent, &r.NeedsRewrite, &r.ApprovedByTeacher, &r.ApprovedAt, &r.CreatedAt, &r.UpdatedAt, &r.StudentName); err != nil {
			return nil, err
		}
		results = append(results, r)
	}
	return results, nil
}

// ApproveExamResult approves a result for transfer to grade overview
func (s *ExamService) ApproveExamResult(ctx context.Context, examID, studentID string) error {
	_, err := s.db.Exec(ctx, `
		UPDATE exam_results SET approved_by_teacher = true, approved_at = NOW(), updated_at = NOW()
		WHERE exam_id = $1 AND student_id = $2
	`, examID, studentID)
	return err
}

// GetStudentsNeedingRewrite returns students who need to rewrite an exam
func (s *ExamService) GetStudentsNeedingRewrite(ctx context.Context, examID string) ([]models.Student, error) {
	rows, err := s.db.Query(ctx, `
		SELECT s.id, s.class_id, s.first_name, s.last_name, s.date_of_birth, s.student_number, s.created_at
		FROM students s
		JOIN exam_results er ON s.id = er.student_id
		WHERE er.exam_id = $1 AND (er.needs_rewrite = true OR er.is_absent = true)
		ORDER BY s.last_name, s.first_name
	`, examID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var students []models.Student
	for rows.Next() {
		var st models.Student
		if err := rows.Scan(&st.ID, &st.ClassID, &st.FirstName, &st.LastName, &st.BirthDate, &st.StudentNumber, &st.CreatedAt); err != nil {
			return nil, err
		}
		students = append(students, st)
	}
	return students, nil
}
