package handlers import ( "Yimaru-Backend/internal/domain" "strconv" "github.com/gofiber/fiber/v2" ) // CreateAssessmentQuestion godoc // @Summary Create assessment question // @Description Creates a new assessment question using the unified questions system // @Tags assessment-question // @Accept json // @Produce json // @Param body body createQuestionReq true "Create question payload" // @Success 201 {object} domain.Response // @Failure 400 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/assessment/questions [post] func (h *Handler) CreateAssessmentQuestion(c *fiber.Ctx) error { var req createQuestionReq if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid request body", Error: err.Error(), }) } if req.QuestionText == "" { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Validation error", Error: "question_text is required", }) } // Build options input var options []domain.CreateQuestionOptionInput for _, opt := range req.Options { options = append(options, domain.CreateQuestionOptionInput{ OptionText: opt.OptionText, OptionOrder: opt.OptionOrder, IsCorrect: opt.IsCorrect, }) } // Build short answers input var shortAnswers []domain.CreateShortAnswerInput for _, sa := range req.ShortAnswers { shortAnswers = append(shortAnswers, domain.CreateShortAnswerInput{ AcceptableAnswer: sa.AcceptableAnswer, MatchType: sa.MatchType, }) } input := domain.CreateQuestionInput{ QuestionText: req.QuestionText, QuestionType: req.QuestionType, DifficultyLevel: req.DifficultyLevel, Points: req.Points, Explanation: req.Explanation, Tips: req.Tips, VoicePrompt: req.VoicePrompt, SampleAnswerVoicePrompt: req.SampleAnswerVoicePrompt, Status: req.Status, Options: options, ShortAnswers: shortAnswers, } question, err := h.assessmentSvc.CreateQuestion(c.Context(), input) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to create assessment question", Error: err.Error(), }) } return c.Status(fiber.StatusCreated).JSON(domain.Response{ Message: "Assessment question created successfully", StatusCode: fiber.StatusCreated, Success: true, Data: questionRes{ ID: question.ID, QuestionText: question.QuestionText, QuestionType: question.QuestionType, Status: question.Status, CreatedAt: question.CreatedAt.String(), }, }) } // ListAssessmentQuestions godoc // @Summary List assessment questions // @Description Returns all active assessment questions from the initial assessment set // @Tags assessment-question // @Produce json // @Success 200 {array} domain.QuestionWithDetails // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/assessment/questions [get] func (h *Handler) ListAssessmentQuestions(c *fiber.Ctx) error { questions, err := h.assessmentSvc.ListQuestions(c.Context()) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to fetch assessment questions", Error: err.Error(), }) } var questionResponses []questionRes for _, q := range questions { var options []optionRes for _, opt := range q.Options { options = append(options, optionRes{ ID: opt.ID, OptionText: opt.OptionText, OptionOrder: opt.OptionOrder, IsCorrect: opt.IsCorrect, }) } var shortAnswers []shortAnswerRes for _, sa := range q.ShortAnswers { shortAnswers = append(shortAnswers, shortAnswerRes{ ID: sa.ID, AcceptableAnswer: sa.AcceptableAnswer, MatchType: sa.MatchType, }) } questionResponses = append(questionResponses, questionRes{ ID: q.ID, QuestionText: q.QuestionText, QuestionType: q.QuestionType, DifficultyLevel: q.DifficultyLevel, Points: q.Points, Status: q.Status, CreatedAt: q.CreatedAt.String(), Options: options, ShortAnswers: shortAnswers, }) } return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Questions fetched successfully", Data: questionResponses, Success: true, StatusCode: 200, }) } // GetAssessmentQuestionByID godoc // @Summary Get assessment question by ID // @Description Returns a single assessment question with its options or answer // @Tags assessment-question // @Produce json // @Param id path int true "Question ID" // @Success 200 {object} domain.QuestionWithDetails // @Failure 400 {object} domain.ErrorResponse // @Failure 404 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/assessment/questions/{id} [get] func (h *Handler) GetAssessmentQuestionByID(c *fiber.Ctx) error { idStr := c.Params("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil || id <= 0 { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid question ID", Error: "question ID must be a positive integer", }) } question, err := h.assessmentSvc.GetQuestionByID(c.Context(), id) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to fetch assessment question", Error: err.Error(), }) } var options []optionRes for _, opt := range question.Options { options = append(options, optionRes{ ID: opt.ID, OptionText: opt.OptionText, OptionOrder: opt.OptionOrder, IsCorrect: opt.IsCorrect, }) } var shortAnswers []shortAnswerRes for _, sa := range question.ShortAnswers { shortAnswers = append(shortAnswers, shortAnswerRes{ ID: sa.ID, AcceptableAnswer: sa.AcceptableAnswer, MatchType: sa.MatchType, }) } return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Question fetched successfully", Data: questionRes{ ID: question.ID, QuestionText: question.QuestionText, QuestionType: question.QuestionType, DifficultyLevel: question.DifficultyLevel, Points: question.Points, Status: question.Status, CreatedAt: question.CreatedAt.String(), Options: options, ShortAnswers: shortAnswers, }, Success: true, StatusCode: 200, }) }