package services import ( "context" "time" "github.com/breakpilot/school-service/internal/models" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" ) // ClassService handles class-related operations type ClassService struct { db *pgxpool.Pool } // NewClassService creates a new ClassService func NewClassService(db *pgxpool.Pool) *ClassService { return &ClassService{db: db} } // School Year Operations // CreateSchoolYear creates a new school year func (s *ClassService) CreateSchoolYear(ctx context.Context, teacherID string, req *models.CreateSchoolYearRequest) (*models.SchoolYear, error) { startDate, _ := time.Parse("2006-01-02", req.StartDate) endDate, _ := time.Parse("2006-01-02", req.EndDate) // If this is current, unset other current years for this teacher if req.IsCurrent { s.db.Exec(ctx, `UPDATE school_years SET is_current = false WHERE teacher_id = $1`, teacherID) } var year models.SchoolYear err := s.db.QueryRow(ctx, ` INSERT INTO school_years (teacher_id, name, start_date, end_date, is_current) VALUES ($1, $2, $3, $4, $5) RETURNING id, name, start_date, end_date, is_current, teacher_id, created_at `, teacherID, req.Name, startDate, endDate, req.IsCurrent).Scan( &year.ID, &year.Name, &year.StartDate, &year.EndDate, &year.IsCurrent, &year.TeacherID, &year.CreatedAt, ) return &year, err } // GetSchoolYears returns all school years for a teacher func (s *ClassService) GetSchoolYears(ctx context.Context, teacherID string) ([]models.SchoolYear, error) { rows, err := s.db.Query(ctx, ` SELECT id, name, start_date, end_date, is_current, teacher_id, created_at FROM school_years WHERE teacher_id = $1 ORDER BY start_date DESC `, teacherID) if err != nil { return nil, err } defer rows.Close() var years []models.SchoolYear for rows.Next() { var y models.SchoolYear if err := rows.Scan(&y.ID, &y.Name, &y.StartDate, &y.EndDate, &y.IsCurrent, &y.TeacherID, &y.CreatedAt); err != nil { return nil, err } years = append(years, y) } return years, nil } // Class Operations // CreateClass creates a new class func (s *ClassService) CreateClass(ctx context.Context, teacherID string, req *models.CreateClassRequest) (*models.Class, error) { var schoolYearID *uuid.UUID if req.SchoolYearID != "" { id, _ := uuid.Parse(req.SchoolYearID) schoolYearID = &id } var class models.Class err := s.db.QueryRow(ctx, ` INSERT INTO classes (teacher_id, school_year_id, name, grade_level, school_type, federal_state) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, teacher_id, school_year_id, name, grade_level, school_type, federal_state, created_at `, teacherID, schoolYearID, req.Name, req.GradeLevel, req.SchoolType, req.FederalState).Scan( &class.ID, &class.TeacherID, &class.SchoolYearID, &class.Name, &class.GradeLevel, &class.SchoolType, &class.FederalState, &class.CreatedAt, ) return &class, err } // GetClasses returns all classes for a teacher func (s *ClassService) GetClasses(ctx context.Context, teacherID string) ([]models.Class, error) { rows, err := s.db.Query(ctx, ` SELECT c.id, c.teacher_id, c.school_year_id, c.name, c.grade_level, c.school_type, c.federal_state, c.created_at, COALESCE((SELECT COUNT(*) FROM students WHERE class_id = c.id), 0) as student_count FROM classes c WHERE c.teacher_id = $1 ORDER BY c.grade_level, c.name `, teacherID) if err != nil { return nil, err } defer rows.Close() var classes []models.Class for rows.Next() { var c models.Class if err := rows.Scan(&c.ID, &c.TeacherID, &c.SchoolYearID, &c.Name, &c.GradeLevel, &c.SchoolType, &c.FederalState, &c.CreatedAt, &c.StudentCount); err != nil { return nil, err } classes = append(classes, c) } return classes, nil } // GetClass returns a single class func (s *ClassService) GetClass(ctx context.Context, classID, teacherID string) (*models.Class, error) { var class models.Class err := s.db.QueryRow(ctx, ` SELECT c.id, c.teacher_id, c.school_year_id, c.name, c.grade_level, c.school_type, c.federal_state, c.created_at, COALESCE((SELECT COUNT(*) FROM students WHERE class_id = c.id), 0) as student_count FROM classes c WHERE c.id = $1 AND c.teacher_id = $2 `, classID, teacherID).Scan( &class.ID, &class.TeacherID, &class.SchoolYearID, &class.Name, &class.GradeLevel, &class.SchoolType, &class.FederalState, &class.CreatedAt, &class.StudentCount, ) return &class, err } // DeleteClass deletes a class func (s *ClassService) DeleteClass(ctx context.Context, classID, teacherID string) error { _, err := s.db.Exec(ctx, `DELETE FROM classes WHERE id = $1 AND teacher_id = $2`, classID, teacherID) return err } // Student Operations // CreateStudent creates a new student func (s *ClassService) CreateStudent(ctx context.Context, classID string, req *models.CreateStudentRequest) (*models.Student, error) { var birthDate *time.Time if req.BirthDate != "" { t, _ := time.Parse("2006-01-02", req.BirthDate) birthDate = &t } // Get school_id from class var schoolID string err := s.db.QueryRow(ctx, `SELECT school_id FROM classes WHERE id = $1`, classID).Scan(&schoolID) if err != nil { return nil, err } var student models.Student err = s.db.QueryRow(ctx, ` INSERT INTO students (school_id, class_id, first_name, last_name, date_of_birth, student_number) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, class_id, first_name, last_name, date_of_birth, student_number, created_at `, schoolID, classID, req.FirstName, req.LastName, birthDate, req.StudentNumber).Scan( &student.ID, &student.ClassID, &student.FirstName, &student.LastName, &student.BirthDate, &student.StudentNumber, &student.CreatedAt, ) return &student, err } // GetStudents returns all students in a class func (s *ClassService) GetStudents(ctx context.Context, classID string) ([]models.Student, error) { rows, err := s.db.Query(ctx, ` SELECT id, class_id, first_name, last_name, date_of_birth, student_number, created_at FROM students WHERE class_id = $1 ORDER BY last_name, first_name `, classID) 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 } // DeleteStudent deletes a student func (s *ClassService) DeleteStudent(ctx context.Context, studentID string) error { _, err := s.db.Exec(ctx, `DELETE FROM students WHERE id = $1`, studentID) return err } // Subject Operations // CreateSubject creates a new subject func (s *ClassService) CreateSubject(ctx context.Context, teacherID string, req *models.CreateSubjectRequest) (*models.Subject, error) { var subject models.Subject err := s.db.QueryRow(ctx, ` INSERT INTO subjects (teacher_id, name, short_name, is_main_subject) VALUES ($1, $2, $3, $4) RETURNING id, teacher_id, name, short_name, is_main_subject, created_at `, teacherID, req.Name, req.ShortName, req.IsMainSubject).Scan( &subject.ID, &subject.TeacherID, &subject.Name, &subject.ShortName, &subject.IsMainSubject, &subject.CreatedAt, ) return &subject, err } // GetSubjects returns all subjects for a teacher func (s *ClassService) GetSubjects(ctx context.Context, teacherID string) ([]models.Subject, error) { rows, err := s.db.Query(ctx, ` SELECT id, teacher_id, name, short_name, is_main_subject, created_at FROM subjects WHERE teacher_id = $1 ORDER BY name `, teacherID) if err != nil { return nil, err } defer rows.Close() var subjects []models.Subject for rows.Next() { var subj models.Subject if err := rows.Scan(&subj.ID, &subj.TeacherID, &subj.Name, &subj.ShortName, &subj.IsMainSubject, &subj.CreatedAt); err != nil { return nil, err } subjects = append(subjects, subj) } return subjects, nil } // DeleteSubject deletes a subject func (s *ClassService) DeleteSubject(ctx context.Context, subjectID, teacherID string) error { _, err := s.db.Exec(ctx, `DELETE FROM subjects WHERE id = $1 AND teacher_id = $2`, subjectID, teacherID) return err }