package payment

import (
	"context"
	"encoding/json"
	"io"
	bizpayment "kra/internal/biz/payment"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	gopayWechatV3 "github.com/go-pay/gopay/wechat/v3"
)

func TestWechatV3CodePayUsesGoPaySDK(t *testing.T) {
	platformKey, _, platformPublicPEM := testRSAKeyPair(t)
	_, merchantPrivatePEM, _ := testRSAKeyPair(t)
	const (
		platformSerial = "PLATFORM-SERIAL"
		timestamp      = "1787068800"
		nonce          = "codepay-response-nonce"
	)
	response := []byte(`{"appid":"wx-test","mchid":"mch-test","out_trade_no":"T-WECHAT-CODEPAY","transaction_id":"WX-CODEPAY-1","trade_type":"MICROPAY","trade_state":"SUCCESS","amount":{"total":123,"payer_total":100,"currency":"CNY","payer_currency":"CNY"},"promotion_detail":[{"coupon_id":"COUPON-1","amount":23}]}`)
	signature := testRSA2Sign(t, platformKey, []byte(timestamp+"\n"+nonce+"\n"+string(response)+"\n"))

	var requestBody map[string]any
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost || r.URL.Path != "/v3/pay/transactions/codepay" {
			t.Errorf("request = %s %s", r.Method, r.URL.Path)
		}
		if strings.TrimSpace(r.Header.Get("Authorization")) == "" {
			t.Error("missing GoPay authorization header")
		}
		body, err := io.ReadAll(r.Body)
		if err != nil {
			t.Errorf("read request: %v", err)
			return
		}
		if err = json.Unmarshal(body, &requestBody); err != nil {
			t.Errorf("decode request: %v", err)
			return
		}

		w.Header().Set(gopayWechatV3.HeaderTimestamp, timestamp)
		w.Header().Set(gopayWechatV3.HeaderNonce, nonce)
		w.Header().Set(gopayWechatV3.HeaderSerial, platformSerial)
		w.Header().Set(gopayWechatV3.HeaderSignature, signature)
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write(response)
	}))
	defer server.Close()

	result, err := (&wechatV3Adapter{}).Create(context.Background(), &bizpayment.PaymentRequest{
		Provider:  bizpayment.PaymentWechatV3,
		TradeNo:   "T-WECHAT-CODEPAY",
		Subject:   "subject",
		Amount:    123,
		Currency:  "CNY",
		NotifyURL: "https://merchant.test/payment/callback",
		Extra: map[string]any{
			"method":    "codepay",
			"auth_code": "AUTH-CODE-1",
			"payer":     map[string]any{"auth_code": "OVERRIDE"},
		},
	}, map[string]any{
		"app_id":             "wx-test",
		"merchant_id":        "mch-test",
		"serial_no":          "merchant-serial",
		"private_key":        merchantPrivatePEM,
		"api_v3_key":         "01234567890123456789012345678901",
		"platform_cert":      platformPublicPEM,
		"platform_serial_no": platformSerial,
		"create_url":         server.URL,
	})
	if err != nil {
		t.Fatal(err)
	}

	payer, _ := requestBody["payer"].(map[string]any)
	amount, _ := requestBody["amount"].(map[string]any)
	if payer["auth_code"] != "AUTH-CODE-1" || requestBody["out_trade_no"] != "T-WECHAT-CODEPAY" {
		t.Fatalf("request identity = %#v", requestBody)
	}
	if amount["total"] != float64(123) || amount["currency"] != "CNY" {
		t.Fatalf("request amount = %#v", amount)
	}
	if result.Provider != bizpayment.PaymentWechatV3 || result.Status != "success" || result.TradeNo != "T-WECHAT-CODEPAY" || result.ProviderTradeNo != "WX-CODEPAY-1" || result.QueryID != "" {
		t.Fatalf("result identity = %+v", result)
	}
	if result.Amount != 123 || result.Currency != "CNY" || result.PayerCurrency != "CNY" || !result.AmountBreakdownKnown || result.PayerPaidAmount != 100 || result.CashPaidAmount != 100 || result.DiscountAmount != 23 {
		t.Fatalf("result amount = %+v", result)
	}
}

func TestWechatV3CodePayAliasesRequireAuthCode(t *testing.T) {
	_, merchantPrivatePEM, _ := testRSAKeyPair(t)
	_, _, platformPublicPEM := testRSAKeyPair(t)
	config := map[string]any{
		"app_id":             "wx-test",
		"merchant_id":        "mch-test",
		"serial_no":          "merchant-serial",
		"private_key":        merchantPrivatePEM,
		"api_v3_key":         "01234567890123456789012345678901",
		"platform_cert":      platformPublicPEM,
		"platform_serial_no": "platform-serial",
	}

	for _, method := range []string{"micropay", "micro_pay", "micro-pay", "codepay", "code_pay", "code-pay", "barcode", "barcode_pay", "facepay", "face_pay"} {
		t.Run(method, func(t *testing.T) {
			_, err := (&wechatV3Adapter{}).Create(context.Background(), &bizpayment.PaymentRequest{
				TradeNo: "T-WECHAT-CODEPAY", Subject: "subject", Amount: 1, Currency: "CNY",
				Extra: map[string]any{"method": method},
			}, config)
			if err == nil || !strings.Contains(err.Error(), "auth_code") {
				t.Fatalf("method %q error = %v", method, err)
			}
		})
	}
}

func TestWechatV3CodePayResultRejectsInvalidIdentity(t *testing.T) {
	_, err := wechatV3CodePayResult("T-WECHAT-CODEPAY", &gopayWechatV3.CodePayRsp{
		Code: gopayWechatV3.Success,
		Response: &gopayWechatV3.CodePay{
			OutTradeNo: "OTHER-ORDER",
			TradeState: "SUCCESS",
			Amount:     &gopayWechatV3.Amount{Total: 1, Currency: "CNY"},
		},
	})
	if err == nil || !strings.Contains(err.Error(), "商户订单号不匹配") {
		t.Fatalf("identity mismatch error = %v", err)
	}
}

func TestWechatV3CodePayResultRequiresMerchantOrderNumber(t *testing.T) {
	_, err := wechatV3CodePayResult("T-WECHAT-CODEPAY", &gopayWechatV3.CodePayRsp{
		Code: gopayWechatV3.Success,
		Response: &gopayWechatV3.CodePay{
			TradeState:    "SUCCESS",
			TransactionId: "WX-CODEPAY-1",
			Amount:        &gopayWechatV3.Amount{Total: 1, Currency: "CNY"},
		},
	})
	if err == nil || !strings.Contains(err.Error(), "out_trade_no") {
		t.Fatalf("missing out_trade_no error = %v", err)
	}
}

func TestWechatV3CodePayResultPreservesFullyDiscountedBreakdown(t *testing.T) {
	result, err := wechatV3CodePayResult("T-WECHAT-CODEPAY", &gopayWechatV3.CodePayRsp{
		Code: gopayWechatV3.Success,
		Response: &gopayWechatV3.CodePay{
			OutTradeNo:    "T-WECHAT-CODEPAY",
			TransactionId: "WX-CODEPAY-1",
			TradeState:    "SUCCESS",
			Amount: &gopayWechatV3.Amount{
				Total: 100, PayerTotal: 0, Currency: "CNY", PayerCurrency: "CNY",
			},
		},
	})
	if err != nil {
		t.Fatal(err)
	}
	if !result.AmountBreakdownKnown || result.PayerPaidAmount != 0 || result.CashPaidAmount != 0 || result.DiscountAmount != 100 || result.PayerCurrency != "CNY" {
		t.Fatalf("fully discounted result = %+v", result)
	}
}

func TestWechatV3CodePayDoesNotInferMissingPayerTotal(t *testing.T) {
	result, err := wechatV3CodePayResult("T-WECHAT-CODEPAY", &gopayWechatV3.CodePayRsp{
		Code:     gopayWechatV3.Success,
		SignInfo: &gopayWechatV3.SignInfo{SignBody: `{"out_trade_no":"T-WECHAT-CODEPAY","transaction_id":"WX-CODEPAY-2","trade_state":"SUCCESS","amount":{"total":100,"currency":"CNY"}}`},
		Response: &gopayWechatV3.CodePay{
			OutTradeNo:    "T-WECHAT-CODEPAY",
			TransactionId: "WX-CODEPAY-2",
			TradeState:    "SUCCESS",
			Amount:        &gopayWechatV3.Amount{Total: 100, Currency: "CNY"},
		},
	})
	if err != nil {
		t.Fatal(err)
	}
	if result.AmountBreakdownKnown || result.PayerPaidAmount != 0 || result.DiscountAmount != 0 {
		t.Fatalf("missing payer_total was treated as a complete breakdown: %+v", result)
	}
}

func TestWechatV3CodePayUserPayingRemainsPending(t *testing.T) {
	result, err := wechatV3CodePayResult("T-WECHAT-CODEPAY", &gopayWechatV3.CodePayRsp{
		Code: gopayWechatV3.Success,
		Response: &gopayWechatV3.CodePay{
			OutTradeNo: "T-WECHAT-CODEPAY",
			TradeState: "USERPAYING",
		},
	})
	if err != nil {
		t.Fatal(err)
	}
	if result.Status != "pending" || result.TradeNo != "T-WECHAT-CODEPAY" || result.ProviderTradeNo != "" || result.QueryID != "" {
		t.Fatalf("pending result = %+v", result)
	}
}
