package controllers

import (
	database "ares/database/config"
	"ares/database/models"
	"errors"
	"net/http"
	"strconv"

	"github.com/gofiber/fiber/v3"
	"gorm.io/gorm"
)

// getOrCreateCart is a helper to fetch the cart of the current user.
func getOrCreateCart(userID uint) (models.Cart, error) {
	var cart models.Cart
	if err := database.DB.Preload("Items").Preload("Items.Product").Where("user_id = ?", userID).First(&cart).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			cart = models.Cart{UserID: userID}
			if createErr := database.DB.Create(&cart).Error; createErr != nil {
				return cart, createErr
			}
			return cart, nil
		}
		return cart, err
	}
	return cart, nil
}

// GetMyCart godoc
// @Summary Get the current user's cart
// @Tags Cart
// @Produce json
// @Security BearerAuth
// @Success 200 {object} models.CartDoc
// @Failure 401 {object} map[string]string
// @Router /api/v1/cart [get]
func GetMyCart(c fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(uint)
	if !ok || userID == 0 {
		return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized"})
	}

	cart, err := getOrCreateCart(userID)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "could not load cart"})
	}

	return c.JSON(cart)
}

// AddToCartRequest represents the body for adding to a cart
type AddToCartRequest struct {
	ProductID uint `json:"product_id" validate:"required"`
	Quantity  int  `json:"quantity" validate:"required,min=1"`
}

// AddToCart godoc
// @Summary Add item to cart
// @Tags Cart
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param data body AddToCartRequest true "Cart Item Details"
// @Success 200 {object} models.CartDoc
// @Failure 400 {object} map[string]string
// @Failure 401 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /api/v1/cart/items [post]
func AddToCart(c fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(uint)
	if !ok || userID == 0 {
		return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized"})
	}

	var input AddToCartRequest
	if err := c.Bind().Body(&input); err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid payload"})
	}

	if err := validate.Struct(input); err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
	}

	// Check product exists
	var product models.Product
	if err := database.DB.First(&product, input.ProductID).Error; err != nil {
		return c.Status(http.StatusNotFound).JSON(fiber.Map{"error": "product not found"})
	}

	cart, err := getOrCreateCart(userID)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "could not load cart"})
	}

	// Check if item already exists in cart
	var existingItem models.CartItem
	if err := database.DB.Where("cart_id = ? AND product_id = ?", cart.ID, input.ProductID).First(&existingItem).Error; err == nil {
		// Update quantity
		existingItem.Quantity += input.Quantity
		database.DB.Save(&existingItem)
	} else {
		// Create new item
		newItem := models.CartItem{
			CartID:    cart.ID,
			ProductID: input.ProductID,
			Quantity:  input.Quantity,
		}
		database.DB.Create(&newItem)
	}

	// Return updated cart
	cart, _ = getOrCreateCart(userID)
	return c.JSON(cart)
}

// UpdateCartItemRequest represents the body for updating a cart item quantity
type UpdateCartItemRequest struct {
	Quantity int `json:"quantity" validate:"required,min=1"`
}

// UpdateCartItem godoc
// @Summary Update cart item quantity
// @Tags Cart
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param item_id path int true "Cart Item ID"
// @Param data body UpdateCartItemRequest true "Update Quantity"
// @Success 200 {object} models.CartDoc
// @Failure 400 {object} map[string]string
// @Failure 401 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /api/v1/cart/items/{item_id} [put]
func UpdateCartItem(c fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(uint)
	if !ok || userID == 0 {
		return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized"})
	}

	itemID, err := strconv.ParseUint(c.Params("item_id"), 10, 32)
	if err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid item id"})
	}

	var input UpdateCartItemRequest
	if err := c.Bind().Body(&input); err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid payload"})
	}

	if err := validate.Struct(input); err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
	}

	cart, err := getOrCreateCart(userID)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "could not load cart"})
	}

	var cartItem models.CartItem
	if err := database.DB.Where("id = ? AND cart_id = ?", itemID, cart.ID).First(&cartItem).Error; err != nil {
		return c.Status(http.StatusNotFound).JSON(fiber.Map{"error": "item not found in your cart"})
	}

	cartItem.Quantity = input.Quantity
	database.DB.Save(&cartItem)

	// Return updated cart
	cart, _ = getOrCreateCart(userID)
	return c.JSON(cart)
}

// RemoveFromCart godoc
// @Summary Remove item from cart
// @Tags Cart
// @Produce json
// @Security BearerAuth
// @Param item_id path int true "Cart Item ID"
// @Success 200 {object} models.CartDoc
// @Failure 400 {object} map[string]string
// @Failure 401 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /api/v1/cart/items/{item_id} [delete]
func RemoveFromCart(c fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(uint)
	if !ok || userID == 0 {
		return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized"})
	}

	itemID, err := strconv.ParseUint(c.Params("item_id"), 10, 32)
	if err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid item id"})
	}

	cart, err := getOrCreateCart(userID)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "could not load cart"})
	}

	var cartItem models.CartItem
	if err := database.DB.Where("id = ? AND cart_id = ?", itemID, cart.ID).First(&cartItem).Error; err != nil {
		return c.Status(http.StatusNotFound).JSON(fiber.Map{"error": "item not found in your cart"})
	}

	database.DB.Delete(&cartItem)

	// Return updated cart
	cart, _ = getOrCreateCart(userID)
	return c.JSON(cart)
}

// ClearCart godoc
// @Summary Clear the entire cart
// @Tags Cart
// @Produce json
// @Security BearerAuth
// @Success 200 {object} models.CartDoc
// @Failure 401 {object} map[string]string
// @Router /api/v1/cart [delete]
func ClearCart(c fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(uint)
	if !ok || userID == 0 {
		return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized"})
	}

	cart, err := getOrCreateCart(userID)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "could not load cart"})
	}

	// Delete all items for this cart
	database.DB.Where("cart_id = ?", cart.ID).Delete(&models.CartItem{})

	// Return updated empty cart
	cart, _ = getOrCreateCart(userID)
	return c.JSON(cart)
}
