package controller

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/QuantumNous/new-api/common"
	"github.com/QuantumNous/new-api/model"
	"github.com/QuantumNous/new-api/setting"
	"github.com/glebarez/sqlite"
	"github.com/gin-gonic/gin"
	"github.com/go-pay/gopay/wechat/v3"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gorm.io/gorm"
)

func setupWechatControllerDB(t *testing.T) *gorm.DB {
	t.Helper()
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	require.NoError(t, err)
	sqlDB, _ := db.DB()
	sqlDB.SetMaxOpenConns(1)

	origDB := model.DB
	origLogDB := model.LOG_DB
	model.DB = db
	model.LOG_DB = db
	common.UsingSQLite = true
	common.RedisEnabled = false
	common.QuotaPerUnit = 500 * 1000.0

	require.NoError(t, db.AutoMigrate(&model.User{}, &model.TopUp{}, &model.Log{}))

	t.Cleanup(func() {
		model.DB = origDB
		model.LOG_DB = origLogDB
		sqlDB.Close()
	})
	return db
}

func createTestUser(t *testing.T, db *gorm.DB, id int, username string) *model.User {
	user := &model.User{
		Id:       id,
		Username: username,
		Password: "hashed",
		Quota:    100000,
		Role:     common.RoleCommonUser,
		Status:   common.UserStatusEnabled,
		AffCode:  "aff_test_" + username,
	}
	require.NoError(t, db.Create(user).Error)
	return user
}

func newTestContextWithUser(userID int) (*gin.Context, *httptest.ResponseRecorder) {
	gin.SetMode(gin.TestMode)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Set("id", userID)
	return c, w
}

func setupWechatTestRouter() *gin.Engine {
	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.POST("/api/user/wechat/pay/amount", RequestWechatPayAmount)
	r.POST("/api/user/wechat/pay", RequestWechatPay)
	r.GET("/api/user/wechat/pay/status", WechatPayStatus)
	return r
}

// ========== RequestWechatPayAmount 测试 ==========

func TestRequestWechatPayAmount_Success(t *testing.T) {
	// 此测试验证 getWechatPayMoney 核心计算逻辑
	// RequestWechatPayAmount 依赖 GetUserGroup 等完整数据库，在集成测试中覆盖
	origUnitPrice := setting.WechatPayUnitPrice
	setting.WechatPayUnitPrice = 7.0
	t.Cleanup(func() {
		setting.WechatPayUnitPrice = origUnitPrice
	})

	// 测试不同金额的计算
	result := getWechatPayMoney(10.0, "default")
	assert.InDelta(t, 70.0, result, 0.01)

	result = getWechatPayMoney(1.0, "default")
	assert.InDelta(t, 7.0, result, 0.01)

	result = getWechatPayMoney(100.0, "default")
	assert.InDelta(t, 700.0, result, 0.01)
}

func TestRequestWechatPayAmount_BelowMinTopup(t *testing.T) {
	setupWechatControllerDB(t)

	origMinTopUp := setting.WechatPayMinTopUp
	setting.WechatPayMinTopUp = 5
	t.Cleanup(func() {
		setting.WechatPayMinTopUp = origMinTopUp
	})

	body, _ := json.Marshal(WechatPayRequest{Amount: 3})
	req := httptest.NewRequest(http.MethodPost, "/api/user/wechat/pay/amount", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("id", 400)

	RequestWechatPayAmount(c)

	var resp map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
	assert.Equal(t, "error", resp["message"])
}

func TestRequestWechatPayAmount_InvalidBody(t *testing.T) {
	setupWechatControllerDB(t)

	req := httptest.NewRequest(http.MethodPost, "/api/user/wechat/pay/amount", bytes.NewReader([]byte("invalid")))
	req.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("id", 400)

	RequestWechatPayAmount(c)

	var resp map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
	assert.Equal(t, "error", resp["message"])
	assert.Equal(t, "参数错误", resp["data"])
}

// ========== RequestWechatPay 测试 ==========

func TestRequestWechatPay_NotConfigured(t *testing.T) {
	setupWechatControllerDB(t)

	// 确保微信支付未配置
	origAppID := setting.WechatPayAppID
	setting.WechatPayAppID = ""
	t.Cleanup(func() {
		setting.WechatPayAppID = origAppID
	})

	body, _ := json.Marshal(WechatPayRequest{Amount: 10})
	req := httptest.NewRequest(http.MethodPost, "/api/user/wechat/pay", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("id", 400)

	RequestWechatPay(c)

	var resp map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
	assert.Equal(t, "error", resp["message"])
	assert.Equal(t, "微信支付未配置", resp["data"])
}

func TestRequestWechatPay_ExceedsMaxAmount(t *testing.T) {
	setupWechatControllerDB(t)

	body, _ := json.Marshal(WechatPayRequest{Amount: 20000})
	req := httptest.NewRequest(http.MethodPost, "/api/user/wechat/pay", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("id", 400)

	RequestWechatPay(c)

	var resp map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
	assert.Equal(t, "error", resp["message"])
	assert.Contains(t, resp["data"], "不能大于 10000")
}

func TestRequestWechatPay_BelowMinAmount(t *testing.T) {
	setupWechatControllerDB(t)

	origMinTopUp := setting.WechatPayMinTopUp
	setting.WechatPayMinTopUp = 5
	t.Cleanup(func() {
		setting.WechatPayMinTopUp = origMinTopUp
	})

	body, _ := json.Marshal(WechatPayRequest{Amount: 3})
	req := httptest.NewRequest(http.MethodPost, "/api/user/wechat/pay", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("id", 400)

	RequestWechatPay(c)

	var resp map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
	assert.Equal(t, "error", resp["message"])
}

// ========== WechatPayStatus 测试 ==========

func TestWechatPayStatus_Success(t *testing.T) {
	db := setupWechatControllerDB(t)
	user := createTestUser(t, db, 401, "wx_status_test")

	// 创建订单
	topUp := &model.TopUp{
		UserId:        user.Id,
		Amount:        10,
		Money:         70.0,
		TradeNo:       "wx_status_trade_001",
		PaymentMethod: PaymentMethodWechatPay,
		CreateTime:    common.GetTimestamp(),
		Status:        common.TopUpStatusSuccess,
	}
	require.NoError(t, db.Create(topUp).Error)

	req := httptest.NewRequest(http.MethodGet, "/api/user/wechat/pay/status?trade_no=wx_status_trade_001", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("id", user.Id)

	WechatPayStatus(c)

	assert.Equal(t, 200, w.Code)
	var resp map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
	assert.Equal(t, "success", resp["message"])

	data := resp["data"].(map[string]interface{})
	assert.Equal(t, common.TopUpStatusSuccess, data["status"])
}

func TestWechatPayStatus_OrderNotFound(t *testing.T) {
	setupWechatControllerDB(t)

	req := httptest.NewRequest(http.MethodGet, "/api/user/wechat/pay/status?trade_no=nonexistent", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("id", 401)

	WechatPayStatus(c)

	var resp map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
	assert.Equal(t, "error", resp["message"])
	assert.Equal(t, "订单不存在", resp["data"])
}

func TestWechatPayStatus_WrongUser(t *testing.T) {
	db := setupWechatControllerDB(t)
	createTestUser(t, db, 402, "wx_status_owner")
	// 用户 403 尝试查询 402 的订单
	createTestUser(t, db, 403, "wx_status_other")

	topUp := &model.TopUp{
		UserId:        402,
		Amount:        10,
		Money:         70.0,
		TradeNo:       "wx_status_trade_wrong_user",
		PaymentMethod: PaymentMethodWechatPay,
		CreateTime:    common.GetTimestamp(),
		Status:        common.TopUpStatusPending,
	}
	require.NoError(t, db.Create(topUp).Error)

	req := httptest.NewRequest(http.MethodGet, "/api/user/wechat/pay/status?trade_no=wx_status_trade_wrong_user", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("id", 403) // 不同用户

	WechatPayStatus(c)

	var resp map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
	assert.Equal(t, "error", resp["message"])
	assert.Equal(t, "订单不存在", resp["data"]) // 不泄露订单存在信息
}

func TestWechatPayStatus_MissingTradeNo(t *testing.T) {
	setupWechatControllerDB(t)

	req := httptest.NewRequest(http.MethodGet, "/api/user/wechat/pay/status", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("id", 401)

	WechatPayStatus(c)

	var resp map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
	assert.Equal(t, "error", resp["message"])
	assert.Equal(t, "参数错误", resp["data"])
}

// ========== getWechatPayMoney 测试 ==========

func TestGetWechatPayMoney(t *testing.T) {
	origUnitPrice := setting.WechatPayUnitPrice
	setting.WechatPayUnitPrice = 7.0
	t.Cleanup(func() {
		setting.WechatPayUnitPrice = origUnitPrice
	})

	tests := []struct {
		name     string
		amount   float64
		group    string
		expected float64
	}{
		{"标准计算", 10, "default", 70.0},
		{"最小金额", 1, "default", 7.0},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := getWechatPayMoney(tt.amount, tt.group)
			assert.InDelta(t, tt.expected, result, 0.01)
		})
	}
}

// ========== getWechatMinTopup 测试 ==========

func TestGetWechatMinTopup(t *testing.T) {
	origMinTopUp := setting.WechatPayMinTopUp
	setting.WechatPayMinTopUp = 1
	t.Cleanup(func() {
		setting.WechatPayMinTopUp = origMinTopUp
	})

	result := getWechatMinTopup()
	assert.Equal(t, int64(1), result)
}

// ========== ResetWechatPayClient 测试 ==========

func TestResetWechatPayClient(t *testing.T) {
	// 确保重置后客户端为 nil
	wechatPayClient = nil
	ResetWechatPayClient()
	assert.Nil(t, wechatPayClient)
}

func TestResetWechatPayClient_CallbackRegistered(t *testing.T) {
	// 验证 init() 注册了回调
	assert.NotNil(t, setting.OnWechatPayConfigChanged)
}

// ========== PaymentMethodWechatPay 常量测试 ==========

func TestPaymentMethodWechatPay(t *testing.T) {
	assert.Equal(t, "wechat_pay", PaymentMethodWechatPay)
}

// ========== Base64 密钥解析测试 ==========

func TestBase64PemDecoding(t *testing.T) {
	// 测试：PEM 文件整体 Base64 编码 → 解码后应能得到有效 PEM
	keyPath := "C:/Users/28221/Downloads/WXCertUtil/cert/1602799864_20260402_cert/apiclient_key.pem"
	pubKeyPath := "C:/Users/28221/Downloads/pub_key.pem"

	// 读取原始文件
	origKeyBytes, err := os.ReadFile(keyPath)
	require.NoError(t, err, "读取私钥文件失败")
	t.Logf("原始私钥前30字符: %s", string(origKeyBytes[:30]))

	// 模拟 getWechatPayClient 的逻辑：base64 编码 → 存入配置 → 解码
	encoded := base64.StdEncoding.EncodeToString(origKeyBytes)
	t.Logf("Base64 编码后长度: %d", len(encoded))

	// 解码
	decoded, err := base64.StdEncoding.DecodeString(encoded)
	require.NoError(t, err, "Base64 解码失败")
	assert.Equal(t, string(origKeyBytes), string(decoded), "解码后应与原始一致")

	// 验证解码后确实是 PEM 格式
	assert.Contains(t, string(decoded), "-----BEGIN", "解码后应包含 PEM 头")
	t.Logf("解码后前30字符: %s", string(decoded[:30]))

	// 测试 gopay 能否接受这个格式
	mchID := "1602799864"
	serialNo := "1ABE61AF2D4FA785F2782320B001D8B2E5517840"
	apiV3Key := "Pr79nX6Ej4Xw4eReJ7KM7NhE4Hezec3y"

	client, err := wechat.NewClientV3(mchID, serialNo, apiV3Key, string(decoded))
	if err != nil {
		t.Errorf("NewClientV3 失败: %v", err)
	} else {
		t.Log("NewClientV3 成功")
		_ = client
	}

	// 读取公钥
	pubKeyBytes, err := os.ReadFile(pubKeyPath)
	require.NoError(t, err, "读取公钥文件失败")
	t.Logf("原始公钥前30字符: %s", string(pubKeyBytes[:30]))

	// Base64 编码公钥
	encodedPub := base64.StdEncoding.EncodeToString(pubKeyBytes)
	decodedPub, err := base64.StdEncoding.DecodeString(encodedPub)
	require.NoError(t, err)
	assert.Contains(t, string(decodedPub), "-----BEGIN", "解码后应包含 PEM 头")
}

func TestWrapAsPEM(t *testing.T) {
	// Case 1: PEM 格式输入应原样返回
	pemInput := "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkq\n-----END PRIVATE KEY-----"
	result := wrapAsPEM([]byte(pemInput), "PRIVATE KEY")
	assert.Contains(t, result, "-----BEGIN PRIVATE KEY-----")

	// Case 2: 裸 DER 字节应自动包装
	rawDER := []byte{0x30, 0x82, 0x02, 0x5c}
	result = wrapAsPEM(rawDER, "PRIVATE KEY")
	assert.Contains(t, result, "-----BEGIN PRIVATE KEY-----")
	assert.Contains(t, result, "-----END PRIVATE KEY-----")

	// Case 3: PEM 内层 base64 文本（双重编码场景）应正确解码为 DER 再包装
	// 模拟：原始 DER → base64 得到 "MIIEvAI..." 文本 → 作为输入
	innerB64 := base64.StdEncoding.EncodeToString(rawDER)
	result = wrapAsPEM([]byte(innerB64), "PRIVATE KEY")
	assert.Contains(t, result, "-----BEGIN PRIVATE KEY-----")
	// 验证内层内容解码后等于原始 DER
	block, _ := pem.Decode([]byte(result))
	assert.Equal(t, rawDER, block.Bytes)

	// Case 4: 用实际私钥文件测试内层 base64 场景
	keyPath := "C:/Users/28221/Downloads/WXCertUtil/cert/1602799864_20260402_cert/apiclient_key.pem"
	if keyBytes, err := os.ReadFile(keyPath); err == nil {
		// 提取 PEM 内层的 base64 文本
		block, _ := pem.Decode(keyBytes)
		require.NotNil(t, block, "原始 PEM 解析失败")
		innerB64 := base64.StdEncoding.EncodeToString(block.Bytes)

		// 用内层 base64 文本作为输入
		result := wrapAsPEM([]byte(innerB64), "PRIVATE KEY")
		assert.Contains(t, result, "-----BEGIN PRIVATE KEY-----")

		// 验证 gopay 能接受
		_, err := wechat.NewClientV3("test_mch", "test_serial", "test_key", result)
		t.Logf("gopay NewClientV3 with inner-b64 input: err=%v", err)
	}
}
