package robokassa

import (
	"net/url"
	"strings"
	"testing"

	"github.com/google/uuid"
)

func testCfg() Config {
	return Config{MerchantLogin: "shop", Password1: "p1", Password2: "p2", IsTest: true}
}

// resultSig computes the Pass2 Result signature Robokassa would send for a callback carrying only
// Shp_order — the fixture the verifier must accept.
func resultSig(pass2, outSum, invID string, orderID uuid.UUID) string {
	return sign(outSum + ":" + invID + ":" + pass2 + ":Shp_order=" + orderID.String())
}

func TestPaymentURL(t *testing.T) {
	cfg := testCfg()
	id := uuid.New()
	u, err := url.Parse(cfg.PaymentURL(id, "149.00", "10 chips"))
	if err != nil {
		t.Fatalf("parse payment url: %v", err)
	}
	q := u.Query()
	if got := q.Get("OutSum"); got != "149.00" {
		t.Errorf("OutSum = %q, want 149.00", got)
	}
	if got := q.Get("InvId"); got != "0" {
		t.Errorf("InvId = %q, want 0 (unused)", got)
	}
	if got := q.Get("Shp_order"); got != id.String() {
		t.Errorf("Shp_order = %q, want the order id", got)
	}
	if got := q.Get("IsTest"); got != "1" {
		t.Errorf("IsTest = %q, want 1 (test shop)", got)
	}
	want := sign("shop:149.00:0:p1:Shp_order=" + id.String())
	if got := q.Get("SignatureValue"); got != want {
		t.Errorf("SignatureValue = %q, want %q", got, want)
	}
}

func TestVerifyResult(t *testing.T) {
	cfg := testCfg()
	id := uuid.New()

	valid := func() url.Values {
		v := url.Values{}
		v.Set("OutSum", "149.00")
		v.Set("InvId", "0")
		v.Set("Shp_order", id.String())
		// Robokassa sends the hash uppercase; the verifier must be case-insensitive.
		v.Set("SignatureValue", strings.ToUpper(resultSig("p2", "149.00", "0", id)))
		return v
	}

	gotID, gotSum, ok := cfg.VerifyResult(valid())
	if !ok || gotID != id || gotSum != "149.00" {
		t.Fatalf("VerifyResult(valid) = %v/%q/%v, want %v/149.00/true", gotID, gotSum, ok, id)
	}

	// A tampered amount breaks the signature.
	tampered := valid()
	tampered.Set("OutSum", "1.00")
	if _, _, ok := cfg.VerifyResult(tampered); ok {
		t.Error("VerifyResult accepted a tampered amount")
	}

	// The wrong Password2 (a forged callback) does not verify.
	wrongPass := Config{MerchantLogin: "shop", Password1: "p1", Password2: "other"}
	if _, _, ok := wrongPass.VerifyResult(valid()); ok {
		t.Error("VerifyResult accepted a signature under the wrong password")
	}

	// A missing Shp_order is rejected (no order to credit).
	noOrder := valid()
	noOrder.Del("Shp_order")
	if _, _, ok := cfg.VerifyResult(noOrder); ok {
		t.Error("VerifyResult accepted a callback with no order")
	}
}
