package User

import (
	config "astrology-api/configs"
	dto "astrology-api/dto"
	repositories "astrology-api/repositories"
	services "astrology-api/services"
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
)

func NewUserReviewController() *UserReviewController {

	userReviewRepository := repositories.NewUserReviewRepository()
	astrologerRepository := repositories.NewAstrologerRepository(config.DB)
	userReviewService := services.NewUserReviewService(
		userReviewRepository,
		astrologerRepository,

		// So a review that names a consultation can be checked against it:
		// the session has to be this customer's, with this astrologer, and
		// finished.
		repositories.NewConsultationRepository(config.DB),
	)

	return &UserReviewController{
		userReviewService: userReviewService,
	}
}

type UserReviewController struct {
	userReviewService services.UserReviewService
}

func (c *UserReviewController) AddReview(ctx *gin.Context) {

	var req dto.AddReviewRequest

	// Bind Request
	if err := ctx.ShouldBindJSON(&req); err != nil {

		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	// Get Logged-in User ID from JWT Middleware
	userIDValue, exists := ctx.Get("user_id")
	if !exists {

		ctx.JSON(http.StatusUnauthorized, gin.H{
			"status":  401,
			"message": "Unauthorized",
		})
		return
	}

	userID, ok := userIDValue.(uint)
	if !ok {

		ctx.JSON(http.StatusUnauthorized, gin.H{
			"status":  401,
			"message": "Invalid User",
		})
		return
	}

	// Call Service
	review, err := c.userReviewService.AddReview(userID, req)

	if err != nil {

		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":     200,
		"message":    "User review added successfully",
		"recordList": review,
	})
}

func (c *UserReviewController) GetAstrologerUserReview(ctx *gin.Context) {

	var req dto.ReviewListRequest

	// Bind Request
	if err := ctx.ShouldBindJSON(&req); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	// Service Call
	reviews, total, err := c.userReviewService.GetAstrologerReviews(req)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	if reviews == nil {
		reviews = make([]dto.UserReviewResponse, 0)
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":      200,
		"message":     "Reviews fetched successfully",
		"recordList":  reviews,
		"totalRecord": total,
	})
}

func (c *UserReviewController) GetUserReviewHistory(ctx *gin.Context) {

	// Logged-in User
	userIDValue, exists := ctx.Get("user_id")
	if !exists {

		ctx.JSON(http.StatusUnauthorized, gin.H{
			"status":  401,
			"message": "Unauthorized",
		})
		return
	}

	userID, ok := userIDValue.(uint)
	if !ok {

		ctx.JSON(http.StatusUnauthorized, gin.H{
			"status":  401,
			"message": "Invalid User",
		})
		return
	}

	reviews, err := c.userReviewService.GetUserReviewHistory(userID)

	if err != nil {

		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":     200,
		"message":    "Review history fetched successfully",
		"recordList": reviews,
	})
}

func (c *UserReviewController) ReplyReview(ctx *gin.Context) {

	var req dto.ReplyReviewRequest

	// Bind Request
	if err := ctx.ShouldBindJSON(&req); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	// Get Logged-in Astrologer ID
	astrologerIDValue, exists := ctx.Get("astrologerID")
	if !exists {
		ctx.JSON(http.StatusUnauthorized, gin.H{
			"status":  401,
			"message": "Unauthorized",
		})
		return
	}

	astrologerID, ok := astrologerIDValue.(uint)
	if !ok {
		ctx.JSON(http.StatusUnauthorized, gin.H{
			"status":  401,
			"message": "Invalid Astrologer",
		})
		return
	}

	// Service
	err := c.userReviewService.ReplyReview(astrologerID, req)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":  200,
		"message": "Reply added successfully",
	})
}

func (c *UserReviewController) BlockReview(ctx *gin.Context) {

	var req dto.BlockReviewRequest

	// Bind Request
	if err := ctx.ShouldBindJSON(&req); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	// Logged In User
	userIDValue, exists := ctx.Get("user_id")
	if !exists {
		ctx.JSON(http.StatusUnauthorized, gin.H{
			"status":  401,
			"message": "Unauthorized",
		})
		return
	}

	userID, ok := userIDValue.(uint)
	if !ok {
		ctx.JSON(http.StatusUnauthorized, gin.H{
			"status":  401,
			"message": "Invalid User",
		})
		return
	}

	// Service
	err := c.userReviewService.BlockReview(userID, req)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":  200,
		"message": "Review blocked successfully",
	})
}

func (c *UserReviewController) GetBlockedReviews(ctx *gin.Context) {

	// Logged In User
	userIDValue, exists := ctx.Get("user_id")
	if !exists {
		ctx.JSON(http.StatusUnauthorized, gin.H{
			"status":  401,
			"message": "Unauthorized",
		})
		return
	}

	userID, ok := userIDValue.(uint)
	if !ok {
		ctx.JSON(http.StatusUnauthorized, gin.H{
			"status":  401,
			"message": "Invalid User",
		})
		return
	}

	// Service
	reviews, err := c.userReviewService.GetBlockedReviews(userID)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":     200,
		"message":    "Blocked reviews fetched successfully",
		"recordList": reviews,
	})
}

func (c *UserReviewController) GetUserReviewForAdmin(ctx *gin.Context) {

	var req dto.ReviewListRequest

	// Bind Request
	if err := ctx.ShouldBindJSON(&req); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	reviews, err := c.userReviewService.GetAdminReviewList(req.AstrologerID)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":     200,
		"message":    "Admin review list fetched successfully",
		"recordList": reviews,
	})
}

func (c *UserReviewController) UpdateReview(ctx *gin.Context) {

	// Get Review ID from URL
	id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": "Invalid review id",
		})
		return
	}

	var req dto.UpdateReviewRequest

	// Bind Request
	if err := ctx.ShouldBindJSON(&req); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	// Get Logged In User
	userIDValue, exists := ctx.Get("user_id")
	if !exists {
		ctx.JSON(http.StatusUnauthorized, gin.H{
			"status":  401,
			"message": "Unauthorized",
		})
		return
	}

	userID, ok := userIDValue.(uint)
	if !ok {
		ctx.JSON(http.StatusUnauthorized, gin.H{
			"status":  401,
			"message": "Invalid User",
		})
		return
	}

	// Service
	review, err := c.userReviewService.UpdateReview(
		userID,
		uint(id),
		req,
	)

	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":     200,
		"message":    "User review updated successfully",
		"recordList": review,
	})
}

func (c *UserReviewController) DeleteReview(ctx *gin.Context) {

	// Get Review ID
	id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": "Invalid review id",
		})
		return
	}

	// Logged In User
	userIDValue, exists := ctx.Get("user_id")
	if !exists {
		ctx.JSON(http.StatusUnauthorized, gin.H{
			"status":  401,
			"message": "Unauthorized",
		})
		return
	}

	userID, ok := userIDValue.(uint)
	if !ok {
		ctx.JSON(http.StatusUnauthorized, gin.H{
			"status":  401,
			"message": "Invalid User",
		})
		return
	}

	// Service
	err = c.userReviewService.DeleteReview(
		userID,
		uint(id),
	)

	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":  200,
		"message": "User review deleted successfully",
	})
}

func (c *UserReviewController) GetAverageRating(ctx *gin.Context) {

	var req dto.AverageRatingRequest

	if err := ctx.ShouldBindJSON(&req); err != nil {

		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	rating, err := c.userReviewService.GetAverageRating(req.AstrologerID)

	if err != nil {

		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  400,
			"message": err.Error(),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":        200,
		"averageRating": rating,
	})
}
