package handlers

import (
	"Yimaru-Backend/internal/domain"
	"context"
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/gofiber/fiber/v2"
)

// Request/Response types for Questions

type optionInput struct {
	OptionText  string `json:"option_text" validate:"required"`
	OptionOrder *int32 `json:"option_order"`
	IsCorrect   bool   `json:"is_correct"`
}

type shortAnswerInput struct {
	AcceptableAnswer string  `json:"acceptable_answer" validate:"required"`
	MatchType        *string `json:"match_type"`
}

type createQuestionReq struct {
	QuestionText            string             `json:"question_text" validate:"required"`
	QuestionType            string             `json:"question_type" validate:"required,oneof=MCQ TRUE_FALSE SHORT_ANSWER"`
	DifficultyLevel         *string            `json:"difficulty_level"`
	Points                  *int32             `json:"points"`
	Explanation             *string            `json:"explanation"`
	Tips                    *string            `json:"tips"`
	VoicePrompt             *string            `json:"voice_prompt"`
	SampleAnswerVoicePrompt *string            `json:"sample_answer_voice_prompt"`
	Status                  *string            `json:"status"`
	Options                 []optionInput      `json:"options"`
	ShortAnswers            []shortAnswerInput `json:"short_answers"`
}

type optionRes struct {
	ID          int64  `json:"id"`
	OptionText  string `json:"option_text"`
	OptionOrder int32  `json:"option_order"`
	IsCorrect   bool   `json:"is_correct"`
}

type shortAnswerRes struct {
	ID               int64  `json:"id"`
	AcceptableAnswer string `json:"acceptable_answer"`
	MatchType        string `json:"match_type"`
}

type questionRes struct {
	ID                      int64            `json:"id"`
	QuestionText            string           `json:"question_text"`
	QuestionType            string           `json:"question_type"`
	DifficultyLevel         *string          `json:"difficulty_level,omitempty"`
	Points                  int32            `json:"points"`
	Explanation             *string          `json:"explanation,omitempty"`
	Tips                    *string          `json:"tips,omitempty"`
	VoicePrompt             *string          `json:"voice_prompt,omitempty"`
	SampleAnswerVoicePrompt *string          `json:"sample_answer_voice_prompt,omitempty"`
	Status                  string           `json:"status"`
	CreatedAt               string           `json:"created_at"`
	Options                 []optionRes      `json:"options,omitempty"`
	ShortAnswers            []shortAnswerRes `json:"short_answers,omitempty"`
}

type listQuestionsRes struct {
	Questions  []questionRes `json:"questions"`
	TotalCount int64         `json:"total_count"`
}

// CreateQuestion godoc
// @Summary Create a new question
// @Description Creates a new question with options (for MCQ/TRUE_FALSE) or short answers (for SHORT_ANSWER)
// @Tags questions
// @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/questions [post]
func (h *Handler) CreateQuestion(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(),
		})
	}

	// 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.questionsSvc.CreateQuestion(c.Context(), input)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to create question",
			Error:   err.Error(),
		})
	}

	actorID := c.Locals("user_id").(int64)
	actorRole := string(c.Locals("role").(domain.Role))
	ip := c.IP()
	ua := c.Get("User-Agent")
	meta, _ := json.Marshal(map[string]interface{}{"question_type": question.QuestionType})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionQuestionCreated, domain.ResourceQuestion, &question.ID, "Created question: "+question.QuestionText, meta, &ip, &ua)

	return c.Status(fiber.StatusCreated).JSON(domain.Response{
		Message: "Question created successfully",
		Data: questionRes{
			ID:                      question.ID,
			QuestionText:            question.QuestionText,
			QuestionType:            question.QuestionType,
			DifficultyLevel:         question.DifficultyLevel,
			Points:                  question.Points,
			Explanation:             question.Explanation,
			Tips:                    question.Tips,
			VoicePrompt:             question.VoicePrompt,
			SampleAnswerVoicePrompt: question.SampleAnswerVoicePrompt,
			Status:                  question.Status,
			CreatedAt:               question.CreatedAt.String(),
		},
	})
}

// GetQuestionByID godoc
// @Summary Get question by ID
// @Description Returns a question with its options/short answers
// @Tags questions
// @Produce json
// @Param id path int true "Question ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 404 {object} domain.ErrorResponse
// @Router /api/v1/questions/{id} [get]
func (h *Handler) GetQuestionByID(c *fiber.Ctx) error {
	idStr := c.Params("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid question ID",
			Error:   err.Error(),
		})
	}

	question, err := h.questionsSvc.GetQuestionWithDetails(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
			Message: "Question not found",
			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.JSON(domain.Response{
		Message: "Question retrieved successfully",
		Data: questionRes{
			ID:                      question.ID,
			QuestionText:            question.QuestionText,
			QuestionType:            question.QuestionType,
			DifficultyLevel:         question.DifficultyLevel,
			Points:                  question.Points,
			Explanation:             question.Explanation,
			Tips:                    question.Tips,
			VoicePrompt:             question.VoicePrompt,
			SampleAnswerVoicePrompt: question.SampleAnswerVoicePrompt,
			Status:                  question.Status,
			CreatedAt:               question.CreatedAt.String(),
			Options:                 options,
			ShortAnswers:            shortAnswers,
		},
	})
}

// ListQuestions godoc
// @Summary List questions
// @Description Returns a paginated list of questions with optional filters
// @Tags questions
// @Produce json
// @Param question_type query string false "Question type filter (MCQ, TRUE_FALSE, SHORT_ANSWER)"
// @Param difficulty query string false "Difficulty level filter (EASY, MEDIUM, HARD)"
// @Param status query string false "Status filter (DRAFT, PUBLISHED, INACTIVE)"
// @Param limit query int false "Limit" default(10)
// @Param offset query int false "Offset" default(0)
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/questions [get]
func (h *Handler) ListQuestions(c *fiber.Ctx) error {
	questionType := c.Query("question_type")
	difficulty := c.Query("difficulty")
	status := c.Query("status")
	limitStr := c.Query("limit", "10")
	offsetStr := c.Query("offset", "0")

	limit, _ := strconv.Atoi(limitStr)
	offset, _ := strconv.Atoi(offsetStr)

	var qTypePtr, diffPtr, statusPtr *string
	if questionType != "" {
		qTypePtr = &questionType
	}
	if difficulty != "" {
		diffPtr = &difficulty
	}
	if status != "" {
		statusPtr = &status
	}

	questions, totalCount, err := h.questionsSvc.ListQuestions(c.Context(), qTypePtr, diffPtr, statusPtr, int32(limit), int32(offset))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to list questions",
			Error:   err.Error(),
		})
	}

	var questionResponses []questionRes
	for _, q := range questions {
		questionResponses = append(questionResponses, questionRes{
			ID:              q.ID,
			QuestionText:    q.QuestionText,
			QuestionType:    q.QuestionType,
			DifficultyLevel: q.DifficultyLevel,
			Points:          q.Points,
			Explanation:     q.Explanation,
			Tips:            q.Tips,
			VoicePrompt:     q.VoicePrompt,
			Status:          q.Status,
			CreatedAt:       q.CreatedAt.String(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Questions retrieved successfully",
		Data: listQuestionsRes{
			Questions:  questionResponses,
			TotalCount: totalCount,
		},
	})
}

// SearchQuestions godoc
// @Summary Search questions
// @Description Search questions by text
// @Tags questions
// @Produce json
// @Param q query string true "Search query"
// @Param limit query int false "Limit" default(10)
// @Param offset query int false "Offset" default(0)
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/questions/search [get]
func (h *Handler) SearchQuestions(c *fiber.Ctx) error {
	query := c.Query("q")
	if query == "" {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Search query is required",
		})
	}

	limitStr := c.Query("limit", "10")
	offsetStr := c.Query("offset", "0")
	limit, _ := strconv.Atoi(limitStr)
	offset, _ := strconv.Atoi(offsetStr)

	questions, totalCount, err := h.questionsSvc.SearchQuestions(c.Context(), query, int32(limit), int32(offset))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to search questions",
			Error:   err.Error(),
		})
	}

	var questionResponses []questionRes
	for _, q := range questions {
		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(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Questions retrieved successfully",
		Data: listQuestionsRes{
			Questions:  questionResponses,
			TotalCount: totalCount,
		},
	})
}

type updateQuestionReq struct {
	QuestionText            *string            `json:"question_text"`
	QuestionType            *string            `json:"question_type"`
	DifficultyLevel         *string            `json:"difficulty_level"`
	Points                  *int32             `json:"points"`
	Explanation             *string            `json:"explanation"`
	Tips                    *string            `json:"tips"`
	VoicePrompt             *string            `json:"voice_prompt"`
	SampleAnswerVoicePrompt *string            `json:"sample_answer_voice_prompt"`
	Status                  *string            `json:"status"`
	Options                 []optionInput      `json:"options"`
	ShortAnswers            []shortAnswerInput `json:"short_answers"`
}

// UpdateQuestion godoc
// @Summary Update a question
// @Description Updates a question and optionally replaces its options/short answers
// @Tags questions
// @Accept json
// @Produce json
// @Param id path int true "Question ID"
// @Param body body updateQuestionReq true "Update question payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/questions/{id} [put]
func (h *Handler) UpdateQuestion(c *fiber.Ctx) error {
	idStr := c.Params("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid question ID",
			Error:   err.Error(),
		})
	}

	var req updateQuestionReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	var options []domain.CreateQuestionOptionInput
	for _, opt := range req.Options {
		options = append(options, domain.CreateQuestionOptionInput{
			OptionText:  opt.OptionText,
			OptionOrder: opt.OptionOrder,
			IsCorrect:   opt.IsCorrect,
		})
	}

	var shortAnswers []domain.CreateShortAnswerInput
	for _, sa := range req.ShortAnswers {
		shortAnswers = append(shortAnswers, domain.CreateShortAnswerInput{
			AcceptableAnswer: sa.AcceptableAnswer,
			MatchType:        sa.MatchType,
		})
	}

	questionText := ""
	if req.QuestionText != nil {
		questionText = *req.QuestionText
	}
	questionType := ""
	if req.QuestionType != nil {
		questionType = *req.QuestionType
	}

	input := domain.CreateQuestionInput{
		QuestionText:            questionText,
		QuestionType:            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,
	}

	err = h.questionsSvc.UpdateQuestion(c.Context(), id, input)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to update question",
			Error:   err.Error(),
		})
	}

	actorID := c.Locals("user_id").(int64)
	actorRole := string(c.Locals("role").(domain.Role))
	ip := c.IP()
	ua := c.Get("User-Agent")
	meta, _ := json.Marshal(map[string]interface{}{"question_id": id})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionQuestionUpdated, domain.ResourceQuestion, &id, fmt.Sprintf("Updated question ID: %d", id), meta, &ip, &ua)

	return c.JSON(domain.Response{
		Message: "Question updated successfully",
	})
}

// DeleteQuestion godoc
// @Summary Delete a question
// @Description Archives a question (soft delete)
// @Tags questions
// @Produce json
// @Param id path int true "Question ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/questions/{id} [delete]
func (h *Handler) DeleteQuestion(c *fiber.Ctx) error {
	idStr := c.Params("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid question ID",
			Error:   err.Error(),
		})
	}

	err = h.questionsSvc.ArchiveQuestion(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to delete question",
			Error:   err.Error(),
		})
	}

	actorID := c.Locals("user_id").(int64)
	actorRole := string(c.Locals("role").(domain.Role))
	ip := c.IP()
	ua := c.Get("User-Agent")
	meta, _ := json.Marshal(map[string]interface{}{"question_id": id})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionQuestionDeleted, domain.ResourceQuestion, &id, fmt.Sprintf("Deleted question ID: %d", id), meta, &ip, &ua)

	return c.JSON(domain.Response{
		Message: "Question deleted successfully",
	})
}

// Question Set types

type createQuestionSetReq struct {
	Title            string  `json:"title" validate:"required"`
	Description      *string `json:"description"`
	SetType          string  `json:"set_type" validate:"required,oneof=PRACTICE INITIAL_ASSESSMENT QUIZ EXAM SURVEY"`
	OwnerType        *string `json:"owner_type"`
	OwnerID          *int64  `json:"owner_id"`
	BannerImage      *string `json:"banner_image"`
	Persona          *string `json:"persona"`
	TimeLimitMinutes *int32  `json:"time_limit_minutes"`
	PassingScore     *int32  `json:"passing_score"`
	ShuffleQuestions *bool   `json:"shuffle_questions"`
	Status           *string `json:"status"`
	SubCourseVideoID *int64  `json:"sub_course_video_id"`
}

type questionSetRes struct {
	ID               int64   `json:"id"`
	Title            string  `json:"title"`
	Description      *string `json:"description,omitempty"`
	SetType          string  `json:"set_type"`
	OwnerType        *string `json:"owner_type,omitempty"`
	OwnerID          *int64  `json:"owner_id,omitempty"`
	BannerImage      *string `json:"banner_image,omitempty"`
	Persona          *string `json:"persona,omitempty"`
	TimeLimitMinutes *int32  `json:"time_limit_minutes,omitempty"`
	PassingScore     *int32  `json:"passing_score,omitempty"`
	ShuffleQuestions bool    `json:"shuffle_questions"`
	Status           string  `json:"status"`
	SubCourseVideoID *int64  `json:"sub_course_video_id,omitempty"`
	CreatedAt        string  `json:"created_at"`
	QuestionCount    *int64  `json:"question_count,omitempty"`
}

type listQuestionSetsRes struct {
	QuestionSets []questionSetRes `json:"question_sets"`
	TotalCount   int64            `json:"total_count"`
}

// CreateQuestionSet godoc
// @Summary Create a new question set
// @Description Creates a new question set (practice, assessment, quiz, exam, or survey)
// @Tags question-sets
// @Accept json
// @Produce json
// @Param body body createQuestionSetReq true "Create question set payload"
// @Success 201 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/question-sets [post]
func (h *Handler) CreateQuestionSet(c *fiber.Ctx) error {
	var req createQuestionSetReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	input := domain.CreateQuestionSetInput{
		Title:            req.Title,
		Description:      req.Description,
		SetType:          req.SetType,
		OwnerType:        req.OwnerType,
		OwnerID:          req.OwnerID,
		BannerImage:      req.BannerImage,
		Persona:          req.Persona,
		TimeLimitMinutes: req.TimeLimitMinutes,
		PassingScore:     req.PassingScore,
		ShuffleQuestions: req.ShuffleQuestions,
		Status:           req.Status,
		SubCourseVideoID: req.SubCourseVideoID,
	}

	set, err := h.questionsSvc.CreateQuestionSet(c.Context(), input)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to create question set",
			Error:   err.Error(),
		})
	}

	actorID := c.Locals("user_id").(int64)
	actorRole := string(c.Locals("role").(domain.Role))
	ip := c.IP()
	ua := c.Get("User-Agent")
	qsMeta, _ := json.Marshal(map[string]interface{}{"title": set.Title, "set_type": set.SetType})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionQuestionSetCreated, domain.ResourceQuestionSet, &set.ID, "Created question set: "+set.Title, qsMeta, &ip, &ua)

	return c.Status(fiber.StatusCreated).JSON(domain.Response{
		Message: "Question set created successfully",
		Data: questionSetRes{
			ID:               set.ID,
			Title:            set.Title,
			Description:      set.Description,
			SetType:          set.SetType,
			OwnerType:        set.OwnerType,
			OwnerID:          set.OwnerID,
			BannerImage:      set.BannerImage,
			Persona:          set.Persona,
			TimeLimitMinutes: set.TimeLimitMinutes,
			PassingScore:     set.PassingScore,
			ShuffleQuestions: set.ShuffleQuestions,
			Status:           set.Status,
			SubCourseVideoID: set.SubCourseVideoID,
			CreatedAt:        set.CreatedAt.String(),
		},
	})
}

// GetQuestionSetByID godoc
// @Summary Get question set by ID
// @Description Returns a question set with question count
// @Tags question-sets
// @Produce json
// @Param id path int true "Question Set ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 404 {object} domain.ErrorResponse
// @Router /api/v1/question-sets/{id} [get]
func (h *Handler) GetQuestionSetByID(c *fiber.Ctx) error {
	idStr := c.Params("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid question set ID",
			Error:   err.Error(),
		})
	}

	set, err := h.questionsSvc.GetQuestionSetByID(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
			Message: "Question set not found",
			Error:   err.Error(),
		})
	}

	count, _ := h.questionsSvc.CountQuestionsInSet(c.Context(), id)

	return c.JSON(domain.Response{
		Message: "Question set retrieved successfully",
		Data: questionSetRes{
			ID:               set.ID,
			Title:            set.Title,
			Description:      set.Description,
			SetType:          set.SetType,
			OwnerType:        set.OwnerType,
			OwnerID:          set.OwnerID,
			BannerImage:      set.BannerImage,
			Persona:          set.Persona,
			TimeLimitMinutes: set.TimeLimitMinutes,
			PassingScore:     set.PassingScore,
			ShuffleQuestions: set.ShuffleQuestions,
			Status:           set.Status,
			SubCourseVideoID: set.SubCourseVideoID,
			CreatedAt:        set.CreatedAt.String(),
			QuestionCount:    &count,
		},
	})
}

// GetQuestionSetsByType godoc
// @Summary Get question sets by type
// @Description Returns a paginated list of question sets filtered by type
// @Tags question-sets
// @Produce json
// @Param set_type query string true "Set type (PRACTICE, INITIAL_ASSESSMENT, QUIZ, EXAM, SURVEY)"
// @Param limit query int false "Limit" default(10)
// @Param offset query int false "Offset" default(0)
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/question-sets [get]
func (h *Handler) GetQuestionSetsByType(c *fiber.Ctx) error {
	setType := c.Query("set_type")
	if setType == "" {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "set_type query parameter is required",
		})
	}

	limitStr := c.Query("limit", "10")
	offsetStr := c.Query("offset", "0")
	limit, _ := strconv.Atoi(limitStr)
	offset, _ := strconv.Atoi(offsetStr)

	sets, totalCount, err := h.questionsSvc.GetQuestionSetsByType(c.Context(), setType, int32(limit), int32(offset))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to get question sets",
			Error:   err.Error(),
		})
	}

	var setResponses []questionSetRes
	for _, s := range sets {
		setResponses = append(setResponses, questionSetRes{
			ID:               s.ID,
			Title:            s.Title,
			Description:      s.Description,
			SetType:          s.SetType,
			OwnerType:        s.OwnerType,
			OwnerID:          s.OwnerID,
			BannerImage:      s.BannerImage,
			Persona:          s.Persona,
			TimeLimitMinutes: s.TimeLimitMinutes,
			PassingScore:     s.PassingScore,
			ShuffleQuestions: s.ShuffleQuestions,
			Status:           s.Status,
			SubCourseVideoID: s.SubCourseVideoID,
			CreatedAt:        s.CreatedAt.String(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Question sets retrieved successfully",
		Data: listQuestionSetsRes{
			QuestionSets: setResponses,
			TotalCount:   totalCount,
		},
	})
}

// GetQuestionSetsByOwner godoc
// @Summary Get question sets by owner
// @Description Returns question sets for a specific owner (e.g., sub-course)
// @Tags question-sets
// @Produce json
// @Param owner_type query string true "Owner type (SUB_COURSE, COURSE, CATEGORY, STANDALONE)"
// @Param owner_id query int true "Owner ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/question-sets/by-owner [get]
func (h *Handler) GetQuestionSetsByOwner(c *fiber.Ctx) error {
	ownerType := c.Query("owner_type")
	ownerIDStr := c.Query("owner_id")

	if ownerType == "" || ownerIDStr == "" {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "owner_type and owner_id query parameters are required",
		})
	}

	ownerID, err := strconv.ParseInt(ownerIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid owner_id",
			Error:   err.Error(),
		})
	}

	sets, err := h.questionsSvc.GetQuestionSetsByOwner(c.Context(), ownerType, ownerID)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to get question sets",
			Error:   err.Error(),
		})
	}

	var setResponses []questionSetRes
	for _, s := range sets {
		setResponses = append(setResponses, questionSetRes{
			ID:               s.ID,
			Title:            s.Title,
			Description:      s.Description,
			SetType:          s.SetType,
			OwnerType:        s.OwnerType,
			OwnerID:          s.OwnerID,
			BannerImage:      s.BannerImage,
			Persona:          s.Persona,
			TimeLimitMinutes: s.TimeLimitMinutes,
			PassingScore:     s.PassingScore,
			ShuffleQuestions: s.ShuffleQuestions,
			Status:           s.Status,
			SubCourseVideoID: s.SubCourseVideoID,
			CreatedAt:        s.CreatedAt.String(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Question sets retrieved successfully",
		Data:    setResponses,
	})
}

type updateQuestionSetReq struct {
	Title            *string `json:"title"`
	Description      *string `json:"description"`
	BannerImage      *string `json:"banner_image"`
	Persona          *string `json:"persona"`
	TimeLimitMinutes *int32  `json:"time_limit_minutes"`
	PassingScore     *int32  `json:"passing_score"`
	ShuffleQuestions *bool   `json:"shuffle_questions"`
	Status           *string `json:"status"`
	SubCourseVideoID *int64  `json:"sub_course_video_id"`
}

// UpdateQuestionSet godoc
// @Summary Update a question set
// @Description Updates a question set's properties
// @Tags question-sets
// @Accept json
// @Produce json
// @Param id path int true "Question Set ID"
// @Param body body updateQuestionSetReq true "Update question set payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/question-sets/{id} [put]
func (h *Handler) UpdateQuestionSet(c *fiber.Ctx) error {
	idStr := c.Params("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid question set ID",
			Error:   err.Error(),
		})
	}

	var req updateQuestionSetReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	title := ""
	if req.Title != nil {
		title = *req.Title
	}

	input := domain.CreateQuestionSetInput{
		Title:            title,
		Description:      req.Description,
		BannerImage:      req.BannerImage,
		Persona:          req.Persona,
		TimeLimitMinutes: req.TimeLimitMinutes,
		PassingScore:     req.PassingScore,
		ShuffleQuestions: req.ShuffleQuestions,
		Status:           req.Status,
		SubCourseVideoID: req.SubCourseVideoID,
	}

	err = h.questionsSvc.UpdateQuestionSet(c.Context(), id, input)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to update question set",
			Error:   err.Error(),
		})
	}

	actorID := c.Locals("user_id").(int64)
	actorRole := string(c.Locals("role").(domain.Role))
	ip := c.IP()
	ua := c.Get("User-Agent")
	meta, _ := json.Marshal(map[string]interface{}{"question_set_id": id})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionQuestionSetUpdated, domain.ResourceQuestionSet, &id, fmt.Sprintf("Updated question set ID: %d", id), meta, &ip, &ua)

	return c.JSON(domain.Response{
		Message: "Question set updated successfully",
	})
}

// DeleteQuestionSet godoc
// @Summary Delete a question set
// @Description Archives a question set (soft delete)
// @Tags question-sets
// @Produce json
// @Param id path int true "Question Set ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/question-sets/{id} [delete]
func (h *Handler) DeleteQuestionSet(c *fiber.Ctx) error {
	idStr := c.Params("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid question set ID",
			Error:   err.Error(),
		})
	}

	err = h.questionsSvc.ArchiveQuestionSet(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to delete question set",
			Error:   err.Error(),
		})
	}

	actorID := c.Locals("user_id").(int64)
	actorRole := string(c.Locals("role").(domain.Role))
	ip := c.IP()
	ua := c.Get("User-Agent")
	meta, _ := json.Marshal(map[string]interface{}{"question_set_id": id})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionQuestionSetDeleted, domain.ResourceQuestionSet, &id, fmt.Sprintf("Deleted question set ID: %d", id), meta, &ip, &ua)

	return c.JSON(domain.Response{
		Message: "Question set deleted successfully",
	})
}

// Question Set Items

type addQuestionToSetReq struct {
	QuestionID   int64  `json:"question_id" validate:"required"`
	DisplayOrder *int32 `json:"display_order"`
}

type questionSetItemRes struct {
	ID              int64   `json:"id"`
	SetID           int64   `json:"set_id"`
	QuestionID      int64   `json:"question_id"`
	DisplayOrder    int32   `json:"display_order"`
	QuestionText    string  `json:"question_text"`
	QuestionType    string  `json:"question_type"`
	DifficultyLevel *string `json:"difficulty_level,omitempty"`
	Points          int32   `json:"points"`
	Explanation     *string `json:"explanation,omitempty"`
	Tips            *string `json:"tips,omitempty"`
	VoicePrompt     *string `json:"voice_prompt,omitempty"`
	QuestionStatus  string  `json:"question_status"`
}

// AddQuestionToSet godoc
// @Summary Add question to set
// @Description Links a question to a question set
// @Tags question-set-items
// @Accept json
// @Produce json
// @Param setId path int true "Question Set ID"
// @Param body body addQuestionToSetReq true "Add question to set payload"
// @Success 201 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/question-sets/{setId}/questions [post]
func (h *Handler) AddQuestionToSet(c *fiber.Ctx) error {
	setIDStr := c.Params("setId")
	setID, err := strconv.ParseInt(setIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid set ID",
			Error:   err.Error(),
		})
	}

	var req addQuestionToSetReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	item, err := h.questionsSvc.AddQuestionToSet(c.Context(), setID, req.QuestionID, req.DisplayOrder)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to add question to set",
			Error:   err.Error(),
		})
	}

	return c.Status(fiber.StatusCreated).JSON(domain.Response{
		Message: "Question added to set successfully",
		Data: map[string]interface{}{
			"id":            item.ID,
			"set_id":        item.SetID,
			"question_id":   item.QuestionID,
			"display_order": item.DisplayOrder,
		},
	})
}

// GetQuestionSetItems godoc
// @Summary Get questions in set
// @Description Returns all questions in a question set with details
// @Tags question-set-items
// @Produce json
// @Param setId path int true "Question Set ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/question-sets/{setId}/questions [get]
func (h *Handler) GetQuestionsInSet(c *fiber.Ctx) error {
	setIDStr := c.Params("setId")
	setID, err := strconv.ParseInt(setIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid set ID",
			Error:   err.Error(),
		})
	}

	items, err := h.questionsSvc.GetQuestionSetItems(c.Context(), setID)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to get questions in set",
			Error:   err.Error(),
		})
	}

	var itemResponses []questionSetItemRes
	for _, item := range items {
		itemResponses = append(itemResponses, questionSetItemRes{
			ID:              item.ID,
			SetID:           item.SetID,
			QuestionID:      item.QuestionID,
			DisplayOrder:    item.DisplayOrder,
			QuestionText:    item.QuestionText,
			QuestionType:    item.QuestionType,
			DifficultyLevel: item.DifficultyLevel,
			Points:          item.Points,
			Explanation:     item.Explanation,
			Tips:            item.Tips,
			VoicePrompt:     item.VoicePrompt,
			QuestionStatus:  item.QuestionStatus,
		})
	}

	return c.JSON(domain.Response{
		Message: "Questions retrieved successfully",
		Data:    itemResponses,
	})
}

// RemoveQuestionFromSet godoc
// @Summary Remove question from set
// @Description Unlinks a question from a question set
// @Tags question-set-items
// @Produce json
// @Param setId path int true "Question Set ID"
// @Param questionId path int true "Question ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/question-sets/{setId}/questions/{questionId} [delete]
func (h *Handler) RemoveQuestionFromSet(c *fiber.Ctx) error {
	setIDStr := c.Params("setId")
	setID, err := strconv.ParseInt(setIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid set ID",
			Error:   err.Error(),
		})
	}

	questionIDStr := c.Params("questionId")
	questionID, err := strconv.ParseInt(questionIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid question ID",
			Error:   err.Error(),
		})
	}

	err = h.questionsSvc.RemoveQuestionFromSet(c.Context(), setID, questionID)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to remove question from set",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Question removed from set successfully",
	})
}

type updateQuestionOrderReq struct {
	DisplayOrder int32 `json:"display_order" validate:"required"`
}

// UpdateQuestionOrder godoc
// @Summary Update question order in set
// @Description Updates the display order of a question in a set
// @Tags question-set-items
// @Accept json
// @Produce json
// @Param setId path int true "Question Set ID"
// @Param questionId path int true "Question ID"
// @Param body body updateQuestionOrderReq true "Update order payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/question-sets/{setId}/questions/{questionId}/order [put]
func (h *Handler) UpdateQuestionOrderInSet(c *fiber.Ctx) error {
	setIDStr := c.Params("setId")
	setID, err := strconv.ParseInt(setIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid set ID",
			Error:   err.Error(),
		})
	}

	questionIDStr := c.Params("questionId")
	questionID, err := strconv.ParseInt(questionIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid question ID",
			Error:   err.Error(),
		})
	}

	var req updateQuestionOrderReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	err = h.questionsSvc.UpdateQuestionOrder(c.Context(), setID, questionID, req.DisplayOrder)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to update question order",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Question order updated successfully",
	})
}

// User Persona types for question sets

type userPersonaRes struct {
	ID                int64   `json:"id"`
	FirstName         *string `json:"first_name,omitempty"`
	LastName          *string `json:"last_name,omitempty"`
	NickName          *string `json:"nick_name,omitempty"`
	ProfilePictureURL *string `json:"profile_picture_url,omitempty"`
	Role              string  `json:"role"`
	DisplayOrder      int32   `json:"display_order"`
}

type addUserPersonaReq struct {
	UserID       int64 `json:"user_id" validate:"required"`
	DisplayOrder int32 `json:"display_order"`
}

// GetUserPersonasByQuestionSet godoc
// @Summary Get user personas for a question set
// @Description Returns all users assigned as personas to a question set (practice)
// @Tags question-sets
// @Produce json
// @Param setId path int true "Question Set ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/question-sets/{setId}/personas [get]
func (h *Handler) GetUserPersonasByQuestionSet(c *fiber.Ctx) error {
	setIDStr := c.Params("setId")
	setID, err := strconv.ParseInt(setIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid set ID",
			Error:   err.Error(),
		})
	}

	personas, err := h.questionsSvc.GetUserPersonasByQuestionSetID(c.Context(), setID)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to get personas",
			Error:   err.Error(),
		})
	}

	result := make([]userPersonaRes, len(personas))
	for i, p := range personas {
		result[i] = userPersonaRes{
			ID:                p.ID,
			FirstName:         p.FirstName,
			LastName:          p.LastName,
			NickName:          p.NickName,
			ProfilePictureURL: p.ProfilePictureURL,
			Role:              p.Role,
			DisplayOrder:      p.DisplayOrder,
		}
	}

	return c.JSON(domain.Response{
		Message: "Personas retrieved successfully",
		Data:    result,
	})
}

// AddUserPersonaToQuestionSet godoc
// @Summary Add a user as persona to a question set
// @Description Links a user as a persona to a question set (practice)
// @Tags question-sets
// @Accept json
// @Produce json
// @Param setId path int true "Question Set ID"
// @Param body body addUserPersonaReq true "Add user persona payload"
// @Success 201 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/question-sets/{setId}/personas [post]
func (h *Handler) AddUserPersonaToQuestionSet(c *fiber.Ctx) error {
	setIDStr := c.Params("setId")
	setID, err := strconv.ParseInt(setIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid set ID",
			Error:   err.Error(),
		})
	}

	var req addUserPersonaReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	err = h.questionsSvc.AddUserPersonaToQuestionSet(c.Context(), setID, req.UserID, req.DisplayOrder)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to add persona to question set",
			Error:   err.Error(),
		})
	}

	return c.Status(fiber.StatusCreated).JSON(domain.Response{
		Message: "Persona added to question set successfully",
	})
}

// RemoveUserPersonaFromQuestionSet godoc
// @Summary Remove a user persona from a question set
// @Description Unlinks a user as persona from a question set (practice)
// @Tags question-sets
// @Produce json
// @Param setId path int true "Question Set ID"
// @Param userId path int true "User ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/question-sets/{setId}/personas/{userId} [delete]
func (h *Handler) RemoveUserPersonaFromQuestionSet(c *fiber.Ctx) error {
	setIDStr := c.Params("setId")
	setID, err := strconv.ParseInt(setIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid set ID",
			Error:   err.Error(),
		})
	}

	userIDStr := c.Params("userId")
	userID, err := strconv.ParseInt(userIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid user ID",
			Error:   err.Error(),
		})
	}

	err = h.questionsSvc.RemoveUserPersonaFromQuestionSet(c.Context(), setID, userID)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to remove persona from question set",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Persona removed from question set successfully",
	})
}
