package handlers

import (
	"crypto"
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"io"
	"log"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"

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

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

type PaymentHandler struct{}

func NewPaymentHandler() *PaymentHandler {
	return &PaymentHandler{}
}

type BepUsdtCreateRequest struct {
	TradeType   string  `json:"trade_type,omitempty"`
	OrderID     string  `json:"order_id"`
	Amount      float64 `json:"amount"`
	NotifyURL   string  `json:"notify_url"`
	RedirectURL string  `json:"redirect_url,omitempty"`
	Signature   string  `json:"signature"`
}

func (h *PaymentHandler) CreatePayment(c *gin.Context) {
	userID := c.GetUint("user_id")
	orderID, _ := strconv.Atoi(c.Param("id"))

	var order models.Order
	if err := utils.DB.Preload("OrderItems").Where("id = ? AND user_id = ?", orderID, userID).First(&order).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Order not found"})
		return
	}

	if order.Status != models.OrderStatusPendingPayment {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Order is not pending payment"})
		return
	}

	if order.PaymentExpiresAt != nil && time.Now().After(*order.PaymentExpiresAt) {
		tx := utils.DB.Begin()
		tx.Model(&order).Update("status", models.OrderStatusCancelled)
		for _, item := range order.OrderItems {
			var inventory models.Inventory
			if err := tx.Where("product_id = ?", item.ProductID).First(&inventory).Error; err == nil {
				tx.Model(&inventory).UpdateColumn("quantity", inventory.Quantity+item.Quantity)
			}
		}
		tx.Commit()
		c.JSON(http.StatusBadRequest, gin.H{"error": "支付已超时，订单已取消"})
		return
	}

	// 根据订单支付方式获取对应的支付通道
	paymentMethod := order.PaymentMethod
	if paymentMethod == "" {
		paymentMethod = "usdt" // 默认
	}

	// 先通过 code 查找支付通道
	var channels []models.PaymentChannel
	utils.DB.Where("is_enabled = ? AND code = ?", true, paymentMethod).Order("sort_order ASC").Find(&channels)

	// 如果没找到，尝试通过 type 查找
	if len(channels) == 0 {
		utils.DB.Where("is_enabled = ? AND type = ?", true, paymentMethod).Order("sort_order ASC").Find(&channels)
	}

	if len(channels) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "No available payment channel"})
		return
	}

	channel := channels[0]

	// 如果是 hashpay，调用 HashPay 支付
	if channel.Type == "hashpay" {
		h.CreateHashPayPayment(c)
		return
	}

	// BepUsdt 支付流程
	config := parseChannelConfig(channel.Config)

	apiURL := strings.TrimSuffix(config["bepusdt_api_url"], "/")
	apiToken := config["bepusdt_api_token"]
	tradeType := config["bepusdt_trade_type"]
	if tradeType == "" {
		tradeType = "usdt.trc20"
	}

	scheme := "http"
	if c.Request.TLS != nil || c.GetHeader("X-Forwarded-Proto") == "https" {
		scheme = "https"
	}
	host := c.Request.Host
	baseURL := fmt.Sprintf("%s://%s", scheme, host)

	notifyURL := fmt.Sprintf("%s/api/payment/bepusdt/notify", baseURL)
	redirectURL := fmt.Sprintf("%s/orders/%d", baseURL, order.ID)

	params := map[string]string{
		"order_id":     strconv.Itoa(int(order.ID)),
		"amount":       fmt.Sprint(order.TotalAmount),
		"notify_url":   notifyURL,
		"redirect_url": redirectURL,
	}
	if tradeType != "" {
		params["trade_type"] = tradeType
	}

	signature := signBepUsdt(params, apiToken)

	log.Printf("[BepUsdt] signature params: %v", params)
	log.Printf("[BepUsdt] signature: %s, token: %s", signature, apiToken)

	reqBody := BepUsdtCreateRequest{
		TradeType:   tradeType,
		OrderID:     strconv.Itoa(int(order.ID)),
		Amount:      order.TotalAmount,
		NotifyURL:   notifyURL,
		RedirectURL: redirectURL,
		Signature:   signature,
	}

	body, _ := json.Marshal(reqBody)
	log.Printf("[BepUsdt] request body: %s", string(body))

	req, err := http.NewRequest("POST", apiURL+"/api/v1/order/create-transaction", strings.NewReader(string(body)))
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create request"})
		return
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to connect payment gateway"})
		return
	}
	defer resp.Body.Close()

	respBody, _ := io.ReadAll(resp.Body)
	log.Printf("[BepUsdt] response: %s", string(respBody))

	var result map[string]interface{}
	json.Unmarshal(respBody, &result)

	if statusCode, ok := result["status_code"].(float64); !ok || int(statusCode) != 200 {
		errMsg := "Payment creation failed"
		if msg, ok := result["message"].(string); ok {
			errMsg = msg
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": errMsg})
		return
	}

	data, ok := result["data"].(map[string]interface{})
	if !ok {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid response from payment gateway"})
		return
	}

	tradeID, _ := data["trade_id"].(string)
	token, _ := data["token"].(string)
	paymentURL, _ := data["payment_url"].(string)

	// 钱包地址在token字段中
	walletAddress := token

	log.Printf("[BepUsdt] payment data: trade_id=%s, token=%s, payment_url=%s, wallet_address=%s", tradeID, token, paymentURL, walletAddress)

	var amount float64
	if v, ok := data["amount"].(float64); ok {
		amount = v
	} else if v, ok := data["amount"].(string); ok {
		amount, _ = strconv.ParseFloat(v, 64)
	}

	var actualAmount float64
	if v, ok := data["actual_amount"].(float64); ok {
		actualAmount = v
	} else if v, ok := data["actual_amount"].(string); ok {
		actualAmount, _ = strconv.ParseFloat(v, 64)
	}
	if actualAmount == 0 {
		actualAmount = amount
	}

	var expiration int64
	if v, ok := data["expiration_time"].(float64); ok {
		expiration = int64(v)
	}
	if expiration > 1e12 {
		expiration = expiration / 1000
	}

	var remainingSeconds int64
	var expiresAt *time.Time
	if expiration > 0 {
		remainingSeconds = expiration - time.Now().Unix()
		if remainingSeconds < 0 {
			remainingSeconds = 0
		}
		if remainingSeconds > 0 {
			t := time.Now().Add(time.Duration(remainingSeconds) * time.Second)
			expiresAt = &t
		}
	}

	utils.DB.Model(&order).Updates(map[string]interface{}{
		"payment_method":      "bepusdt",
		"payment_expires_at":  expiresAt,
	})

	paymentInfo := map[string]interface{}{
		"trade_id":        tradeID,
		"order_id":        order.ID,
		"amount":          amount,
		"actual_amount":   actualAmount,
		"token":           token,
		"payment_url":     paymentURL,
		"wallet_address":  walletAddress,
		"expiration_time": remainingSeconds,
		"trade_type":      tradeType,
		"channel_id":      channel.ID,
	}

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

func (h *PaymentHandler) BepUsdtNotify(c *gin.Context) {
	body, _ := io.ReadAll(c.Request.Body)

	var params map[string]interface{}
	json.Unmarshal(body, &params)

	orderIDStr, _ := params["order_id"].(string)
	status, _ := params["status"].(float64)
	signature, _ := params["signature"].(string)

	orderID, err := strconv.Atoi(orderIDStr)
	if err != nil {
		c.String(http.StatusOK, "ok")
		return
	}

	var order models.Order
	if err := utils.DB.First(&order, orderID).Error; err != nil {
		c.String(http.StatusOK, "ok")
		return
	}

	var channels []models.PaymentChannel
	utils.DB.Where("type = ?", "bepusdt").Find(&channels)
	if len(channels) == 0 {
		c.String(http.StatusOK, "ok")
		return
	}

	channel := channels[0]
	config := parseChannelConfig(channel.Config)
	apiToken := config["bepusdt_api_token"]

	stringParams := make(map[string]string)
	for k, v := range params {
		if k != "signature" {
			stringParams[k] = fmt.Sprintf("%v", v)
		}
	}
	expectedSign := signBepUsdt(stringParams, apiToken)

	if signature != expectedSign {
		c.String(http.StatusOK, "ok")
		return
	}

	if int(status) == 2 {
		utils.DB.Model(&order).Update("status", models.OrderStatusPendingConfirm)
	} else if int(status) == 3 {
		utils.DB.Model(&order).Update("status", models.OrderStatusCancelled)
	}

	c.String(http.StatusOK, "ok")
}

func (h *PaymentHandler) CheckPaymentStatus(c *gin.Context) {
	userID := c.GetUint("user_id")
	orderID, _ := strconv.Atoi(c.Param("id"))

	var order models.Order
	if err := utils.DB.Where("id = ? AND user_id = ?", orderID, userID).First(&order).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Order not found"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"data": gin.H{
		"status": order.Status,
	}})
}

func signBepUsdt(params map[string]string, token string) string {
	keys := make([]string, 0, len(params))
	for k := range params {
		if params[k] != "" && k != "signature" && k != "sign_type" {
			keys = append(keys, k)
		}
	}
	sort.Strings(keys)

	var parts []string
	for _, k := range keys {
		parts = append(parts, fmt.Sprintf("%s=%s", k, params[k]))
	}
	str := strings.Join(parts, "&") + token

	hash := md5.Sum([]byte(str))
	return hex.EncodeToString(hash[:])
}

func parseChannelConfig(configStr string) map[string]string {
	result := make(map[string]string)
	if configStr == "" {
		return result
	}
	var raw map[string]interface{}
	if err := json.Unmarshal([]byte(configStr), &raw); err != nil {
		var rawStr map[string]string
		if err2 := json.Unmarshal([]byte(configStr), &rawStr); err2 == nil {
			return rawStr
		}
		log.Printf("[Payment] failed to parse channel config: %v", err)
		return result
	}
	for k, v := range raw {
		if v == nil {
			continue
		}
		switch val := v.(type) {
		case string:
			result[k] = val
		case float64:
			if val == float64(int64(val)) {
				result[k] = strconv.FormatInt(int64(val), 10)
			} else {
				result[k] = strconv.FormatFloat(val, 'f', -1, 64)
			}
		case bool:
			result[k] = strconv.FormatBool(val)
		default:
			result[k] = fmt.Sprintf("%v", val)
		}
	}
	return result
}

// ===================== HashPay 支付 =====================

// HashPay 创建订单请求
type HashPayCreateRequest struct {
	MerchantNo  string  `json:"merchantNo"`
	Amount      float64 `json:"amount"`
	Currency    string  `json:"currency,omitempty"`
	Description string  `json:"description,omitempty"`
	ReturnURL   string  `json:"return_url,omitempty"`
}

// HashPay 创建订单响应
type HashPayCreateResponse struct {
	CheckoutURL string `json:"checkoutUrl"`
	Order       struct {
		ID        string  `json:"id"`
		Amount    float64 `json:"amount"`
		Currency  string  `json:"currency"`
		ExpiresAt int64   `json:"expiresAt"`
		Status    string  `json:"status"`
	} `json:"order"`
	Reused bool `json:"reused"`
}

// CreateHashPayPayment 创建 HashPay 支付订单
func (h *PaymentHandler) CreateHashPayPayment(c *gin.Context) {
	userID := c.GetUint("user_id")
	orderID, _ := strconv.Atoi(c.Param("id"))

	var order models.Order
	if err := utils.DB.Preload("OrderItems").Where("id = ? AND user_id = ?", orderID, userID).First(&order).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Order not found"})
		return
	}

	if order.Status != models.OrderStatusPendingPayment {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Order is not pending payment"})
		return
	}

	if order.PaymentExpiresAt != nil && time.Now().After(*order.PaymentExpiresAt) {
		tx := utils.DB.Begin()
		tx.Model(&order).Update("status", models.OrderStatusCancelled)
		for _, item := range order.OrderItems {
			var inventory models.Inventory
			if err := tx.Where("product_id = ?", item.ProductID).First(&inventory).Error; err == nil {
				tx.Model(&inventory).UpdateColumn("quantity", inventory.Quantity+item.Quantity)
			}
		}
		tx.Commit()
		c.JSON(http.StatusBadRequest, gin.H{"error": "支付已超时，订单已取消"})
		return
	}

	var channels []models.PaymentChannel
	utils.DB.Where("is_enabled = ? AND type = ?", true, "hashpay").Order("sort_order ASC").Find(&channels)
	if len(channels) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "No available HashPay payment channel"})
		return
	}

	channel := channels[0]
	config := parseChannelConfig(channel.Config)

	baseURL := config["hashpay_base_url"]
	merchantID := config["hashpay_merchant_id"]
	privateKeyPEM := config["hashpay_private_key"]

	if baseURL == "" || merchantID == "" || privateKeyPEM == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "HashPay channel not configured properly"})
		return
	}

	// 解析私钥
	privateKey, err := parseRSAPrivateKey(privateKeyPEM)
	if err != nil {
		log.Printf("[HashPay] failed to parse private key: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid private key configuration"})
		return
	}

	scheme := "http"
	if c.Request.TLS != nil || c.GetHeader("X-Forwarded-Proto") == "https" {
		scheme = "https"
	}
	host := c.Request.Host
	apiBaseURL := fmt.Sprintf("%s://%s", scheme, host)

	merchantNo := fmt.Sprintf("ORDER-%d", order.ID)
	returnURL := fmt.Sprintf("%s/orders/%d", apiBaseURL, order.ID)

	reqBody := HashPayCreateRequest{
		MerchantNo:  merchantNo,
		Amount:      order.TotalAmount,
		Currency:    "USD",
		Description: fmt.Sprintf("Order #%d", order.ID),
		ReturnURL:   returnURL,
	}

	bodyBytes, _ := json.Marshal(reqBody)
	timestamp := fmt.Sprintf("%d", time.Now().Unix())

	// 生成签名
	signature, err := signHashPay("POST", "/api/merchant/new", timestamp, string(bodyBytes), privateKey)
	if err != nil {
		log.Printf("[HashPay] failed to sign request: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sign request"})
		return
	}

	// 发送请求
	req, err := http.NewRequest("POST", baseURL+"/api/merchant/new", strings.NewReader(string(bodyBytes)))
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create request"})
		return
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Merchant-Id", merchantID)
	req.Header.Set("X-Timestamp", timestamp)
	req.Header.Set("X-Signature", signature)

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		log.Printf("[HashPay] request failed: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to connect to HashPay"})
		return
	}
	defer resp.Body.Close()

	respBody, _ := io.ReadAll(resp.Body)
	log.Printf("[HashPay] response: %s", string(respBody))

	var result HashPayCreateResponse
	if err := json.Unmarshal(respBody, &result); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid response from HashPay"})
		return
	}

	if result.CheckoutURL == "" {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "No checkout URL returned"})
		return
	}

	// 更新订单支付方式
	utils.DB.Model(&order).Updates(map[string]interface{}{
		"payment_method": "hashpay",
	})

	// 计算过期时间
	var expiresAt *time.Time
	if result.Order.ExpiresAt > 0 {
		t := time.Unix(result.Order.ExpiresAt, 0)
		expiresAt = &t
	}

	paymentInfo := map[string]interface{}{
		"order_id":     order.ID,
		"checkout_url": result.CheckoutURL,
		"hashpay_id":   result.Order.ID,
		"amount":       result.Order.Amount,
		"currency":     result.Order.Currency,
		"expires_at":   expiresAt,
		"channel_id":   channel.ID,
	}

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

// HashPayNotify 处理 HashPay 回调通知
func (h *PaymentHandler) HashPayNotify(c *gin.Context) {
	body, _ := io.ReadAll(c.Request.Body)

	log.Printf("[HashPay] notify body: %s", string(body))

	var envelope struct {
		Alg string `json:"alg"`
		Key string `json:"key"`
		IV  string `json:"iv"`
		Data string `json:"data"`
	}
	if err := json.Unmarshal(body, &envelope); err != nil {
		c.String(http.StatusOK, "ok")
		return
	}

	// 获取商户私钥进行解密
	var channels []models.PaymentChannel
	utils.DB.Where("type = ?", "hashpay").Find(&channels)
	if len(channels) == 0 {
		c.String(http.StatusOK, "ok")
		return
	}

	channel := channels[0]
	config := parseChannelConfig(channel.Config)
	privateKeyPEM := config["hashpay_private_key"]

	privateKey, err := parseRSAPrivateKey(privateKeyPEM)
	if err != nil {
		log.Printf("[HashPay] failed to parse private key for decryption: %v", err)
		c.String(http.StatusOK, "ok")
		return
	}

	// 解密回调数据
	payload, err := decryptHashPayNotify(envelope.Key, envelope.IV, envelope.Data, privateKey)
	if err != nil {
		log.Printf("[HashPay] failed to decrypt notify: %v", err)
		c.String(http.StatusOK, "ok")
		return
	}

	log.Printf("[HashPay] decrypted payload: %+v", payload)

	// 解析订单号
	merchantNo := payload.MerchantNo
	if merchantNo == "" {
		c.String(http.StatusOK, "ok")
		return
	}

	// 从 merchantNo 中提取订单ID (格式: ORDER-123)
	orderIDStr := strings.TrimPrefix(merchantNo, "ORDER-")
	orderID, err := strconv.Atoi(orderIDStr)
	if err != nil {
		c.String(http.StatusOK, "ok")
		return
	}

	var order models.Order
	if err := utils.DB.First(&order, orderID).Error; err != nil {
		c.String(http.StatusOK, "ok")
		return
	}

	// 更新订单状态
	if payload.Status == "paid" {
		utils.DB.Model(&order).Update("status", models.OrderStatusPendingConfirm)
	} else if payload.Status == "expired" || payload.Status == "cancelled" {
		utils.DB.Model(&order).Update("status", models.OrderStatusCancelled)
	}

	c.String(http.StatusOK, "ok")
}

// HashPayNotifyPayload 回调解密后的数据结构
type HashPayNotifyPayload struct {
	Timestamp  int64  `json:"timestamp"`
	MerchantNo string `json:"merchantNo"`
	OrderID    string `json:"orderId"`
	Amount     float64 `json:"amount"`
	Currency   string `json:"currency"`
	Status     string `json:"status"`
}

// parseRSAPrivateKey 解析 RSA 私钥
func parseRSAPrivateKey(pemStr string) (*rsa.PrivateKey, error) {
	block, _ := pem.Decode([]byte(pemStr))
	if block == nil {
		return nil, fmt.Errorf("failed to parse PEM block")
	}

	// 尝试 PKCS1
	if key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
		return key, nil
	}

	// 尝试 PKCS8
	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	rsaKey, ok := key.(*rsa.PrivateKey)
	if !ok {
		return nil, fmt.Errorf("not an RSA private key")
	}
	return rsaKey, nil
}

// signHashPay 生成 HashPay 请求签名
func signHashPay(method, path, timestamp, body string, privateKey *rsa.PrivateKey) (string, error) {
	// 签名原文: method + "\n" + path + "\n" + timestamp + "\n" + body
	message := fmt.Sprintf("%s\n%s\n%s\n%s", method, path, timestamp, body)

	hashed := sha256.Sum256([]byte(message))
	signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed[:])
	if err != nil {
		return "", err
	}

	return base64.StdEncoding.EncodeToString(signature), nil
}

// decryptHashPayNotify 解密 HashPay 回调通知
func decryptHashPayNotify(encryptedKey, iv, encryptedData string, privateKey *rsa.PrivateKey) (*HashPayNotifyPayload, error) {
	// 解码 Base64
	keyBytes, err := base64.StdEncoding.DecodeString(encryptedKey)
	if err != nil {
		return nil, fmt.Errorf("failed to decode key: %v", err)
	}

	ivBytes, err := base64.StdEncoding.DecodeString(iv)
	if err != nil {
		return nil, fmt.Errorf("failed to decode iv: %v", err)
	}

	dataBytes, err := base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		return nil, fmt.Errorf("failed to decode data: %v", err)
	}

	// 使用 RSA-OAEP 解密 AES 密钥
	aesKey, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privateKey, keyBytes, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to decrypt AES key: %v", err)
	}

	// 使用 AES-256-GCM 解密数据
	// Go 标准库的 AES-GCM 实现
	block, err := aes.NewCipher(aesKey)
	if err != nil {
		return nil, fmt.Errorf("failed to create cipher: %v", err)
	}

	aesgcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, fmt.Errorf("failed to create GCM: %v", err)
	}

	// GCM 的 nonce 就是 IV，密文最后是 tag
	// dataBytes = ciphertext + tag
	tagSize := aesgcm.Overhead()
	if len(dataBytes) < tagSize {
		return nil, fmt.Errorf("ciphertext too short")
	}

	plaintext, err := aesgcm.Open(nil, ivBytes, dataBytes, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to decrypt data: %v", err)
	}

	var payload HashPayNotifyPayload
	if err := json.Unmarshal(plaintext, &payload); err != nil {
		return nil, fmt.Errorf("failed to unmarshal payload: %v", err)
	}

	return &payload, nil
}
