package handlers

import (
	"math"
	"net/http"
	"strconv"

	"sale/internal/models"
	"sale/internal/schemas"
	"sale/internal/utils"

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

type CategoryHandler struct{}

func NewCategoryHandler() *CategoryHandler {
	return &CategoryHandler{}
}

func (h *CategoryHandler) List(c *gin.Context) {
	var categories []models.Category
	utils.DB.Where("parent_id IS NULL").Order("sort_order ASC, id ASC").Find(&categories)
	for i := range categories {
		utils.DB.Where("parent_id = ?", categories[i].ID).Order("sort_order ASC, id ASC").Find(&categories[i].Children)
		var count int64
		utils.DB.Table("product_categories").Where("category_id = ?", categories[i].ID).Count(&count)
		categories[i].ProductCount = count
		for j := range categories[i].Children {
			var childCount int64
			utils.DB.Table("product_categories").Where("category_id = ?", categories[i].Children[j].ID).Count(&childCount)
			categories[i].Children[j].ProductCount = childCount
		}
	}
	c.JSON(http.StatusOK, gin.H{"data": categories})
}

func (h *CategoryHandler) Create(c *gin.Context) {
	var req schemas.CreateCategoryRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	var existing models.Category
	if err := utils.DB.Where("name = ?", req.Name).First(&existing).Error; err == nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "分类名称已存在"})
		return
	}

	category := models.Category{
		Name:        req.Name,
		Description: req.Description,
		ParentID:    req.ParentID,
		MinAmount:   req.MinAmount,
		MaxAmount:   req.MaxAmount,
		MinQuantity: req.MinQuantity,
		MaxQuantity: req.MaxQuantity,
		MinWeight:   req.MinWeight,
		MaxWeight:   req.MaxWeight,
		SortOrder:   req.SortOrder,
	}

	if err := utils.DB.Create(&category).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create category"})
		return
	}

	c.JSON(http.StatusCreated, gin.H{"data": category})
}

func (h *CategoryHandler) Update(c *gin.Context) {
	id, _ := strconv.Atoi(c.Param("id"))
	var category models.Category
	if err := utils.DB.First(&category, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Category not found"})
		return
	}

	var req schemas.UpdateCategoryRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	if req.Name != nil && *req.Name != category.Name {
		var existing models.Category
		if err := utils.DB.Where("name = ? AND id != ?", *req.Name, id).First(&existing).Error; err == nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "分类名称已存在"})
			return
		}
	}

	updates := make(map[string]interface{})
	if req.Name != nil {
		updates["name"] = *req.Name
	}
	if req.Description != nil {
		updates["description"] = *req.Description
	}
	if req.ParentID != nil {
		updates["parent_id"] = *req.ParentID
	}
	if req.MinAmount != nil {
		updates["min_amount"] = *req.MinAmount
	}
	if req.MaxAmount != nil {
		updates["max_amount"] = *req.MaxAmount
	}
	if req.MinQuantity != nil {
		updates["min_quantity"] = *req.MinQuantity
	}
	if req.MaxQuantity != nil {
		updates["max_quantity"] = *req.MaxQuantity
	}
	if req.MinWeight != nil {
		updates["min_weight"] = *req.MinWeight
	}
	if req.MaxWeight != nil {
		updates["max_weight"] = *req.MaxWeight
	}
	if req.SortOrder != nil {
		updates["sort_order"] = *req.SortOrder
	}

	utils.DB.Model(&category).Updates(updates)
	c.JSON(http.StatusOK, gin.H{"data": category})
}

func (h *CategoryHandler) Delete(c *gin.Context) {
	id, _ := strconv.Atoi(c.Param("id"))

	var childCount int64
	utils.DB.Model(&models.Category{}).Where("parent_id = ?", id).Count(&childCount)
	if childCount > 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "该分类下有子分类，无法删除"})
		return
	}

	var productCount int64
	utils.DB.Table("product_categories").Where("category_id = ?", id).Count(&productCount)
	if productCount > 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "该分类下有商品，无法删除"})
		return
	}

	if err := utils.DB.Delete(&models.Category{}, id).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete category"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "Category deleted successfully"})
}

type ProductHandler struct{}

func NewProductHandler() *ProductHandler {
	return &ProductHandler{}
}

func (h *ProductHandler) List(c *gin.Context) {
	var req schemas.ProductListRequest
	if err := c.ShouldBindQuery(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	query := utils.DB.Model(&models.Product{}).Where("is_active = ?", true)

	if req.CategoryID != nil {
		query = query.Joins("JOIN product_categories ON product_categories.product_id = products.id").
			Where("product_categories.category_id = ?", *req.CategoryID)
	}

	if req.BrandID != nil {
		query = query.Where("brand_id = ?", *req.BrandID)
	}

	if req.Keyword != "" {
		query = query.Where("name ILIKE ?", "%"+req.Keyword+"%")
	}

	if req.MinPrice != nil {
		query = query.Where("price >= ?", *req.MinPrice)
	}

	if req.MaxPrice != nil {
		query = query.Where("price <= ?", *req.MaxPrice)
	}

	var total int64
	query.Count(&total)

	var products []models.Product
	offset := (req.Page - 1) * req.PageSize
	query.Preload("Categories").Preload("CustomFields").Preload("Brand").
		Order("created_at DESC").
		Offset(offset).Limit(req.PageSize).
		Find(&products)

	totalPages := int(math.Ceil(float64(total) / float64(req.PageSize)))
	c.JSON(http.StatusOK, gin.H{
		"data": products,
		"pagination": gin.H{
			"page":       req.Page,
			"page_size":  req.PageSize,
			"total":      total,
			"total_pages": totalPages,
		},
	})
}

func (h *ProductHandler) GetByID(c *gin.Context) {
	id, _ := strconv.Atoi(c.Param("id"))
	var product models.Product
	if err := utils.DB.Preload("Categories").Preload("CustomFields").First(&product, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Product not found"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"data": product})
}

func (h *ProductHandler) AdminList(c *gin.Context) {
	var products []models.Product
	utils.DB.Preload("Categories").Preload("Brand").Order("created_at DESC").Find(&products)
	c.JSON(http.StatusOK, gin.H{"data": products})
}

func (h *ProductHandler) Create(c *gin.Context) {
	var req schemas.CreateProductRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	product := models.Product{
		Name:          req.Name,
		Description:   req.Description,
		Price:         req.Price,
		MinPurchase:   req.MinPurchase,
		MaxPurchase:   req.MaxPurchase,
		MinWeight:     req.MinWeight,
		MaxWeight:     req.MaxWeight,
		MinAmount:     req.MinAmount,
		MaxAmount:     req.MaxAmount,
		RequireCredit: req.RequireCredit,
		CreditCost:    req.CreditCost,
		CreditReward:  req.CreditReward,
		Images:        req.Images,
		BrandID:       req.BrandID,
		IsActive:      true,
	}

	if err := utils.DB.Create(&product).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create product"})
		return
	}

	if len(req.CategoryIDs) > 0 {
		var categories []models.Category
		utils.DB.Where("id IN ?", req.CategoryIDs).Find(&categories)
		utils.DB.Model(&product).Association("Categories").Replace(categories)
	}

	if len(req.CustomFields) > 0 {
		for _, cf := range req.CustomFields {
			utils.DB.Create(&models.ProductCustomField{
				ProductID:  product.ID,
				FieldName:  cf.FieldName,
				FieldValue: cf.FieldValue,
			})
		}
	}

	utils.DB.Preload("Categories").Preload("CustomFields").Preload("Brand").First(&product, product.ID)
	c.JSON(http.StatusCreated, gin.H{"data": product})
}

func (h *ProductHandler) Update(c *gin.Context) {
	id, _ := strconv.Atoi(c.Param("id"))
	var product models.Product
	if err := utils.DB.First(&product, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Product not found"})
		return
	}

	var req schemas.UpdateProductRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	updates := make(map[string]interface{})
	if req.Name != nil {
		updates["name"] = *req.Name
	}
	if req.Description != nil {
		updates["description"] = *req.Description
	}
	if req.Price != nil {
		updates["price"] = *req.Price
	}
	if req.MinPurchase != nil {
		updates["min_purchase"] = *req.MinPurchase
	}
	if req.MaxPurchase != nil {
		updates["max_purchase"] = *req.MaxPurchase
	}
	if req.MinWeight != nil {
		updates["min_weight"] = *req.MinWeight
	}
	if req.MaxWeight != nil {
		updates["max_weight"] = *req.MaxWeight
	}
	if req.MinAmount != nil {
		updates["min_amount"] = *req.MinAmount
	}
	if req.MaxAmount != nil {
		updates["max_amount"] = *req.MaxAmount
	}
	if req.RequireCredit != nil {
		updates["require_credit"] = *req.RequireCredit
	}
	if req.CreditCost != nil {
		updates["credit_cost"] = *req.CreditCost
	}
	if req.CreditReward != nil {
		updates["credit_reward"] = *req.CreditReward
	}
	if req.Images != nil {
		updates["images"] = *req.Images
	}
	if req.IsActive != nil {
		updates["is_active"] = *req.IsActive
	}
	if req.BrandID != nil {
		updates["brand_id"] = *req.BrandID
	}

	utils.DB.Model(&product).Updates(updates)

	if req.CategoryIDs != nil {
		var categories []models.Category
		utils.DB.Where("id IN ?", req.CategoryIDs).Find(&categories)
		utils.DB.Model(&product).Association("Categories").Replace(categories)
	}

	if req.CustomFields != nil {
		utils.DB.Where("product_id = ?", product.ID).Delete(&models.ProductCustomField{})
		for _, cf := range req.CustomFields {
			utils.DB.Create(&models.ProductCustomField{
				ProductID:  product.ID,
				FieldName:  cf.FieldName,
				FieldValue: cf.FieldValue,
			})
		}
	}

	utils.DB.Preload("Categories").Preload("CustomFields").Preload("Brand").First(&product, product.ID)
	c.JSON(http.StatusOK, gin.H{"data": product})
}

func (h *ProductHandler) Delete(c *gin.Context) {
	id, _ := strconv.Atoi(c.Param("id"))
	if err := utils.DB.Delete(&models.Product{}, id).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete product"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "Product deleted successfully"})
}

func (h *ProductHandler) AddCustomField(c *gin.Context) {
	id, _ := strconv.Atoi(c.Param("id"))
	var product models.Product
	if err := utils.DB.First(&product, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Product not found"})
		return
	}

	var req schemas.CustomFieldRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	field := models.ProductCustomField{
		ProductID:  product.ID,
		FieldName:  req.FieldName,
		FieldValue: req.FieldValue,
	}

	utils.DB.Create(&field)
	c.JSON(http.StatusCreated, gin.H{"data": field})
}
