package handlers

import (
	"Yimaru-Backend/internal/domain"
	"strconv"

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

type submitRatingReq struct {
	TargetType string  `json:"target_type" validate:"required"`
	TargetID   int64   `json:"target_id"`
	Stars      int16   `json:"stars" validate:"required,min=1,max=5"`
	Review     *string `json:"review"`
}

type ratingRes struct {
	ID         int64   `json:"id"`
	UserID     int64   `json:"user_id"`
	TargetType string  `json:"target_type"`
	TargetID   int64   `json:"target_id"`
	Stars      int16   `json:"stars"`
	Review     *string `json:"review"`
	CreatedAt  string  `json:"created_at"`
	UpdatedAt  string  `json:"updated_at"`
}

type ratingSummaryRes struct {
	TotalCount   int64   `json:"total_count"`
	AverageStars float64 `json:"average_stars"`
}

func mapRatingToRes(r domain.Rating) ratingRes {
	return ratingRes{
		ID:         r.ID,
		UserID:     r.UserID,
		TargetType: string(r.TargetType),
		TargetID:   r.TargetID,
		Stars:      r.Stars,
		Review:     r.Review,
		CreatedAt:  r.CreatedAt.String(),
		UpdatedAt:  r.UpdatedAt.String(),
	}
}

func isValidTargetType(t string) bool {
	switch domain.RatingTargetType(t) {
	case domain.RatingTargetApp, domain.RatingTargetCourse, domain.RatingTargetSubCourse:
		return true
	}
	return false
}

// SubmitRating godoc
// @Summary Submit a rating
// @Description Submit a rating for an app, course, or sub-course
// @Tags ratings
// @Accept json
// @Produce json
// @Param body body submitRatingReq true "Submit rating payload"
// @Success 201 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/ratings [post]
func (h *Handler) SubmitRating(c *fiber.Ctx) error {
	userID := c.Locals("user_id").(int64)

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

	if !isValidTargetType(req.TargetType) {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid target_type, must be one of: app, course, sub_course",
		})
	}

	targetType := domain.RatingTargetType(req.TargetType)
	targetID := req.TargetID
	if targetType == domain.RatingTargetApp {
		targetID = 0
	}

	rating, err := h.ratingSvc.SubmitRating(c.Context(), userID, targetType, targetID, req.Stars, req.Review)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to submit rating",
			Error:   err.Error(),
		})
	}

	return c.Status(fiber.StatusCreated).JSON(domain.Response{
		Message: "Rating submitted successfully",
		Data:    mapRatingToRes(rating),
	})
}

// GetMyRating godoc
// @Summary Get my rating for a target
// @Description Returns the current user's rating for a specific target
// @Tags ratings
// @Produce json
// @Param target_type query string true "Target type (app, course, sub_course)"
// @Param target_id query int true "Target ID (0 for app)"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 404 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/ratings/me [get]
func (h *Handler) GetMyRating(c *fiber.Ctx) error {
	userID := c.Locals("user_id").(int64)

	targetType := c.Query("target_type")
	if !isValidTargetType(targetType) {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid target_type, must be one of: app, course, sub_course",
		})
	}

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

	if domain.RatingTargetType(targetType) == domain.RatingTargetApp {
		targetID = 0
	}

	rating, err := h.ratingSvc.GetMyRating(c.Context(), userID, domain.RatingTargetType(targetType), targetID)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
			Message: "Rating not found",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Rating retrieved successfully",
		Data:    mapRatingToRes(rating),
	})
}

// GetRatingsByTarget godoc
// @Summary Get ratings for a target
// @Description Returns paginated ratings for a specific target
// @Tags ratings
// @Produce json
// @Param target_type query string true "Target type (app, course, sub_course)"
// @Param target_id query int true "Target ID (0 for app)"
// @Param limit query int false "Limit (default 20)"
// @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/ratings [get]
func (h *Handler) GetRatingsByTarget(c *fiber.Ctx) error {
	targetType := c.Query("target_type")
	if !isValidTargetType(targetType) {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid target_type, must be one of: app, course, sub_course",
		})
	}

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

	if domain.RatingTargetType(targetType) == domain.RatingTargetApp {
		targetID = 0
	}

	limit, err := strconv.ParseInt(c.Query("limit", "20"), 10, 32)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid limit",
			Error:   err.Error(),
		})
	}

	offset, err := strconv.ParseInt(c.Query("offset", "0"), 10, 32)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid offset",
			Error:   err.Error(),
		})
	}

	ratings, err := h.ratingSvc.GetRatingsByTarget(c.Context(), domain.RatingTargetType(targetType), targetID, int32(limit), int32(offset))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to retrieve ratings",
			Error:   err.Error(),
		})
	}

	res := make([]ratingRes, len(ratings))
	for i, r := range ratings {
		res[i] = mapRatingToRes(r)
	}

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

// GetRatingSummary godoc
// @Summary Get rating summary for a target
// @Description Returns the total count and average stars for a specific target
// @Tags ratings
// @Produce json
// @Param target_type query string true "Target type (app, course, sub_course)"
// @Param target_id query int true "Target ID (0 for app)"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/ratings/summary [get]
func (h *Handler) GetRatingSummary(c *fiber.Ctx) error {
	targetType := c.Query("target_type")
	if !isValidTargetType(targetType) {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid target_type, must be one of: app, course, sub_course",
		})
	}

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

	if domain.RatingTargetType(targetType) == domain.RatingTargetApp {
		targetID = 0
	}

	summary, err := h.ratingSvc.GetRatingSummary(c.Context(), domain.RatingTargetType(targetType), targetID)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to retrieve rating summary",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Rating summary retrieved successfully",
		Data: ratingSummaryRes{
			TotalCount:   summary.TotalCount,
			AverageStars: summary.AverageStars,
		},
	})
}

// GetMyRatings godoc
// @Summary Get all my ratings
// @Description Returns all ratings submitted by the current user
// @Tags ratings
// @Produce json
// @Success 200 {object} domain.Response
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/ratings/me/all [get]
func (h *Handler) GetMyRatings(c *fiber.Ctx) error {
	userID := c.Locals("user_id").(int64)

	ratings, err := h.ratingSvc.GetUserRatings(c.Context(), userID)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to retrieve ratings",
			Error:   err.Error(),
		})
	}

	res := make([]ratingRes, len(ratings))
	for i, r := range ratings {
		res[i] = mapRatingToRes(r)
	}

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

// DeleteRating godoc
// @Summary Delete a rating
// @Description Deletes a rating by ID for the current user
// @Tags ratings
// @Produce json
// @Param id path int true "Rating ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/ratings/{id} [delete]
func (h *Handler) DeleteRating(c *fiber.Ctx) error {
	userID := c.Locals("user_id").(int64)

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

	if err := h.ratingSvc.DeleteRating(c.Context(), ratingID, userID); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to delete rating",
			Error:   err.Error(),
		})
	}

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