package handlers import ( "Yimaru-Backend/internal/domain" "Yimaru-Backend/internal/services/examprep" "errors" "strconv" "github.com/gofiber/fiber/v2" ) // CreateExamPrepLesson godoc // @Summary Create exam-prep lesson (under a unit module) // @Tags exam-prep // @Param moduleId path int true "Exam prep unit module ID" // @Param body body domain.CreateExamPrepLessonInput true "Lesson" // @Router /api/v1/exam-prep/modules/{moduleId}/lessons [post] func (h *Handler) CreateExamPrepLesson(c *fiber.Ctx) error { moduleID, err := strconv.ParseInt(c.Params("moduleId"), 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid module id", Error: err.Error(), }) } var req domain.CreateExamPrepLessonInput if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid request body", Error: err.Error(), }) } if valErrs, ok := h.validator.Validate(c, req); !ok { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Validation failed", Error: firstValidationError(valErrs), }) } les, err := h.examPrepSvc.CreateLesson(c.Context(), moduleID, req) if err != nil { if errors.Is(err, examprep.ErrModuleNotFound) { return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{ Message: "Module not found", Error: err.Error(), }) } return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to create lesson", Error: err.Error(), }) } return c.Status(fiber.StatusCreated).JSON(domain.Response{ Message: "Lesson created successfully", Data: les, Success: true, StatusCode: fiber.StatusCreated, }) } // ListExamPrepLessonsByUnitModule godoc // @Summary List exam-prep lessons for a unit module // @Tags exam-prep // @Param moduleId path int true "Exam prep unit module ID" // @Param limit query int false "Page size" default(20) // @Param offset query int false "Offset" default(0) // @Router /api/v1/exam-prep/modules/{moduleId}/lessons [get] func (h *Handler) ListExamPrepLessonsByUnitModule(c *fiber.Ctx) error { moduleID, err := strconv.ParseInt(c.Params("moduleId"), 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid module id", Error: err.Error(), }) } limit, _ := strconv.Atoi(c.Query("limit", "20")) offset, _ := strconv.Atoi(c.Query("offset", "0")) items, total, err := h.examPrepSvc.ListLessonsByUnitModule(c.Context(), moduleID, int32(limit), int32(offset)) if err != nil { if errors.Is(err, examprep.ErrModuleNotFound) { return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{ Message: "Module not found", Error: err.Error(), }) } return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to list lessons", Error: err.Error(), }) } return c.JSON(domain.Response{ Message: "Lessons retrieved successfully", Data: fiber.Map{ "lessons": items, "total_count": total, "limit": limit, "offset": offset, }, Success: true, StatusCode: fiber.StatusOK, }) } // ReorderExamPrepLessonsInUnitModule godoc // @Summary Reorder lessons within an exam-prep unit module // @Tags exam-prep // @Router /api/v1/exam-prep/modules/{moduleId}/lessons/reorder [put] func (h *Handler) ReorderExamPrepLessonsInUnitModule(c *fiber.Ctx) error { moduleID, err := strconv.ParseInt(c.Params("moduleId"), 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid module id", Error: err.Error(), }) } var req domain.ReorderIDsRequest if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid request body", Error: err.Error(), }) } if req.OrderedIDs == nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "ordered_ids is required (use an empty array if there are no lessons)", Error: "missing ordered_ids", }) } if err := h.examPrepSvc.ReorderLessonsInUnitModule(c.Context(), moduleID, req.OrderedIDs); err != nil { if errors.Is(err, examprep.ErrModuleNotFound) { return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{ Message: "Module not found", Error: err.Error(), }) } if errors.Is(err, domain.ErrReorderInvalidIDSet) { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: err.Error(), Error: "INVALID_REORDER", }) } return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to reorder lessons", Error: err.Error(), }) } return c.JSON(domain.Response{ Message: "Lessons reordered successfully", Success: true, StatusCode: fiber.StatusOK, }) } // GetExamPrepLessonByID godoc // @Summary Get exam-prep lesson by ID // @Tags exam-prep // @Router /api/v1/exam-prep/lessons/{id} [get] func (h *Handler) GetExamPrepLessonByID(c *fiber.Ctx) error { id, err := strconv.ParseInt(c.Params("id"), 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid lesson id", Error: err.Error(), }) } les, err := h.examPrepSvc.GetLessonByID(c.Context(), id) if err != nil { if errors.Is(err, examprep.ErrLessonNotFound) { return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{ Message: "Lesson not found", Error: err.Error(), }) } return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to get lesson", Error: err.Error(), }) } return c.JSON(domain.Response{ Message: "Lesson retrieved successfully", Data: les, Success: true, StatusCode: fiber.StatusOK, }) } // UpdateExamPrepLesson godoc // @Summary Update exam-prep lesson // @Tags exam-prep // @Router /api/v1/exam-prep/lessons/{id} [put] func (h *Handler) UpdateExamPrepLesson(c *fiber.Ctx) error { id, err := strconv.ParseInt(c.Params("id"), 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid lesson id", Error: err.Error(), }) } var req domain.UpdateExamPrepLessonInput if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid request body", Error: err.Error(), }) } les, err := h.examPrepSvc.UpdateLesson(c.Context(), id, req) if err != nil { if errors.Is(err, examprep.ErrLessonNotFound) { return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{ Message: "Lesson not found", Error: err.Error(), }) } return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to update lesson", Error: err.Error(), }) } return c.JSON(domain.Response{ Message: "Lesson updated successfully", Data: les, Success: true, StatusCode: fiber.StatusOK, }) } // DeleteExamPrepLesson godoc // @Summary Delete exam-prep lesson // @Tags exam-prep // @Router /api/v1/exam-prep/lessons/{id} [delete] func (h *Handler) DeleteExamPrepLesson(c *fiber.Ctx) error { id, err := strconv.ParseInt(c.Params("id"), 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid lesson id", Error: err.Error(), }) } if err := h.examPrepSvc.DeleteLesson(c.Context(), id); err != nil { if errors.Is(err, examprep.ErrLessonNotFound) { return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{ Message: "Lesson not found", Error: err.Error(), }) } return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to delete lesson", Error: err.Error(), }) } return c.JSON(domain.Response{ Message: "Lesson deleted successfully", Success: true, StatusCode: fiber.StatusOK, }) }