package payment

import (
	"context"
	bizpayment "kra/internal/biz/payment"
	"strings"
	"testing"

	"github.com/go-pay/gopay"
	gopayQQ "github.com/go-pay/gopay/qq"
	"github.com/go-pay/gopay/saobei"
	gopayWechat "github.com/go-pay/gopay/wechat"
)

func TestWechatV2RefundRejectsInvalidRequestBeforeClient(t *testing.T) {
	for _, tc := range []struct {
		name string
		req  *bizpayment.PaymentRefundRequest
		want string
	}{
		{name: "blank trade number", req: &bizpayment.PaymentRefundRequest{TradeNo: "  ", RefundNo: "R1", Amount: 1, TotalAmount: 1}, want: "out_trade_no"},
		{name: "blank refund number", req: &bizpayment.PaymentRefundRequest{TradeNo: "T1", RefundNo: "  ", Amount: 1, TotalAmount: 1}, want: "out_refund_no"},
		{name: "invalid amount", req: &bizpayment.PaymentRefundRequest{TradeNo: "T1", RefundNo: "R1", Amount: 2, TotalAmount: 1}, want: "金额"},
	} {
		t.Run(tc.name, func(t *testing.T) {
			_, err := (&wechatV2Adapter{}).Refund(context.Background(), tc.req, nil)
			if err == nil || !strings.Contains(err.Error(), tc.want) {
				t.Fatalf("Refund() error = %v, want %q", err, tc.want)
			}
		})
	}
}

func TestWechatV2RefundIdentityValidation(t *testing.T) {
	valid := func() *gopayWechat.RefundResponse {
		return &gopayWechat.RefundResponse{OutTradeNo: "T1", OutRefundNo: "R1", RefundId: "WX-R1"}
	}
	if err := validateWechatV2RefundIdentity("T1", "R1", valid()); err != nil {
		t.Fatalf("valid refund response: %v", err)
	}
	for _, tc := range []struct {
		name string
		edit func(*gopayWechat.RefundResponse)
	}{
		{name: "trade mismatch", edit: func(r *gopayWechat.RefundResponse) { r.OutTradeNo = "OTHER" }},
		{name: "refund missing", edit: func(r *gopayWechat.RefundResponse) { r.OutRefundNo = "" }},
		{name: "refund mismatch", edit: func(r *gopayWechat.RefundResponse) { r.OutRefundNo = "OTHER" }},
		{name: "provider refund missing", edit: func(r *gopayWechat.RefundResponse) { r.RefundId = "" }},
	} {
		t.Run(tc.name, func(t *testing.T) {
			rsp := valid()
			tc.edit(rsp)
			if err := validateWechatV2RefundIdentity("T1", "R1", rsp); err == nil {
				t.Fatal("accepted invalid refund identity")
			}
		})
	}
}

func TestQQRefundRejectsInvalidRequestBeforeClient(t *testing.T) {
	for _, tc := range []struct {
		name string
		req  *bizpayment.PaymentRefundRequest
		want string
	}{
		{name: "blank trade number", req: &bizpayment.PaymentRefundRequest{TradeNo: " ", RefundNo: "R1", Amount: 1}, want: "out_trade_no"},
		{name: "blank refund number", req: &bizpayment.PaymentRefundRequest{TradeNo: "T1", RefundNo: " ", Amount: 1}, want: "out_refund_no"},
		{name: "invalid amount", req: &bizpayment.PaymentRefundRequest{TradeNo: "T1", RefundNo: "R1", Amount: 0}, want: "金额"},
	} {
		t.Run(tc.name, func(t *testing.T) {
			_, err := (&qqAdapter{}).Refund(context.Background(), tc.req, nil)
			if err == nil || !strings.Contains(err.Error(), tc.want) {
				t.Fatalf("Refund() error = %v, want %q", err, tc.want)
			}
		})
	}
}

func TestQQRefundIdentityValidation(t *testing.T) {
	valid := func() *gopayQQ.RefundResponse {
		return &gopayQQ.RefundResponse{OutTradeNo: "T1", OutRefundNo: "R1", RefundId: "QQ-R1"}
	}
	if err := validateQQRefundIdentity("T1", "R1", valid()); err != nil {
		t.Fatalf("valid refund response: %v", err)
	}
	for _, tc := range []struct {
		name string
		edit func(*gopayQQ.RefundResponse)
	}{
		{name: "trade mismatch", edit: func(r *gopayQQ.RefundResponse) { r.OutTradeNo = "OTHER" }},
		{name: "refund missing", edit: func(r *gopayQQ.RefundResponse) { r.OutRefundNo = "" }},
		{name: "refund mismatch", edit: func(r *gopayQQ.RefundResponse) { r.OutRefundNo = "OTHER" }},
		{name: "provider refund missing", edit: func(r *gopayQQ.RefundResponse) { r.RefundId = "" }},
	} {
		t.Run(tc.name, func(t *testing.T) {
			rsp := valid()
			tc.edit(rsp)
			if err := validateQQRefundIdentity("T1", "R1", rsp); err == nil {
				t.Fatal("accepted invalid refund identity")
			}
		})
	}
}

func TestSaobeiRefundRejectsInvalidRequestBeforeClient(t *testing.T) {
	for _, tc := range []struct {
		name string
		req  *bizpayment.PaymentRefundRequest
		want string
	}{
		{name: "blank trade number", req: &bizpayment.PaymentRefundRequest{TradeNo: " ", RefundNo: "R1", Amount: 1}, want: "商户订单号"},
		{name: "blank refund number", req: &bizpayment.PaymentRefundRequest{TradeNo: "T1", RefundNo: " ", Amount: 1}, want: "退款单号"},
		{name: "invalid amount", req: &bizpayment.PaymentRefundRequest{TradeNo: "T1", RefundNo: "R1", Amount: 0}, want: "金额"},
	} {
		t.Run(tc.name, func(t *testing.T) {
			clientCreated := false
			adapter := &saobeiAdapter{newClient: func(map[string]any) (saobeiClient, error) {
				clientCreated = true
				return &saobeiClientStub{}, nil
			}}
			_, err := adapter.Refund(context.Background(), tc.req, nil)
			if err == nil || !strings.Contains(err.Error(), tc.want) {
				t.Fatalf("Refund() error = %v, want %q", err, tc.want)
			}
			if clientCreated {
				t.Fatal("Refund() initialized client before validating request")
			}
		})
	}
}

func TestSaobeiRefundRejectsMismatchedRefundNumber(t *testing.T) {
	adapter := newSaobeiTestAdapter(&saobeiClientStub{refund: func(context.Context, gopay.BodyMap) (*saobei.RefundRsp, error) {
		return &saobei.RefundRsp{RspBase: saobei.RspBase{ReturnCode: "01", ResultCode: "01"}, TerminalTrace: "OTHER", OutRefundNo: "SAOBEI-REFUND-1", RefundFee: "25"}, nil
	}})
	_, err := adapter.Refund(context.Background(), &bizpayment.PaymentRefundRequest{TradeNo: "T1", RefundNo: "R1", Amount: 25}, nil)
	if err == nil || !strings.Contains(err.Error(), "terminal_trace") {
		t.Fatalf("Refund() error = %v", err)
	}
}

func TestSaobeiBarcodeCreateTreatsPayingAsPending(t *testing.T) {
	adapter := newSaobeiTestAdapter(&saobeiClientStub{barcodePay: func(context.Context, gopay.BodyMap) (*saobei.BarcodePayRsp, error) {
		return &saobei.BarcodePayRsp{RspBase: saobei.RspBase{ReturnCode: "01", ResultCode: saobei.ResultCodePaying}, OutTradeNo: "SAOBEI-ORDER-1"}, nil
	}})
	result, err := adapter.Create(context.Background(), &bizpayment.PaymentRequest{TradeNo: "LOCAL-1", Amount: 10, Extra: map[string]any{"method": "barcode", "auth_no": "AUTH-1"}}, nil)
	if err != nil {
		t.Fatalf("Create() error = %v", err)
	}
	if result.Status != "pending" {
		t.Fatalf("Create() status = %q, want pending", result.Status)
	}
}
