package handlers

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/gin-gonic/gin"

	"mengyastore-backend/internal/auth"
	"mengyastore-backend/internal/email"
	"mengyastore-backend/internal/models"
	"mengyastore-backend/internal/mq"
	"mengyastore-backend/internal/storage"
)

const qrSize = "320x320"

type OrderHandler struct {
	productStore *storage.ProductStore
	orderStore   *storage.OrderStore
	siteStore    *storage.SiteStore
	authClient   *auth.SproutGateClient
	mqClient     *mq.Client // optional; nil disables RabbitMQ path
}

type checkoutPayload struct {
	ProductID string `json:"productId"`

	Quantity int `json:"quantity"`

	Note string `json:"note"`

	ContactPhone string `json:"contactPhone"`

	ContactEmail string `json:"contactEmail"`

	NotifyEmail string `json:"notifyEmail"`
}

func NewOrderHandler(productStore *storage.ProductStore, orderStore *storage.OrderStore, siteStore *storage.SiteStore, authClient *auth.SproutGateClient, mqClient *mq.Client) *OrderHandler {
	return &OrderHandler{productStore: productStore, orderStore: orderStore, siteStore: siteStore, authClient: authClient, mqClient: mqClient}
}

func (h *OrderHandler) sendOrderNotify(toEmail, toName, productName, orderID string, qty int, codes []string, isManual bool) {
	if toEmail == "" {
		return
	}
	cfg, err := h.siteStore.GetSMTPConfig()
	if err != nil || !cfg.IsConfiguredEmail() {
		return
	}

	if h.mqClient != nil {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		err := h.mqClient.PublishOrderEmail(ctx, mq.OrderEmailPayload{
			ToEmail:     toEmail,
			ToName:      toName,
			ProductName: productName,
			OrderID:     orderID,
			Quantity:    qty,
			Codes:       codes,
			IsManual:    isManual,
		})
		cancel()
		if err == nil {
			log.Printf("[MQ] queued order email order=%s to=%s", orderID, toEmail)
			return
		}
		log.Printf("[MQ] publish failed, fallback email: %v", err)
	}

	go func() {
		emailCfg := email.Config{
			SMTPHost: cfg.Host,
			SMTPPort: cfg.Port,
			From:     cfg.Email,
			Password: cfg.Password,
			FromName: cfg.FromName,
		}
		if err := email.SendOrderNotify(emailCfg, email.OrderNotifyData{
			ToEmail:     toEmail,
			ToName:      toName,
			ProductName: productName,
			OrderID:     orderID,
			Quantity:    qty,
			Codes:       codes,
			IsManual:    isManual,
		}); err != nil {
			log.Printf("[Email] 发送通知失败 order=%s to=%s: %v", orderID, toEmail, err)
		} else {
			log.Printf("[Email] 发送通知成功 order=%s to=%s", orderID, toEmail)
		}
	}()
}

func (h *OrderHandler) tryExtractUserWithEmail(c *gin.Context) (account, username, userEmail string) {

	authHeader := c.GetHeader("Authorization")

	if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {

		return "", "", ""

	}

	userToken := strings.TrimPrefix(authHeader, "Bearer ")

	result, err := h.authClient.VerifyToken(userToken)

	if err != nil || !result.Valid || result.User == nil {

		return "", "", ""

	}

	return result.User.Account, result.User.Username, result.User.Email

}

func (h *OrderHandler) CreateOrder(c *gin.Context) {

	userAccount, userName, userEmail := h.tryExtractUserWithEmail(c)

	var payload checkoutPayload

	if err := c.ShouldBindJSON(&payload); err != nil {

		c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数有误"})

		return

	}

	payload.ProductID = strings.TrimSpace(payload.ProductID)

	if payload.ProductID == "" {

		c.JSON(http.StatusBadRequest, gin.H{"error": "缺少必填字段"})

		return

	}

	if payload.Quantity <= 0 {

		payload.Quantity = 1

	}

	product, err := h.productStore.GetByID(payload.ProductID)

	if err != nil {

		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})

		return

	}

	if !product.Active {

		c.JSON(http.StatusBadRequest, gin.H{"error": "商品暂时无法购买"})

		return

	}

	if product.RequireLogin && userAccount == "" {

		c.JSON(http.StatusUnauthorized, gin.H{"error": "该商品需要登录后才能购买"})

		return

	}

	if product.MaxPerAccount > 0 && userAccount != "" {

		purchased, err := h.orderStore.CountPurchasedByAccount(userAccount, product.ID)

		if err != nil {

			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})

			return

		}

		if purchased+payload.Quantity > product.MaxPerAccount {

			remain := product.MaxPerAccount - purchased

			if remain <= 0 {

				c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("每个账户最多购买 %d 个，您已达上限", product.MaxPerAccount)})

			} else {

				c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("每个账户最多购买 %d 个，您还可购买 %d 个", product.MaxPerAccount, remain)})

			}

			return

		}

	}

	var deliveredCodes []string

	var updatedProduct models.Product

	isFixed := strings.EqualFold(strings.TrimSpace(product.FulfillmentType), "fixed")

	if isFixed {

		content := strings.TrimSpace(product.FixedContent)

		if content == "" {

			c.JSON(http.StatusBadRequest, gin.H{"error": "该商品为固定内容发货，但未配置发货内容，请联系管理员"})

			return

		}

		deliveredCodes = make([]string, payload.Quantity)

		for i := 0; i < payload.Quantity; i++ {

			deliveredCodes[i] = content

		}

		var err error

		updatedProduct, err = h.productStore.GetByID(product.ID)

		if err != nil {

			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})

			return

		}

	} else {

		if product.Quantity < payload.Quantity {

			c.JSON(http.StatusBadRequest, gin.H{"error": "库存不足"})

			return

		}

		var ok bool

		deliveredCodes, ok = extractCodes(&product, payload.Quantity)

		if !ok {

			c.JSON(http.StatusBadRequest, gin.H{"error": "卡密不足"})

			return

		}

		product.Quantity = len(product.Codes)

		var err error

		updatedProduct, err = h.productStore.Update(product.ID, product)

		if err != nil {

			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})

			return

		}

	}

	deliveryMode := "auto"

	notifyEmail := strings.TrimSpace(userEmail)

	if notifyEmail == "" {

		notifyEmail = strings.TrimSpace(payload.NotifyEmail)

	}

	if notifyEmail == "" {

		notifyEmail = strings.TrimSpace(payload.ContactEmail)

	}

	order := models.Order{

		ProductID: updatedProduct.ID,

		ProductName: updatedProduct.Name,

		UserAccount: userAccount,

		UserName: userName,

		Quantity: payload.Quantity,

		DeliveredCodes: deliveredCodes,

		Status: "completed",

		DeliveryMode: deliveryMode,

		Note: strings.TrimSpace(payload.Note),

		ContactPhone: strings.TrimSpace(payload.ContactPhone),

		ContactEmail: strings.TrimSpace(payload.ContactEmail),

		NotifyEmail: notifyEmail,
	}

	created, err := h.orderStore.Create(order)

	if err != nil {

		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})

		return

	}

	if err := h.productStore.IncrementSold(updatedProduct.ID, payload.Quantity); err != nil {

		log.Printf("[Order] 更新销量失败 (非致命): %v", err)

	}

	h.sendOrderNotify(notifyEmail, userName, updatedProduct.Name, created.ID, payload.Quantity, deliveredCodes, false)

	qrPayload := fmt.Sprintf("order:%s:%s", created.ID, created.ProductID)

	qrURL := fmt.Sprintf("https://api.qrserver.com/v1/create-qr-code/?size=%s&data=%s", qrSize, url.QueryEscape(qrPayload))

	c.JSON(http.StatusOK, gin.H{

		"data": gin.H{

			"orderId": created.ID,

			"qrCodeUrl": qrURL,

			"productId": created.ProductID,

			"productQty": created.Quantity,

			"viewCount": updatedProduct.ViewCount,

			"status": created.Status,
		},
	})

}

func (h *OrderHandler) ConfirmOrder(c *gin.Context) {

	orderID := c.Param("id")

	order, err := h.orderStore.Confirm(orderID)

	if err != nil {

		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})

		return

	}

	isManual := order.DeliveryMode == "manual"

	if isManual {

		confirmNotifyEmail := order.NotifyEmail

		if confirmNotifyEmail == "" {

			confirmNotifyEmail = order.ContactEmail

		}

		h.sendOrderNotify(confirmNotifyEmail, order.UserName, order.ProductName, order.ID, order.Quantity, order.DeliveredCodes, false)

	}

	c.JSON(http.StatusOK, gin.H{

		"data": gin.H{

			"orderId": order.ID,

			"status": order.Status,

			"deliveryMode": order.DeliveryMode,

			"deliveredCodes": order.DeliveredCodes,

			"isManual": isManual,
		},
	})

}

func (h *OrderHandler) ListMyOrders(c *gin.Context) {

	authHeader := c.GetHeader("Authorization")

	if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {

		c.JSON(http.StatusUnauthorized, gin.H{"error": "请先登录"})

		return

	}

	userToken := strings.TrimPrefix(authHeader, "Bearer ")

	result, err := h.authClient.VerifyToken(userToken)

	if err != nil || !result.Valid || result.User == nil {

		c.JSON(http.StatusUnauthorized, gin.H{"error": "登录已过期，请重新登录"})

		return

	}

	orders, err := h.orderStore.ListByAccount(result.User.Account)

	if err != nil {

		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})

		return

	}

	c.JSON(http.StatusOK, gin.H{"data": orders})

}

func extractCodes(product *models.Product, count int) ([]string, bool) {

	if count <= 0 {

		return nil, false

	}

	if len(product.Codes) < count {

		return nil, false

	}

	delivered := make([]string, count)

	copy(delivered, product.Codes[:count])

	product.Codes = product.Codes[count:]

	return delivered, true

}
