package sales

import (
	"context"
	"encoding/json"
	"net/http"
	"strings"
	"testing"
)

// The Snap finish redirect must carry ?order=<sales order id>. The checkout
// confirmation page resolves the order from that query param and renders
// "Pesanan tidak ditemukan" when it is absent, so a bare static finish URL
// breaks every Snap return. Midtrans appends its own transaction_id /
// transaction_status params to whatever we send, so ours only has to lead.
func TestFinishURLForOrder(t *testing.T) {
	cases := []struct {
		name, base, want string
	}{
		{
			name: "empty base yields no callbacks",
			base: "",
			want: "",
		},
		{
			name: "appends ?order= when no query is present",
			base: "https://b2b.example/checkout/confirmation",
			want: "https://b2b.example/checkout/confirmation?order=42",
		},
		{
			name: "uses & when a query already exists",
			base: "https://b2b.example/checkout/confirmation?src=snap",
			want: "https://b2b.example/checkout/confirmation?src=snap&order=42",
		},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			if got := finishURLForOrder(c.base, 42); got != c.want {
				t.Errorf("finishURLForOrder(%q, 42) = %q, want %q", c.base, got, c.want)
			}
		})
	}
}

// snapSvcWithFinish mirrors snapSvcWithStub but supplies a FinishURL, which the
// shared helper deliberately leaves empty.
func snapSvcWithFinish(t *testing.T, s *Service, finishURL string) *snapStubRT {
	t.Helper()
	st := &snapStubRT{t: t, key: "test-server-key"}
	s.WithMidtrans(MidtransConfig{
		ServerKey:   "test-server-key",
		SnapBaseURL: "http://example.test/snap",
		FinishURL:   finishURL,
	}).WithHTTPClient(&http.Client{Transport: st})
	return st
}

type snapCallbacks struct {
	Callbacks struct {
		Finish   string `json:"finish"`
		Error    string `json:"error"`
		Unfinish string `json:"unfinish"`
	} `json:"callbacks"`
}

func TestSnapToken_FinishURLCarriesOrderID(t *testing.T) {
	s := svc()
	o, _ := snapOrderWithInvoice(t, s)
	st := snapSvcWithFinish(t, s, "https://b2b.example/checkout/confirmation")

	if _, err := s.SnapToken(context.Background(), o.ID, 42); err != nil {
		t.Fatal(err)
	}

	var payload snapCallbacks
	if err := json.Unmarshal(st.lastBody, &payload); err != nil {
		t.Fatalf("snap request body = %s, want callbacks JSON: %v", st.lastBody, err)
	}

	wantSuffix := "?order=" + itoa2(o.ID)
	for _, tc := range []struct{ name, got string }{
		{"finish", payload.Callbacks.Finish},
		{"error", payload.Callbacks.Error},
		{"unfinish", payload.Callbacks.Unfinish},
	} {
		if !strings.HasSuffix(tc.got, wantSuffix) {
			t.Errorf("callbacks.%s = %q, want it to end with %q", tc.name, tc.got, wantSuffix)
		}
		if strings.Contains(tc.got, "KM-") {
			t.Errorf("callbacks.%s = %q, must carry the sales order id, not the Snap order_ref", tc.name, tc.got)
		}
	}
}

// An unset FinishURL must omit the callbacks block entirely rather than send a
// bare or relative URL to Midtrans.
func TestSnapToken_NoFinishURLOmitsCallbacks(t *testing.T) {
	s := svc()
	o, _ := snapOrderWithInvoice(t, s)
	st := snapSvcWithFinish(t, s, "")

	if _, err := s.SnapToken(context.Background(), o.ID, 42); err != nil {
		t.Fatal(err)
	}

	var payload snapCallbacks
	if err := json.Unmarshal(st.lastBody, &payload); err != nil {
		t.Fatalf("snap request body = %s, want JSON: %v", st.lastBody, err)
	}
	if payload.Callbacks.Finish != "" || payload.Callbacks.Error != "" || payload.Callbacks.Unfinish != "" {
		t.Errorf("callbacks = %+v, want empty when FinishURL is unset", payload.Callbacks)
	}
}
