package handlers

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

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

// Course Category Handlers

type createCourseCategoryReq struct {
	Name string `json:"name" validate:"required"`
}

type courseCategoryRes struct {
	ID        int64  `json:"id"`
	Name      string `json:"name"`
	IsActive  bool   `json:"is_active"`
	CreatedAt string `json:"created_at"`
}

// CreateCourseCategory godoc
// @Summary Create a new course category
// @Description Creates a new course category with the provided name
// @Tags course-categories
// @Accept json
// @Produce json
// @Param body body createCourseCategoryReq true "Create category payload"
// @Success 201 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/categories [post]
func (h *Handler) CreateCourseCategory(c *fiber.Ctx) error {
	var req createCourseCategoryReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	category, err := h.courseMgmtSvc.CreateCourseCategory(c.Context(), req.Name)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to create course category",
			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{}{"name": category.Name})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionCategoryCreated, domain.ResourceCategory, &category.ID, "Created course category: "+category.Name, meta, &ip, &ua)

	return c.Status(fiber.StatusCreated).JSON(domain.Response{
		Message: "Course category created successfully",
		Data: courseCategoryRes{
			ID:        category.ID,
			Name:      category.Name,
			IsActive:  category.IsActive,
			CreatedAt: category.CreatedAt.String(),
		},
	})
}

// GetCourseCategoryByID godoc
// @Summary Get course category by ID
// @Description Returns a single course category by its ID
// @Tags course-categories
// @Produce json
// @Param id path int true "Category ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 404 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/categories/{id} [get]
func (h *Handler) GetCourseCategoryByID(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 category ID",
			Error:   err.Error(),
		})
	}

	category, err := h.courseMgmtSvc.GetCourseCategoryByID(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
			Message: "Course category not found",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Course category retrieved successfully",
		Data: courseCategoryRes{
			ID:        category.ID,
			Name:      category.Name,
			IsActive:  category.IsActive,
			CreatedAt: category.CreatedAt.String(),
		},
	})
}

type getAllCourseCategoriesRes struct {
	Categories []courseCategoryRes `json:"categories"`
	TotalCount int64               `json:"total_count"`
}

// GetAllCourseCategories godoc
// @Summary Get all course categories
// @Description Returns a paginated list of all course categories
// @Tags course-categories
// @Produce json
// @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/course-management/categories [get]
func (h *Handler) GetAllCourseCategories(c *fiber.Ctx) error {
	limitStr := c.Query("limit", "10")
	offsetStr := c.Query("offset", "0")

	limit, err := strconv.Atoi(limitStr)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid limit parameter",
			Error:   err.Error(),
		})
	}

	offset, err := strconv.Atoi(offsetStr)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid offset parameter",
			Error:   err.Error(),
		})
	}

	categories, totalCount, err := h.courseMgmtSvc.GetAllCourseCategories(c.Context(), int32(limit), int32(offset))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to retrieve course categories",
			Error:   err.Error(),
		})
	}

	var categoryResponses []courseCategoryRes
	for _, category := range categories {
		categoryResponses = append(categoryResponses, courseCategoryRes{
			ID:        category.ID,
			Name:      category.Name,
			IsActive:  category.IsActive,
			CreatedAt: category.CreatedAt.String(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Course categories retrieved successfully",
		Data: getAllCourseCategoriesRes{
			Categories: categoryResponses,
			TotalCount: totalCount,
		},
	})
}

type updateCourseCategoryReq struct {
	Name     *string `json:"name"`
	IsActive *bool   `json:"is_active"`
}

// UpdateCourseCategory godoc
// @Summary Update course category
// @Description Updates a course category's name and/or active status
// @Tags course-categories
// @Accept json
// @Produce json
// @Param id path int true "Category ID"
// @Param body body updateCourseCategoryReq true "Update category payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/categories/{id} [put]
func (h *Handler) UpdateCourseCategory(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 category ID",
			Error:   err.Error(),
		})
	}

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

	err = h.courseMgmtSvc.UpdateCourseCategory(c.Context(), id, req.Name, req.IsActive)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to update course category",
			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{}{"id": id, "name": req.Name, "is_active": req.IsActive})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionCategoryUpdated, domain.ResourceCategory, &id, fmt.Sprintf("Updated course category ID: %d", id), meta, &ip, &ua)

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

// DeleteCourseCategory godoc
// @Summary Delete course category
// @Description Deletes a course category by its ID
// @Tags course-categories
// @Produce json
// @Param id path int true "Category ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/categories/{id} [delete]
func (h *Handler) DeleteCourseCategory(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 category ID",
			Error:   err.Error(),
		})
	}

	err = h.courseMgmtSvc.DeleteCourseCategory(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to delete course category",
			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{}{"id": id})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionCategoryDeleted, domain.ResourceCategory, &id, fmt.Sprintf("Deleted category ID: %d", id), meta, &ip, &ua)

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

// Course Handlers

type createCourseReq struct {
	CategoryID  int64   `json:"category_id" validate:"required"`
	Title       string  `json:"title" validate:"required"`
	Description *string `json:"description"`
	Thumbnail   *string `json:"thumbnail"`
}

type courseRes struct {
	ID          int64   `json:"id"`
	CategoryID  int64   `json:"category_id"`
	Title       string  `json:"title"`
	Description *string `json:"description"`
	Thumbnail   *string `json:"thumbnail"`
	IsActive    bool    `json:"is_active"`
}

// CreateCourse godoc
// @Summary Create a new course
// @Description Creates a new course under a specific category
// @Tags courses
// @Accept json
// @Produce json
// @Param body body createCourseReq true "Create course payload"
// @Success 201 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/courses [post]
func (h *Handler) CreateCourse(c *fiber.Ctx) error {
	var req createCourseReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	course, err := h.courseMgmtSvc.CreateCourse(c.Context(), req.CategoryID, req.Title, req.Description, req.Thumbnail)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to create course",
			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{}{"title": course.Title, "category_id": course.CategoryID})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionCourseCreated, domain.ResourceCourse, &course.ID, "Created course: "+course.Title, meta, &ip, &ua)

	go func() {
		students, _, err := h.userSvc.GetAllUsers(context.Background(), domain.UserFilter{Role: string(domain.RoleStudent)})
		if err != nil {
			return
		}
		for _, s := range students {
			h.sendInAppNotification(s.ID, domain.NOTIFICATION_TYPE_COURSE_CREATED, "New Course Available", "A new course \""+course.Title+"\" has been added. Check it out!")
		}
	}()

	return c.Status(fiber.StatusCreated).JSON(domain.Response{
		Message: "Course created successfully",
		Data: courseRes{
			ID:          course.ID,
			CategoryID:  course.CategoryID,
			Title:       course.Title,
			Description: course.Description,
			Thumbnail:   course.Thumbnail,
			IsActive:    course.IsActive,
		},
	})
}

// GetCourseByID godoc
// @Summary Get course by ID
// @Description Returns a single course by its ID
// @Tags courses
// @Produce json
// @Param id path int true "Course ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 404 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/courses/{id} [get]
func (h *Handler) GetCourseByID(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 course ID",
			Error:   err.Error(),
		})
	}

	course, err := h.courseMgmtSvc.GetCourseByID(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
			Message: "Course not found",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Course retrieved successfully",
		Data: courseRes{
			ID:          course.ID,
			CategoryID:  course.CategoryID,
			Title:       course.Title,
			Description: course.Description,
			Thumbnail:   course.Thumbnail,
			IsActive:    course.IsActive,
		},
	})
}

type getCoursesByCategoryRes struct {
	Courses    []courseRes `json:"courses"`
	TotalCount int64       `json:"total_count"`
}

// GetCoursesByCategory godoc
// @Summary Get courses by category
// @Description Returns a paginated list of courses under a specific category
// @Tags courses
// @Produce json
// @Param categoryId path int true "Category ID"
// @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/course-management/categories/{categoryId}/courses [get]
func (h *Handler) GetCoursesByCategory(c *fiber.Ctx) error {
	categoryIDStr := c.Params("categoryId")
	categoryID, err := strconv.ParseInt(categoryIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid category ID",
			Error:   err.Error(),
		})
	}

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

	limit, err := strconv.Atoi(limitStr)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid limit parameter",
			Error:   err.Error(),
		})
	}

	offset, err := strconv.Atoi(offsetStr)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid offset parameter",
			Error:   err.Error(),
		})
	}

	courses, totalCount, err := h.courseMgmtSvc.GetCoursesByCategory(c.Context(), categoryID, int32(limit), int32(offset))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to retrieve courses",
			Error:   err.Error(),
		})
	}

	var courseResponses []courseRes
	for _, course := range courses {
		courseResponses = append(courseResponses, courseRes{
			ID:          course.ID,
			CategoryID:  course.CategoryID,
			Title:       course.Title,
			Description: course.Description,
			Thumbnail:   course.Thumbnail,
			IsActive:    course.IsActive,
		})
	}

	return c.JSON(domain.Response{
		Message: "Courses retrieved successfully",
		Data: getCoursesByCategoryRes{
			Courses:    courseResponses,
			TotalCount: totalCount,
		},
	})
}

type updateCourseReq struct {
	Title       *string `json:"title"`
	Description *string `json:"description"`
	Thumbnail   *string `json:"thumbnail"`
	IsActive    *bool   `json:"is_active"`
}

// UpdateCourse godoc
// @Summary Update course
// @Description Updates a course's title, description, and/or active status
// @Tags courses
// @Accept json
// @Produce json
// @Param id path int true "Course ID"
// @Param body body updateCourseReq true "Update course payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/courses/{id} [put]
func (h *Handler) UpdateCourse(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 course ID",
			Error:   err.Error(),
		})
	}

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

	err = h.courseMgmtSvc.UpdateCourse(c.Context(), id, req.Title, req.Description, req.Thumbnail, req.IsActive)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to update course",
			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{}{"id": id, "title": req.Title, "description": req.Description, "thumbnail": req.Thumbnail, "is_active": req.IsActive})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionCourseUpdated, domain.ResourceCourse, &id, fmt.Sprintf("Updated course ID: %d", id), meta, &ip, &ua)

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

// DeleteCourse godoc
// @Summary Delete course
// @Description Deletes a course by its ID
// @Tags courses
// @Produce json
// @Param id path int true "Course ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/courses/{id} [delete]
func (h *Handler) DeleteCourse(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 course ID",
			Error:   err.Error(),
		})
	}

	err = h.courseMgmtSvc.DeleteCourse(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to delete course",
			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{}{"id": id})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionCourseDeleted, domain.ResourceCourse, &id, fmt.Sprintf("Deleted course ID: %d", id), meta, &ip, &ua)

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

// Sub-course Handlers

type createSubCourseReq struct {
	CourseID     int64   `json:"course_id" validate:"required"`
	Title        string  `json:"title" validate:"required"`
	Description  *string `json:"description"`
	Thumbnail    *string `json:"thumbnail"`
	DisplayOrder *int32  `json:"display_order"`
	Level        string  `json:"level" validate:"required"` // BEGINNER, INTERMEDIATE, ADVANCED
}

type subCourseRes struct {
	ID           int64   `json:"id"`
	CourseID     int64   `json:"course_id"`
	Title        string  `json:"title"`
	Description  *string `json:"description"`
	Thumbnail    *string `json:"thumbnail"`
	DisplayOrder int32   `json:"display_order"`
	Level        string  `json:"level"`
	IsActive     bool    `json:"is_active"`
}

// CreateSubCourse godoc
// @Summary Create a new sub-course
// @Description Creates a new sub-course under a specific course
// @Tags sub-courses
// @Accept json
// @Produce json
// @Param body body createSubCourseReq true "Create sub-course payload"
// @Success 201 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/sub-courses [post]
func (h *Handler) CreateSubCourse(c *fiber.Ctx) error {
	var req createSubCourseReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	subCourse, err := h.courseMgmtSvc.CreateSubCourse(c.Context(), req.CourseID, req.Title, req.Description, req.Thumbnail, req.DisplayOrder, req.Level)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to create sub-course",
			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{}{"title": subCourse.Title, "course_id": subCourse.CourseID, "level": subCourse.Level})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionSubCourseCreated, domain.ResourceSubCourse, &subCourse.ID, "Created sub-course: "+subCourse.Title, meta, &ip, &ua)

	go func() {
		students, _, err := h.userSvc.GetAllUsers(context.Background(), domain.UserFilter{Role: string(domain.RoleStudent)})
		if err != nil {
			return
		}
		for _, s := range students {
			h.sendInAppNotification(s.ID, domain.NOTIFICATION_TYPE_SUB_COURSE_CREATED, "New Content Available", "A new sub-course \""+subCourse.Title+"\" has been added.")
		}
	}()

	return c.Status(fiber.StatusCreated).JSON(domain.Response{
		Message: "Sub-course created successfully",
		Data: subCourseRes{
			ID:           subCourse.ID,
			CourseID:     subCourse.CourseID,
			Title:        subCourse.Title,
			Description:  subCourse.Description,
			Thumbnail:    subCourse.Thumbnail,
			DisplayOrder: subCourse.DisplayOrder,
			Level:        subCourse.Level,
			IsActive:     subCourse.IsActive,
		},
	})
}

// GetSubCourseByID godoc
// @Summary Get sub-course by ID
// @Description Returns a single sub-course by its ID
// @Tags sub-courses
// @Produce json
// @Param id path int true "Sub-course ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 404 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/sub-courses/{id} [get]
func (h *Handler) GetSubCourseByID(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 sub-course ID",
			Error:   err.Error(),
		})
	}

	subCourse, err := h.courseMgmtSvc.GetSubCourseByID(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
			Message: "Sub-course not found",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Sub-course retrieved successfully",
		Data: subCourseRes{
			ID:           subCourse.ID,
			CourseID:     subCourse.CourseID,
			Title:        subCourse.Title,
			Description:  subCourse.Description,
			Thumbnail:    subCourse.Thumbnail,
			DisplayOrder: subCourse.DisplayOrder,
			Level:        subCourse.Level,
			IsActive:     subCourse.IsActive,
		},
	})
}

type getSubCoursesByCourseRes struct {
	SubCourses []subCourseRes `json:"sub_courses"`
	TotalCount int64          `json:"total_count"`
}

// GetSubCoursesByCourse godoc
// @Summary Get sub-courses by course
// @Description Returns all sub-courses under a specific course
// @Tags sub-courses
// @Produce json
// @Param courseId path int true "Course ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/courses/{courseId}/sub-courses [get]
func (h *Handler) GetSubCoursesByCourse(c *fiber.Ctx) error {
	courseIDStr := c.Params("courseId")
	courseID, err := strconv.ParseInt(courseIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid course ID",
			Error:   err.Error(),
		})
	}

	subCourses, totalCount, err := h.courseMgmtSvc.GetSubCoursesByCourse(c.Context(), courseID)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to retrieve sub-courses",
			Error:   err.Error(),
		})
	}

	var subCourseResponses []subCourseRes
	for _, sc := range subCourses {
		subCourseResponses = append(subCourseResponses, subCourseRes{
			ID:           sc.ID,
			CourseID:     sc.CourseID,
			Title:        sc.Title,
			Description:  sc.Description,
			Thumbnail:    sc.Thumbnail,
			DisplayOrder: sc.DisplayOrder,
			Level:        sc.Level,
			IsActive:     sc.IsActive,
		})
	}

	return c.JSON(domain.Response{
		Message: "Sub-courses retrieved successfully",
		Data: getSubCoursesByCourseRes{
			SubCourses: subCourseResponses,
			TotalCount: totalCount,
		},
	})
}

// ListSubCoursesByCourse godoc
// @Summary List active sub-courses by course
// @Description Returns a list of active sub-courses under a specific course
// @Tags sub-courses
// @Produce json
// @Param courseId path int true "Course ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/courses/{courseId}/sub-courses/list [get]
func (h *Handler) ListSubCoursesByCourse(c *fiber.Ctx) error {
	courseIDStr := c.Params("courseId")
	courseID, err := strconv.ParseInt(courseIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid course ID",
			Error:   err.Error(),
		})
	}

	subCourses, err := h.courseMgmtSvc.ListSubCoursesByCourse(c.Context(), courseID)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to retrieve sub-courses",
			Error:   err.Error(),
		})
	}

	var subCourseResponses []subCourseRes
	for _, sc := range subCourses {
		subCourseResponses = append(subCourseResponses, subCourseRes{
			ID:           sc.ID,
			CourseID:     sc.CourseID,
			Title:        sc.Title,
			Description:  sc.Description,
			Thumbnail:    sc.Thumbnail,
			DisplayOrder: sc.DisplayOrder,
			Level:        sc.Level,
			IsActive:     sc.IsActive,
		})
	}

	return c.JSON(domain.Response{
		Message: "Sub-courses retrieved successfully",
		Data:    subCourseResponses,
	})
}

// ListActiveSubCourses godoc
// @Summary List all active sub-courses
// @Description Returns a list of all active sub-courses
// @Tags sub-courses
// @Produce json
// @Success 200 {object} domain.Response
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/sub-courses/active [get]
func (h *Handler) ListActiveSubCourses(c *fiber.Ctx) error {
	subCourses, err := h.courseMgmtSvc.ListActiveSubCourses(c.Context())
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to retrieve active sub-courses",
			Error:   err.Error(),
		})
	}

	var subCourseResponses []subCourseRes
	for _, sc := range subCourses {
		subCourseResponses = append(subCourseResponses, subCourseRes{
			ID:           sc.ID,
			CourseID:     sc.CourseID,
			Title:        sc.Title,
			Description:  sc.Description,
			Thumbnail:    sc.Thumbnail,
			DisplayOrder: sc.DisplayOrder,
			Level:        sc.Level,
			IsActive:     sc.IsActive,
		})
	}

	return c.JSON(domain.Response{
		Message: "Active sub-courses retrieved successfully",
		Data:    subCourseResponses,
	})
}

type updateSubCourseReq struct {
	Title        *string `json:"title"`
	Description  *string `json:"description"`
	Thumbnail    *string `json:"thumbnail"`
	DisplayOrder *int32  `json:"display_order"`
	Level        *string `json:"level"`
	IsActive     *bool   `json:"is_active"`
}

// UpdateSubCourse godoc
// @Summary Update sub-course
// @Description Updates a sub-course's fields
// @Tags sub-courses
// @Accept json
// @Produce json
// @Param id path int true "Sub-course ID"
// @Param body body updateSubCourseReq true "Update sub-course payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/sub-courses/{id} [patch]
func (h *Handler) UpdateSubCourse(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 sub-course ID",
			Error:   err.Error(),
		})
	}

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

	err = h.courseMgmtSvc.UpdateSubCourse(c.Context(), id, req.Title, req.Description, req.Thumbnail, req.DisplayOrder, req.Level, req.IsActive)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to update sub-course",
			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{}{"id": id, "title": req.Title, "description": req.Description, "level": req.Level, "is_active": req.IsActive})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionSubCourseUpdated, domain.ResourceSubCourse, &id, fmt.Sprintf("Updated sub-course ID: %d", id), meta, &ip, &ua)

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

// DeactivateSubCourse godoc
// @Summary Deactivate sub-course
// @Description Deactivates a sub-course by its ID
// @Tags sub-courses
// @Produce json
// @Param id path int true "Sub-course ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/sub-courses/{id}/deactivate [put]
func (h *Handler) DeactivateSubCourse(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 sub-course ID",
			Error:   err.Error(),
		})
	}

	err = h.courseMgmtSvc.DeactivateSubCourse(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to deactivate sub-course",
			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{}{"id": id})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionSubCourseDeactivated, domain.ResourceSubCourse, &id, fmt.Sprintf("Deactivated sub-course ID: %d", id), meta, &ip, &ua)

	return c.JSON(domain.Response{
		Message: "Sub-course deactivated successfully",
	})
}

// DeleteSubCourse godoc
// @Summary Delete sub-course
// @Description Deletes a sub-course by its ID
// @Tags sub-courses
// @Produce json
// @Param id path int true "Sub-course ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/sub-courses/{id} [delete]
func (h *Handler) DeleteSubCourse(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 sub-course ID",
			Error:   err.Error(),
		})
	}

	_, err = h.courseMgmtSvc.DeleteSubCourse(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to delete sub-course",
			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{}{"id": id})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionSubCourseDeleted, domain.ResourceSubCourse, &id, fmt.Sprintf("Deleted sub-course ID: %d", id), meta, &ip, &ua)

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

// Sub-course Video Handlers

type createSubCourseVideoReq struct {
	SubCourseID  int64   `json:"sub_course_id" validate:"required"`
	Title        string  `json:"title" validate:"required"`
	Description  *string `json:"description"`
	VideoURL     string  `json:"video_url" validate:"required"`
	Duration     int32   `json:"duration" validate:"required"`
	Resolution   *string `json:"resolution"`
	InstructorID *string `json:"instructor_id"`
	Thumbnail    *string `json:"thumbnail"`
	Visibility   *string `json:"visibility"`
	DisplayOrder *int32  `json:"display_order"`
	Status       *string `json:"status"` // DRAFT, PUBLISHED, INACTIVE, ARCHIVED
}

type subCourseVideoRes struct {
	ID              int64   `json:"id"`
	SubCourseID     int64   `json:"sub_course_id"`
	Title           string  `json:"title"`
	Description     *string `json:"description"`
	VideoURL        string  `json:"video_url"`
	Duration        int32   `json:"duration"`
	Resolution      *string `json:"resolution"`
	InstructorID    *string `json:"instructor_id"`
	Thumbnail       *string `json:"thumbnail"`
	Visibility      *string `json:"visibility"`
	DisplayOrder    int32   `json:"display_order"`
	IsPublished     bool    `json:"is_published"`
	PublishDate     *string `json:"publish_date"`
	Status          string  `json:"status"`
	VimeoID         *string `json:"vimeo_id,omitempty"`
	VimeoEmbedURL   *string `json:"vimeo_embed_url,omitempty"`
	VimeoPlayerHTML *string `json:"vimeo_player_html,omitempty"`
	VimeoStatus     *string `json:"vimeo_status,omitempty"`
}

type createVimeoVideoReq struct {
	SubCourseID  int64   `json:"sub_course_id" validate:"required"`
	Title        string  `json:"title" validate:"required"`
	Description  *string `json:"description"`
	SourceURL    string  `json:"source_url" validate:"required,url"`
	FileSize     int64   `json:"file_size" validate:"required,gt=0"`
	Duration     int32   `json:"duration"`
	Resolution   *string `json:"resolution"`
	InstructorID *string `json:"instructor_id"`
	Thumbnail    *string `json:"thumbnail"`
	Visibility   *string `json:"visibility"`
	DisplayOrder *int32  `json:"display_order"`
}

type createVideoFromVimeoIDReq struct {
	SubCourseID  int64   `json:"sub_course_id" validate:"required"`
	VimeoVideoID string  `json:"vimeo_video_id" validate:"required"`
	Title        string  `json:"title" validate:"required"`
	Description  *string `json:"description"`
	DisplayOrder *int32  `json:"display_order"`
	InstructorID *string `json:"instructor_id"`
}

// CreateSubCourseVideo godoc
// @Summary Create a new sub-course video
// @Description Creates a new video under a specific sub-course
// @Tags sub-course-videos
// @Accept json
// @Produce json
// @Param body body createSubCourseVideoReq true "Create video payload"
// @Success 201 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/videos [post]
func (h *Handler) CreateSubCourseVideo(c *fiber.Ctx) error {
	var req createSubCourseVideoReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	video, err := h.courseMgmtSvc.CreateSubCourseVideo(c.Context(), req.SubCourseID, req.Title, req.Description, req.VideoURL, req.Duration, req.Resolution, req.InstructorID, req.Thumbnail, req.Visibility, req.DisplayOrder, req.Status)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to create sub-course video",
			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{}{"title": video.Title, "sub_course_id": video.SubCourseID})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionVideoCreated, domain.ResourceVideo, &video.ID, "Created video: "+video.Title, meta, &ip, &ua)

	go func() {
		students, _, err := h.userSvc.GetAllUsers(context.Background(), domain.UserFilter{Role: string(domain.RoleStudent)})
		if err != nil {
			return
		}
		for _, s := range students {
			h.sendInAppNotification(s.ID, domain.NOTIFICATION_TYPE_VIDEO_ADDED, "New Video Available", "A new video \""+req.Title+"\" has been added.")
		}
	}()

	var publishDate *string
	if video.PublishDate != nil {
		pd := video.PublishDate.String()
		publishDate = &pd
	}

	return c.Status(fiber.StatusCreated).JSON(domain.Response{
		Message: "Sub-course video created successfully",
		Data: subCourseVideoRes{
			ID:           video.ID,
			SubCourseID:  video.SubCourseID,
			Title:        video.Title,
			Description:  video.Description,
			VideoURL:     video.VideoURL,
			Duration:     video.Duration,
			Resolution:   video.Resolution,
			InstructorID: video.InstructorID,
			Thumbnail:    video.Thumbnail,
			Visibility:   video.Visibility,
			DisplayOrder: video.DisplayOrder,
			IsPublished:  video.IsPublished,
			PublishDate:  publishDate,
			Status:       video.Status,
		},
	})
}

// GetSubCourseVideoByID godoc
// @Summary Get sub-course video by ID
// @Description Returns a single video by its ID
// @Tags sub-course-videos
// @Produce json
// @Param id path int true "Video ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 404 {object} domain.ErrorResponse
// @Router /api/v1/course-management/videos/{id} [get]
func (h *Handler) GetSubCourseVideoByID(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 video ID",
			Error:   err.Error(),
		})
	}

	video, err := h.courseMgmtSvc.GetSubCourseVideoByID(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
			Message: "Video not found",
			Error:   err.Error(),
		})
	}

	var publishDate *string
	if video.PublishDate != nil {
		pd := video.PublishDate.String()
		publishDate = &pd
	}

	return c.JSON(domain.Response{
		Message: "Video retrieved successfully",
		Data: subCourseVideoRes{
			ID:           video.ID,
			SubCourseID:  video.SubCourseID,
			Title:        video.Title,
			Description:  video.Description,
			VideoURL:     video.VideoURL,
			Duration:     video.Duration,
			Resolution:   video.Resolution,
			InstructorID: video.InstructorID,
			Thumbnail:    video.Thumbnail,
			Visibility:   video.Visibility,
			DisplayOrder: video.DisplayOrder,
			IsPublished:  video.IsPublished,
			PublishDate:  publishDate,
			Status:       video.Status,
		},
	})
}

type getVideosBySubCourseRes struct {
	Videos     []subCourseVideoRes `json:"videos"`
	TotalCount int64               `json:"total_count"`
}

// GetVideosBySubCourse godoc
// @Summary Get videos by sub-course
// @Description Returns all videos under a specific sub-course
// @Tags sub-course-videos
// @Produce json
// @Param subCourseId path int true "Sub-course ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/sub-courses/{subCourseId}/videos [get]
func (h *Handler) GetVideosBySubCourse(c *fiber.Ctx) error {
	subCourseIDStr := c.Params("subCourseId")
	subCourseID, err := strconv.ParseInt(subCourseIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid sub-course ID",
			Error:   err.Error(),
		})
	}

	videos, totalCount, err := h.courseMgmtSvc.GetVideosBySubCourse(c.Context(), subCourseID)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to retrieve videos",
			Error:   err.Error(),
		})
	}

	var videoResponses []subCourseVideoRes
	for _, v := range videos {
		var publishDate *string
		if v.PublishDate != nil {
			pd := v.PublishDate.String()
			publishDate = &pd
		}

		videoResponses = append(videoResponses, subCourseVideoRes{
			ID:           v.ID,
			SubCourseID:  v.SubCourseID,
			Title:        v.Title,
			Description:  v.Description,
			VideoURL:     v.VideoURL,
			Duration:     v.Duration,
			Resolution:   v.Resolution,
			InstructorID: v.InstructorID,
			Thumbnail:    v.Thumbnail,
			Visibility:   v.Visibility,
			DisplayOrder: v.DisplayOrder,
			IsPublished:  v.IsPublished,
			PublishDate:  publishDate,
			Status:       v.Status,
		})
	}

	return c.JSON(domain.Response{
		Message: "Videos retrieved successfully",
		Data: getVideosBySubCourseRes{
			Videos:     videoResponses,
			TotalCount: totalCount,
		},
	})
}

// GetPublishedVideosBySubCourse godoc
// @Summary Get published videos by sub-course
// @Description Returns all published videos under a specific sub-course
// @Tags sub-course-videos
// @Produce json
// @Param subCourseId path int true "Sub-course ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/sub-courses/{subCourseId}/videos/published [get]
func (h *Handler) GetPublishedVideosBySubCourse(c *fiber.Ctx) error {
	subCourseIDStr := c.Params("subCourseId")
	subCourseID, err := strconv.ParseInt(subCourseIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid sub-course ID",
			Error:   err.Error(),
		})
	}

	videos, err := h.courseMgmtSvc.GetPublishedVideosBySubCourse(c.Context(), subCourseID)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to retrieve published videos",
			Error:   err.Error(),
		})
	}

	var videoResponses []subCourseVideoRes
	for _, v := range videos {
		var publishDate *string
		if v.PublishDate != nil {
			pd := v.PublishDate.String()
			publishDate = &pd
		}

		videoResponses = append(videoResponses, subCourseVideoRes{
			ID:           v.ID,
			SubCourseID:  v.SubCourseID,
			Title:        v.Title,
			Description:  v.Description,
			VideoURL:     v.VideoURL,
			Duration:     v.Duration,
			Resolution:   v.Resolution,
			InstructorID: v.InstructorID,
			Thumbnail:    v.Thumbnail,
			Visibility:   v.Visibility,
			DisplayOrder: v.DisplayOrder,
			IsPublished:  v.IsPublished,
			PublishDate:  publishDate,
			Status:       v.Status,
		})
	}

	return c.JSON(domain.Response{
		Message: "Published videos retrieved successfully",
		Data:    videoResponses,
	})
}

// PublishSubCourseVideo godoc
// @Summary Publish sub-course video
// @Description Publishes a video by its ID
// @Tags sub-course-videos
// @Produce json
// @Param id path int true "Video ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/videos/{id}/publish [put]
func (h *Handler) PublishSubCourseVideo(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 video ID",
			Error:   err.Error(),
		})
	}

	err = h.courseMgmtSvc.PublishSubCourseVideo(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to publish video",
			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{}{"id": id})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionVideoPublished, domain.ResourceVideo, &id, fmt.Sprintf("Published video ID: %d", id), meta, &ip, &ua)

	return c.JSON(domain.Response{
		Message: "Video published successfully",
	})
}

type updateSubCourseVideoReq struct {
	Title        *string `json:"title"`
	Description  *string `json:"description"`
	VideoURL     *string `json:"video_url"`
	Duration     *int32  `json:"duration"`
	Resolution   *string `json:"resolution"`
	Visibility   *string `json:"visibility"`
	Thumbnail    *string `json:"thumbnail"`
	DisplayOrder *int32  `json:"display_order"`
	Status       *string `json:"status"` // DRAFT, PUBLISHED, INACTIVE, ARCHIVED
}

// UpdateSubCourseVideo godoc
// @Summary Update sub-course video
// @Description Updates a video's fields
// @Tags sub-course-videos
// @Accept json
// @Produce json
// @Param id path int true "Video ID"
// @Param body body updateSubCourseVideoReq true "Update video payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/videos/{id} [put]
func (h *Handler) UpdateSubCourseVideo(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 video ID",
			Error:   err.Error(),
		})
	}

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

	err = h.courseMgmtSvc.UpdateSubCourseVideo(c.Context(), id, req.Title, req.Description, req.VideoURL, req.Duration, req.Resolution, req.Visibility, req.Thumbnail, req.DisplayOrder, req.Status)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to update video",
			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{}{"id": id, "title": req.Title})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionVideoUpdated, domain.ResourceVideo, &id, fmt.Sprintf("Updated video ID: %d", id), meta, &ip, &ua)

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

// DeleteSubCourseVideo godoc
// @Summary Delete sub-course video
// @Description Archives a video by its ID (soft delete)
// @Tags sub-course-videos
// @Produce json
// @Param id path int true "Video ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/videos/{id} [delete]
func (h *Handler) DeleteSubCourseVideo(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 video ID",
			Error:   err.Error(),
		})
	}

	err = h.courseMgmtSvc.ArchiveSubCourseVideo(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to delete video",
			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{}{"id": id})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionVideoArchived, domain.ResourceVideo, &id, fmt.Sprintf("Archived video ID: %d", id), meta, &ip, &ua)

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

// NOTE: Practice and Practice Question handlers have been removed.
// Use the unified questions system at /api/v1/questions and /api/v1/question-sets instead.
// Create a question set with set_type="PRACTICE" and owner_type="SUB_COURSE" to replace practices.

// Learning Tree Handler

// GetFullLearningTree godoc
// @Summary Get full learning tree
// @Description Returns the complete learning tree structure with courses and sub-courses
// @Tags learning-tree
// @Produce json
// @Success 200 {object} domain.Response
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/learning-tree [get]
func (h *Handler) GetFullLearningTree(c *fiber.Ctx) error {
	courses, err := h.courseMgmtSvc.GetFullLearningTree(c.Context())
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to retrieve learning tree",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Learning tree retrieved successfully",
		Data:    courses,
	})
}

// UploadSubCourseVideo godoc
// @Summary Upload a video file and create sub-course video
// @Description Accepts a video file upload, uploads it to Vimeo via TUS, and creates a sub-course video record
// @Tags sub-course-videos
// @Accept multipart/form-data
// @Produce json
// @Param file formData file true "Video file"
// @Param sub_course_id formData int true "Sub-course ID"
// @Param title formData string true "Video title"
// @Param description formData string false "Video description"
// @Param duration formData int false "Duration in seconds"
// @Param resolution formData string false "Video resolution"
// @Param instructor_id formData string false "Instructor ID"
// @Param thumbnail formData string false "Thumbnail URL"
// @Param visibility formData string false "Visibility"
// @Param display_order formData int false "Display order"
// @Success 201 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/videos/upload [post]
func (h *Handler) UploadSubCourseVideo(c *fiber.Ctx) error {
	subCourseIDStr := c.FormValue("sub_course_id")
	if subCourseIDStr == "" {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "sub_course_id is required",
			Error:   "sub_course_id form field is empty",
		})
	}
	subCourseID, err := strconv.ParseInt(subCourseIDStr, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid sub_course_id",
			Error:   err.Error(),
		})
	}

	title := c.FormValue("title")
	if title == "" {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "title is required",
			Error:   "title form field is empty",
		})
	}

	fileHeader, err := c.FormFile("file")
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Video file is required",
			Error:   err.Error(),
		})
	}

	const maxSize = 500 * 1024 * 1024 // 500 MB
	if fileHeader.Size > maxSize {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "File too large",
			Error:   "Video file must be <= 500MB",
		})
	}

	file, err := fileHeader.Open()
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to read uploaded file",
			Error:   err.Error(),
		})
	}
	defer file.Close()

	var description *string
	if desc := c.FormValue("description"); desc != "" {
		description = &desc
	}

	var duration int32
	if durStr := c.FormValue("duration"); durStr != "" {
		durVal, err := strconv.ParseInt(durStr, 10, 32)
		if err == nil {
			duration = int32(durVal)
		}
	}

	var resolution *string
	if res := c.FormValue("resolution"); res != "" {
		resolution = &res
	}

	var instructorID *string
	if iid := c.FormValue("instructor_id"); iid != "" {
		instructorID = &iid
	}

	var thumbnail *string
	if thumb := c.FormValue("thumbnail"); thumb != "" {
		thumbnail = &thumb
	}

	var visibility *string
	if vis := c.FormValue("visibility"); vis != "" {
		visibility = &vis
	}

	var displayOrder *int32
	if doStr := c.FormValue("display_order"); doStr != "" {
		doVal, err := strconv.ParseInt(doStr, 10, 32)
		if err == nil {
			do := int32(doVal)
			displayOrder = &do
		}
	}

	video, err := h.courseMgmtSvc.CreateSubCourseVideoWithFileUpload(
		c.Context(), subCourseID, title, description,
		fileHeader.Filename, file, fileHeader.Size, duration, resolution,
		instructorID, thumbnail, visibility, displayOrder,
	)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to upload video",
			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{}{"title": video.Title, "sub_course_id": subCourseID})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionVideoUploaded, domain.ResourceVideo, &video.ID, "Uploaded video: "+video.Title, meta, &ip, &ua)

	return c.Status(fiber.StatusCreated).JSON(domain.Response{
		Message: "Video uploaded and created successfully",
		Data:    mapVideoToResponse(video),
		Success: true,
	})
}

// CreateSubCourseVideoWithVimeo godoc
// @Summary Create a new sub-course video with Vimeo upload
// @Description Creates a video by uploading to Vimeo from a source URL
// @Tags sub-course-videos
// @Accept json
// @Produce json
// @Param body body createVimeoVideoReq true "Create Vimeo video payload"
// @Success 201 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/videos/vimeo [post]
func (h *Handler) CreateSubCourseVideoWithVimeo(c *fiber.Ctx) error {
	var req createVimeoVideoReq
	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:   fmt.Sprintf("%v", valErrs),
		})
	}

	video, err := h.courseMgmtSvc.CreateSubCourseVideoWithVimeo(
		c.Context(), req.SubCourseID, req.Title, req.Description,
		req.SourceURL, req.FileSize, req.Duration, req.Resolution,
		req.InstructorID, req.Thumbnail, req.Visibility, req.DisplayOrder,
	)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to create video with Vimeo",
			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{}{"title": video.Title, "sub_course_id": video.SubCourseID})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionVideoCreated, domain.ResourceVideo, &video.ID, "Created video with Vimeo: "+video.Title, meta, &ip, &ua)

	return c.Status(fiber.StatusCreated).JSON(domain.Response{
		Message: "Video created and uploaded to Vimeo successfully",
		Data:    mapVideoToResponse(video),
		Success: true,
	})
}

// CreateSubCourseVideoFromVimeoID godoc
// @Summary Create a sub-course video from existing Vimeo video
// @Description Creates a video record from an existing Vimeo video ID
// @Tags sub-course-videos
// @Accept json
// @Produce json
// @Param body body createVideoFromVimeoIDReq true "Create from Vimeo ID payload"
// @Success 201 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/course-management/videos/vimeo/import [post]
func (h *Handler) CreateSubCourseVideoFromVimeoID(c *fiber.Ctx) error {
	var req createVideoFromVimeoIDReq
	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:   fmt.Sprintf("%v", valErrs),
		})
	}

	video, err := h.courseMgmtSvc.CreateSubCourseVideoFromVimeoID(
		c.Context(), req.SubCourseID, req.VimeoVideoID, req.Title,
		req.Description, req.DisplayOrder, req.InstructorID,
	)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to import video from Vimeo",
			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{}{"title": video.Title, "sub_course_id": video.SubCourseID, "vimeo_video_id": req.VimeoVideoID})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionVideoCreated, domain.ResourceVideo, &video.ID, "Imported video from Vimeo: "+video.Title, meta, &ip, &ua)

	return c.Status(fiber.StatusCreated).JSON(domain.Response{
		Message: "Video imported from Vimeo successfully",
		Data:    mapVideoToResponse(video),
		Success: true,
	})
}

// Helper function to map video to response
func mapVideoToResponse(video domain.SubCourseVideo) subCourseVideoRes {
	var publishDate *string
	if video.PublishDate != nil {
		pd := video.PublishDate.Format("2006-01-02T15:04:05Z07:00")
		publishDate = &pd
	}

	return subCourseVideoRes{
		ID:              video.ID,
		SubCourseID:     video.SubCourseID,
		Title:           video.Title,
		Description:     video.Description,
		VideoURL:        video.VideoURL,
		Duration:        video.Duration,
		Resolution:      video.Resolution,
		InstructorID:    video.InstructorID,
		Thumbnail:       video.Thumbnail,
		Visibility:      video.Visibility,
		DisplayOrder:    video.DisplayOrder,
		IsPublished:     video.IsPublished,
		PublishDate:     publishDate,
		Status:          video.Status,
		VimeoID:         video.VimeoID,
		VimeoEmbedURL:   video.VimeoEmbedURL,
		VimeoPlayerHTML: video.VimeoPlayerHTML,
		VimeoStatus:     video.VimeoStatus,
	}
}
