package services import ( "context" "github.com/breakpilot/school-service/internal/models" ) // Curriculum and Assignment operations. // Ownership is enforced by joining against tt_class.created_by_user_id. // ---------- Curriculum (class × subject → weekly hours) ---------- func (s *TimetableService) CreateCurriculum(ctx context.Context, userID string, req *models.CreateTimetableCurriculumRequest) (*models.TimetableCurriculum, error) { var c models.TimetableCurriculum err := s.db.QueryRow(ctx, ` INSERT INTO tt_curriculum (class_id, subject_id, weekly_hours) SELECT $1, $2, $3 WHERE EXISTS (SELECT 1 FROM tt_class WHERE id = $1 AND created_by_user_id = $4) AND EXISTS (SELECT 1 FROM tt_subject WHERE id = $2 AND created_by_user_id = $4) RETURNING id, class_id, subject_id, weekly_hours, created_at `, req.ClassID, req.SubjectID, req.WeeklyHours, userID).Scan( &c.ID, &c.ClassID, &c.SubjectID, &c.WeeklyHours, &c.CreatedAt, ) return &c, err } func (s *TimetableService) ListCurriculum(ctx context.Context, userID string) ([]models.TimetableCurriculum, error) { rows, err := s.db.Query(ctx, ` SELECT cu.id, cu.class_id, cu.subject_id, cu.weekly_hours, cu.created_at, sub.name, cl.name FROM tt_curriculum cu JOIN tt_class cl ON cu.class_id = cl.id JOIN tt_subject sub ON cu.subject_id = sub.id WHERE cl.created_by_user_id = $1 ORDER BY cl.grade_level, cl.name, sub.name `, userID) if err != nil { return nil, err } defer rows.Close() var out []models.TimetableCurriculum for rows.Next() { var c models.TimetableCurriculum if err := rows.Scan(&c.ID, &c.ClassID, &c.SubjectID, &c.WeeklyHours, &c.CreatedAt, &c.SubjectName, &c.ClassName); err != nil { return nil, err } out = append(out, c) } return out, nil } func (s *TimetableService) DeleteCurriculum(ctx context.Context, id, userID string) error { _, err := s.db.Exec(ctx, ` DELETE FROM tt_curriculum cu USING tt_class cl WHERE cu.id = $1 AND cu.class_id = cl.id AND cl.created_by_user_id = $2 `, id, userID) return err } // ---------- Assignment (teacher × class × subject) ---------- func (s *TimetableService) CreateAssignment(ctx context.Context, userID string, req *models.CreateTimetableAssignmentRequest) (*models.TimetableAssignment, error) { var a models.TimetableAssignment err := s.db.QueryRow(ctx, ` INSERT INTO tt_assignment (teacher_id, class_id, subject_id) SELECT $1, $2, $3 WHERE EXISTS (SELECT 1 FROM tt_teacher WHERE id = $1 AND created_by_user_id = $4) AND EXISTS (SELECT 1 FROM tt_class WHERE id = $2 AND created_by_user_id = $4) AND EXISTS (SELECT 1 FROM tt_subject WHERE id = $3 AND created_by_user_id = $4) RETURNING id, teacher_id, class_id, subject_id, created_at `, req.TeacherID, req.ClassID, req.SubjectID, userID).Scan( &a.ID, &a.TeacherID, &a.ClassID, &a.SubjectID, &a.CreatedAt, ) return &a, err } func (s *TimetableService) ListAssignments(ctx context.Context, userID string) ([]models.TimetableAssignment, error) { rows, err := s.db.Query(ctx, ` SELECT a.id, a.teacher_id, a.class_id, a.subject_id, a.created_at, t.last_name || ', ' || t.first_name, cl.name, sub.name FROM tt_assignment a JOIN tt_teacher t ON a.teacher_id = t.id JOIN tt_class cl ON a.class_id = cl.id JOIN tt_subject sub ON a.subject_id = sub.id WHERE t.created_by_user_id = $1 ORDER BY cl.grade_level, cl.name, sub.name `, userID) if err != nil { return nil, err } defer rows.Close() var out []models.TimetableAssignment for rows.Next() { var a models.TimetableAssignment if err := rows.Scan(&a.ID, &a.TeacherID, &a.ClassID, &a.SubjectID, &a.CreatedAt, &a.TeacherName, &a.ClassName, &a.SubjectName); err != nil { return nil, err } out = append(out, a) } return out, nil } func (s *TimetableService) DeleteAssignment(ctx context.Context, id, userID string) error { _, err := s.db.Exec(ctx, ` DELETE FROM tt_assignment a USING tt_teacher t WHERE a.id = $1 AND a.teacher_id = t.id AND t.created_by_user_id = $2 `, id, userID) return err }