package leshua

import (
	"strings"
	"testing"

	"hfb_sys/backend/internal/config"
)

func TestSignUsesASCIISortedNonEmptyParams(t *testing.T) {
	params := map[string]string{
		"service":        "get_tdcode",
		"merchant_id":    "1234567890",
		"third_order_id": "NO1",
		"amount":         "100",
		"nonce_str":      "abc",
		"empty":          "",
		"sign":           "ignored",
	}

	got := Sign(params, "secret", SignOptions{})
	want := "1E7892034AA77899E8DD609C4FA09E76"
	if got != want {
		t.Fatalf("Sign() = %s, want %s", got, want)
	}

	baseString := SignBaseString(params, SignOptions{})
	wantBaseString := "amount=100&merchant_id=1234567890&nonce_str=abc&service=get_tdcode&third_order_id=NO1"
	if baseString != wantBaseString {
		t.Fatalf("SignBaseString() = %s, want %s", baseString, wantBaseString)
	}
}

func TestVerifyNotifyIncludesEmptyAndExcludesErrorCode(t *testing.T) {
	client := NewClient(config.LeshuaPaymentConfig{NotifyKey: "notify-secret"})
	params := map[string]string{
		"merchant_id":     "1234567890",
		"third_order_id":  "NO1",
		"leshua_order_id": "LS1",
		"amount":          "100",
		"status":          "2",
		"attach":          "",
		"leshua":          "",
		"error_code":      "-20001",
	}
	params["sign"] = Sign(params, "notify-secret", SignOptions{
		IncludeEmpty: true,
		ExcludeKeys:  []string{"error_code", "leshua", "sign"},
	})

	if !client.VerifyNotify(params) {
		t.Fatal("VerifyNotify() = false, want true")
	}

	params["amount"] = "101"
	if client.VerifyNotify(params) {
		t.Fatal("VerifyNotify() = true after amount changed, want false")
	}
}

func TestVerifyNotifyDoesNotFallBackToSignKey(t *testing.T) {
	client := NewClient(config.LeshuaPaymentConfig{
		NotifyKey: "wrong-notify-secret",
		SignKey:   "sign-secret",
	})
	params := map[string]string{
		"merchant_id":     "1234567890",
		"third_order_id":  "NO1",
		"leshua_order_id": "LS1",
		"amount":          "100",
		"status":          "2",
	}
	params["sign"] = Sign(params, "sign-secret", SignOptions{
		IncludeEmpty: true,
		ExcludeKeys:  []string{"error_code", "sign"},
	})

	result := client.VerifyNotifyDetail(params)
	if result.OK {
		t.Fatal("VerifyNotifyDetail().OK = true, want false")
	}
	if _, ok := result.Expected["sign_key"]; ok {
		t.Fatal("VerifyNotifyDetail() unexpectedly used sign_key fallback")
	}
}

func TestVerifyNotifyUsesDocumentedNotifySignature(t *testing.T) {
	client := NewClient(config.LeshuaPaymentConfig{NotifyKey: "notify-secret"})
	params := map[string]string{
		"merchant_id":     "1234567890",
		"third_order_id":  "NO1",
		"leshua_order_id": "LS1",
		"amount":          "100",
		"status":          "2",
		"sign_type":       "MD5",
	}
	params["sign"] = Sign(params, "notify-secret", SignOptions{
		IncludeEmpty: true,
		ExcludeKeys:  []string{"error_code", "leshua", "sign"},
	})

	result := client.VerifyNotifyDetail(params)
	if !result.OK {
		t.Fatal("VerifyNotifyDetail().OK = false, want true")
	}
	if result.MatchedKey != "notify_key" {
		t.Fatalf("MatchedKey = %s, want notify_key", result.MatchedKey)
	}
}

func TestVerifyNotifyKeepsEmptyXMLFieldsInSignature(t *testing.T) {
	client := NewClient(config.LeshuaPaymentConfig{NotifyKey: "notify-secret"})
	params, err := ParsePayload([]byte(`<leshua>
<amount>100</amount>
<goods_tag></goods_tag>
<merchant_id>1234567890</merchant_id>
<sign_type>MD5</sign_type>
<status>2</status>
<third_order_id>NO1</third_order_id>
</leshua>`))
	if err != nil {
		t.Fatalf("ParsePayload(xml) error = %v", err)
	}
	params["sign"] = Sign(params, "notify-secret", SignOptions{
		IncludeEmpty: true,
		ExcludeKeys:  []string{"error_code", "leshua", "sign"},
	})

	result := client.VerifyNotifyDetail(params)
	if !result.OK {
		t.Fatal("VerifyNotifyDetail().OK = false, want true")
	}
	baseString := result.BaseString[result.MatchedKey]
	if !strings.Contains(baseString, "goods_tag=") {
		t.Fatalf("baseString = %s, want goods_tag included", baseString)
	}
}

func TestParsePayloadSupportsFormAndXML(t *testing.T) {
	form, err := ParsePayload([]byte("third_order_id=NO1&status=2&amount=100"))
	if err != nil {
		t.Fatalf("ParsePayload(form) error = %v", err)
	}
	if form["third_order_id"] != "NO1" || form["status"] != "2" || form["amount"] != "100" {
		t.Fatalf("ParsePayload(form) = %#v", form)
	}

	xml, err := ParsePayload([]byte("<xml><third_order_id>NO2</third_order_id><status>6</status><goods_tag></goods_tag><coupon/></xml>"))
	if err != nil {
		t.Fatalf("ParsePayload(xml) error = %v", err)
	}
	if xml["third_order_id"] != "NO2" || xml["status"] != "6" {
		t.Fatalf("ParsePayload(xml) = %#v", xml)
	}
	if value, ok := xml["goods_tag"]; !ok || value != "" {
		t.Fatalf("ParsePayload(xml).goods_tag = %q, exists=%v; want empty value", value, ok)
	}
	if value, ok := xml["coupon"]; !ok || value != "" {
		t.Fatalf("ParsePayload(xml).coupon = %q, exists=%v; want empty value", value, ok)
	}
}

func TestSignRefundUsesSameAlgorithm(t *testing.T) {
	params := map[string]string{
		"service":            "unified_refund",
		"merchant_id":        "1234567890",
		"merchant_refund_id": "REF001",
		"refund_amount":      "100",
		"leshua_order_id":    "LS1",
		"nonce_str":          "abc",
		"sign":               "ignored",
	}
	got := Sign(params, "secret", SignOptions{})
	baseString := SignBaseString(params, SignOptions{})
	wantBaseString := "leshua_order_id=LS1&merchant_id=1234567890&merchant_refund_id=REF001&nonce_str=abc&refund_amount=100&service=unified_refund"
	if baseString != wantBaseString {
		t.Fatalf("SignBaseString() = %s, want %s", baseString, wantBaseString)
	}
	want := Sign(map[string]string{
		"service":            "unified_refund",
		"merchant_id":        "1234567890",
		"merchant_refund_id": "REF001",
		"refund_amount":      "100",
		"leshua_order_id":    "LS1",
		"nonce_str":          "abc",
	}, "secret", SignOptions{})
	if got != want {
		t.Fatalf("Sign() = %s, want %s", got, want)
	}
}

func TestVerifyRefundNotify(t *testing.T) {
	client := NewClient(config.LeshuaPaymentConfig{NotifyKey: "notify-secret"})
	params := map[string]string{
		"merchant_id":        "1234567890",
		"third_order_id":     "NO1",
		"leshua_order_id":    "LS1",
		"merchant_refund_id": "REF001",
		"leshua_refund_id":   "LREF001",
		"refund_amount":      "100",
		"total_amount":       "200",
		"status":             "11",
		"attach":             "",
	}
	params["sign"] = Sign(params, "notify-secret", SignOptions{
		IncludeEmpty: true,
		ExcludeKeys:  []string{"error_code", "leshua", "sign"},
	})

	if !client.VerifyNotify(params) {
		t.Fatal("VerifyNotify() = false, want true for refund notify")
	}

	params["status"] = "12"
	if client.VerifyNotify(params) {
		t.Fatal("VerifyNotify() = true after status changed without re-sign, want false")
	}
}
