package yookassa

import (
	"strings"
	"testing"
)

func testShops() Shops {
	return Shops{
		ChannelWeb:     {ShopID: "100500", SecretKey: "web-secret"},
		ChannelAndroid: {ShopID: "100700", SecretKey: "android-secret"},
	}
}

func TestShopsShopRouting(t *testing.T) {
	s := testShops()
	for channel, wantShopID := range map[string]string{
		ChannelWeb:     "100500",
		ChannelAndroid: "100700",
		"ios":          "100500", // an unconfigured channel falls back to web
		"":             "100500", // so does an unknown platform subtype
	} {
		got, ok := s.Shop(channel)
		if !ok || got.ShopID != wantShopID {
			t.Errorf("Shop(%q) = %q ok=%v, want %q", channel, got.ShopID, ok, wantShopID)
		}
	}
}

func TestShopsShopWithoutWebFallback(t *testing.T) {
	// Only android configured: an unknown channel has nowhere safe to fall back to.
	s := Shops{ChannelAndroid: {ShopID: "100700", SecretKey: "android-secret"}}
	if got, ok := s.Shop(ChannelAndroid); !ok || got.ShopID != "100700" {
		t.Errorf("Shop(android) = %q ok=%v, want the android shop", got.ShopID, ok)
	}
	if _, ok := s.Shop("ios"); ok {
		t.Error("Shop(ios) resolved with no web shop configured")
	}
	if _, ok := (Shops{}).Shop(ChannelWeb); ok {
		t.Error("an empty set resolved a shop")
	}
}

func TestShopsByShopIDDoesNotGuess(t *testing.T) {
	s := testShops()
	channel, cfg, ok := s.ByShopID("100700")
	if !ok || channel != ChannelAndroid || cfg.SecretKey != "android-secret" {
		t.Errorf("ByShopID(100700) = %q/%q ok=%v, want the android shop", channel, cfg.SecretKey, ok)
	}
	// An unrecognised shop id means the notification was not meant for us; guessing at credentials
	// would verify a foreign payment against our own shop.
	for _, id := range []string{"999999", ""} {
		if _, _, ok := s.ByShopID(id); ok {
			t.Errorf("ByShopID(%q) resolved a shop", id)
		}
	}
}

func TestShopsIgnoreHalfConfiguredEntries(t *testing.T) {
	s := Shops{
		ChannelWeb:     {ShopID: "100500"}, // no secret key
		ChannelAndroid: {SecretKey: "android-secret"},
	}
	if s.Configured() {
		t.Error("Configured() = true with no complete shop")
	}
	if _, ok := s.Shop(ChannelWeb); ok {
		t.Error("Shop(web) resolved a shop with no secret key")
	}
	if _, _, ok := s.ByShopID("100500"); ok {
		t.Error("ByShopID matched a shop with no secret key")
	}
}

func TestShopsConfigured(t *testing.T) {
	if !testShops().Configured() {
		t.Error("Configured() = false with two complete shops")
	}
	if (Shops{}).Configured() {
		t.Error("Configured() = true for an empty set")
	}
}

func TestSingleItemReceiptTruncatesLongTitles(t *testing.T) {
	long := strings.Repeat("ф", 200) // Cyrillic: truncation must count runes, not bytes
	r := SingleItemReceipt("buyer@example.test", long, Amount{Value: "1.00", Currency: "RUB"}, VatCodeNone)
	got := []rune(r.Items[0].Description)
	if len(got) != maxDescriptionRunes {
		t.Errorf("description = %d runes, want %d", len(got), maxDescriptionRunes)
	}
	for _, c := range got {
		if c != 'ф' {
			t.Fatalf("truncation cut a multi-byte rune: %q", string(got))
		}
	}
	if r.Items[0].Quantity != 1 {
		t.Errorf("quantity = %v, want 1 (a chip pack is bought once)", r.Items[0].Quantity)
	}
}

func TestValidVatCode(t *testing.T) {
	// VatCodeOff is a legal configuration: it means "send no receipt", the state a merchant outside
	// 54-ФЗ runs in.
	for _, code := range []int{VatCodeOff, 1, 4, 11, 12} {
		if !ValidVatCode(code) {
			t.Errorf("vat code %d rejected, want accepted", code)
		}
	}
	for _, code := range []int{-1, 13} {
		if ValidVatCode(code) {
			t.Errorf("vat code %d accepted, want rejected", code)
		}
	}
}

func TestReceiptEnabled(t *testing.T) {
	if ReceiptEnabled(VatCodeOff) {
		t.Error("receipts reported enabled with no VAT rate configured")
	}
	for _, code := range []int{1, 4, 11} {
		if !ReceiptEnabled(code) {
			t.Errorf("receipts reported disabled for vat code %d", code)
		}
	}
}
