package payment

import (
	"testing"

	"kra/internal/biz"

	"github.com/go-pay/gopay/lakala"
)

func TestLakalaEndpointResponseCodes(t *testing.T) {
	for _, tc := range []struct {
		name       string
		check      func(string, string) bool
		returnCode string
		resultCode string
		want       bool
	}{
		{name: "create success", check: lakalaCreateResponseOK, returnCode: "SUCCESS", resultCode: "SUCCESS", want: true},
		{name: "create idempotent", check: lakalaCreateResponseOK, returnCode: "SUCCESS", resultCode: "EXISTS", want: true},
		{name: "create business failure", check: lakalaCreateResponseOK, returnCode: "SUCCESS", resultCode: "FAILED"},
		{name: "create transport failure", check: lakalaCreateResponseOK, returnCode: "FAILED", resultCode: "SUCCESS"},
		{name: "create missing result", check: lakalaCreateResponseOK, returnCode: "SUCCESS"},
		{name: "retail paying", check: lakalaRetailCreateResponseOK, returnCode: "SUCCESS", resultCode: "PAYING", want: true},
		{name: "retail paid", check: lakalaRetailCreateResponseOK, returnCode: "SUCCESS", resultCode: "PAY_SUCCESS", want: true},
		{name: "retail business failure", check: lakalaRetailCreateResponseOK, returnCode: "SUCCESS", resultCode: "PAY_FAIL"},
		{name: "query state", check: lakalaQueryResponseOK, returnCode: "SUCCESS", resultCode: "PAY_SUCCESS", want: true},
		{name: "query endpoint rejects generic success", check: lakalaQueryResponseOK, returnCode: "SUCCESS", resultCode: "SUCCESS"},
		{name: "query transport failure", check: lakalaQueryResponseOK, returnCode: "FAILED", resultCode: "PAY_SUCCESS"},
		{name: "refund state", check: lakalaRefundResponseOK, returnCode: "SUCCESS", resultCode: "WAITING", want: true},
		{name: "refund endpoint rejects payment state", check: lakalaRefundResponseOK, returnCode: "SUCCESS", resultCode: "PAY_SUCCESS"},
		{name: "refund transport failure", check: lakalaRefundResponseOK, returnCode: "FAILED", resultCode: "SUCCESS"},
	} {
		t.Run(tc.name, func(t *testing.T) {
			if got := tc.check(tc.returnCode, tc.resultCode); got != tc.want {
				t.Fatalf("response check(%q, %q) = %v, want %v", tc.returnCode, tc.resultCode, got, tc.want)
			}
		})
	}
}

func TestLakalaQueryStateMapping(t *testing.T) {
	for _, tc := range []struct {
		code string
		want string
	}{
		{code: "PAYING", want: "pending"},
		{code: "PAY_SUCCESS", want: "success"},
		{code: "CREATE_FAIL", want: "failed"},
		{code: "CLOSED", want: "failed"},
		{code: "PAY_FAIL", want: "failed"},
		{code: "PARTIAL_REFUND", want: "failed"},
		{code: "FULL_REFUND", want: "failed"},
	} {
		if got := normalizeLakalaState(tc.code); got != tc.want {
			t.Errorf("normalizeLakalaState(%q) = %q, want %q", tc.code, got, tc.want)
		}
	}
}

func TestLakalaRefundResultBindsRefundIdentity(t *testing.T) {
	req := &biz.PaymentRefundRequest{TradeNo: "LOCAL-1", RefundNo: "REFUND-1", Amount: 50, Currency: "JPY"}
	base := func() *lakala.RefundRsp {
		return &lakala.RefundRsp{
			ErrorCode:       lakala.ErrorCode{ReturnCode: "SUCCESS", ResultCode: "SUCCESS"},
			RefundId:        "LK-REFUND-1",
			PartnerRefundId: "REFUND-1",
			Amount:          50,
			Currency:        "JPY",
		}
	}

	result, err := lakalaRefundResult(req, base())
	if err != nil {
		t.Fatalf("lakalaRefundResult() error = %v", err)
	}
	if result.ProviderTradeNo != "LK-REFUND-1" || result.TradeNo != "LOCAL-1" || result.Amount != 50 || result.Currency != "JPY" {
		t.Fatalf("lakalaRefundResult() = %+v", result)
	}

	for _, tc := range []struct {
		name   string
		mutate func(*lakala.RefundRsp)
	}{
		{name: "missing merchant refund id", mutate: func(rsp *lakala.RefundRsp) { rsp.PartnerRefundId = "" }},
		{name: "mismatched merchant refund id", mutate: func(rsp *lakala.RefundRsp) { rsp.PartnerRefundId = "OTHER" }},
		{name: "missing provider refund id", mutate: func(rsp *lakala.RefundRsp) { rsp.RefundId = "" }},
		{name: "mismatched amount", mutate: func(rsp *lakala.RefundRsp) { rsp.Amount++ }},
		{name: "mismatched currency", mutate: func(rsp *lakala.RefundRsp) { rsp.Currency = "USD" }},
	} {
		t.Run(tc.name, func(t *testing.T) {
			rsp := base()
			tc.mutate(rsp)
			if _, err := lakalaRefundResult(req, rsp); err == nil {
				t.Fatal("lakalaRefundResult() accepted an unbound refund response")
			}
		})
	}
}

func TestLakalaRefundResultMapsProviderState(t *testing.T) {
	req := &biz.PaymentRefundRequest{TradeNo: "LOCAL-1", RefundNo: "REFUND-1", Amount: 50, Currency: "JPY"}
	for _, tc := range []struct {
		code string
		want string
	}{
		{code: "WAITING", want: "pending"},
		{code: "SUCCESS", want: "success"},
		{code: "FINISHED", want: "success"},
		{code: "CREATE_FAILED", want: "failed"},
		{code: "FAILED", want: "failed"},
		{code: "CHANGE", want: "failed"},
	} {
		t.Run(tc.code, func(t *testing.T) {
			result, err := lakalaRefundResult(req, &lakala.RefundRsp{
				ErrorCode:       lakala.ErrorCode{ReturnCode: "SUCCESS", ResultCode: tc.code},
				RefundId:        "LK-REFUND-1",
				PartnerRefundId: "REFUND-1",
				Amount:          50,
				Currency:        "JPY",
			})
			if err != nil {
				t.Fatalf("lakalaRefundResult() error = %v", err)
			}
			if result.Status != tc.want {
				t.Fatalf("lakalaRefundResult() status = %q, want %q", result.Status, tc.want)
			}
		})
	}
}
