package payments

import "testing"

func TestMoneyFromMinor(t *testing.T) {
	m, err := MoneyFromMinor(14950, CurrencyRUB)
	if err != nil {
		t.Fatalf("MoneyFromMinor: %v", err)
	}
	if m.Minor() != 14950 || m.Currency() != CurrencyRUB {
		t.Fatalf("got minor=%d currency=%s", m.Minor(), m.Currency())
	}
	if _, err := MoneyFromMinor(1, Currency("EUR")); err == nil {
		t.Fatal("expected an error for an unknown currency")
	}
}

func TestMoneyFromMajor(t *testing.T) {
	cases := []struct {
		major int64
		cur   Currency
		minor int64
	}{
		{149, CurrencyRUB, 14900}, // roubles scale to kopecks
		{250, CurrencyStar, 250},  // Stars are whole units
		{7, CurrencyVote, 7},
		{50, CurrencyChip, 50},
	}
	for _, c := range cases {
		m, err := MoneyFromMajor(c.major, c.cur)
		if err != nil {
			t.Fatalf("MoneyFromMajor(%d,%s): %v", c.major, c.cur, err)
		}
		if m.Minor() != c.minor {
			t.Errorf("MoneyFromMajor(%d,%s): minor=%d, want %d", c.major, c.cur, m.Minor(), c.minor)
		}
	}
	if _, err := MoneyFromMajor(1<<62, CurrencyRUB); err == nil {
		t.Fatal("expected an overflow error")
	}
}

func TestParseMoneyExact(t *testing.T) {
	cases := []struct {
		text  string
		cur   Currency
		minor int64
	}{
		{"149.50", CurrencyRUB, 14950},
		{"149", CurrencyRUB, 14900},
		{"0.01", CurrencyRUB, 1},
		{"0.10", CurrencyRUB, 10}, // 0.1 is inexact as a float; big.Rat keeps it exact
		{"-5.00", CurrencyRUB, -500},
		{"250", CurrencyStar, 250},
		{"7", CurrencyVote, 7},
		{"50", CurrencyChip, 50},
	}
	for _, c := range cases {
		m, err := ParseMoney(c.text, c.cur)
		if err != nil {
			t.Fatalf("ParseMoney(%q,%s): %v", c.text, c.cur, err)
		}
		if m.Minor() != c.minor {
			t.Errorf("ParseMoney(%q,%s): minor=%d, want %d", c.text, c.cur, m.Minor(), c.minor)
		}
	}
}

func TestParseMoneyRejectsFraction(t *testing.T) {
	// A whole-unit currency (Vote/Star/chip) must never accept a fraction, and
	// the rouble must never accept finer than a kopeck — the no-float gate.
	bad := []struct {
		text string
		cur  Currency
	}{
		{"250.5", CurrencyStar},
		{"1.5", CurrencyChip},
		{"7.01", CurrencyVote},
		{"1.999", CurrencyRUB},
		{"0.001", CurrencyRUB},
		{"abc", CurrencyRUB},
		{"12.34", Currency("EUR")}, // unknown currency
	}
	for _, c := range bad {
		if m, err := ParseMoney(c.text, c.cur); err == nil {
			t.Errorf("ParseMoney(%q,%s) = %d, want an error", c.text, c.cur, m.Minor())
		}
	}
}

func TestMoneyAddCmp(t *testing.T) {
	a, _ := MoneyFromMinor(14900, CurrencyRUB)
	b, _ := MoneyFromMinor(50, CurrencyRUB)
	sum, err := a.Add(b)
	if err != nil || sum.Minor() != 14950 {
		t.Fatalf("Add: minor=%d err=%v", sum.Minor(), err)
	}
	if c, err := a.Cmp(b); err != nil || c != 1 {
		t.Fatalf("Cmp a>b: got %d err=%v", c, err)
	}
	if c, err := b.Cmp(a); err != nil || c != -1 {
		t.Fatalf("Cmp b<a: got %d err=%v", c, err)
	}
	if c, err := a.Cmp(a); err != nil || c != 0 {
		t.Fatalf("Cmp a==a: got %d err=%v", c, err)
	}
	// Cross-currency arithmetic is refused, not silently coerced.
	star, _ := MoneyFromMinor(1, CurrencyStar)
	if _, err := a.Add(star); err == nil {
		t.Error("Add across currencies: expected an error")
	}
	if _, err := a.Cmp(star); err == nil {
		t.Error("Cmp across currencies: expected an error")
	}
}

func TestMoneyString(t *testing.T) {
	cases := []struct {
		minor int64
		cur   Currency
		want  string
	}{
		{14900, CurrencyRUB, "149.00 RUB"},
		{14950, CurrencyRUB, "149.50 RUB"},
		{1, CurrencyRUB, "0.01 RUB"},
		{-500, CurrencyRUB, "-5.00 RUB"},
		{250, CurrencyStar, "250 XTR"},
		{5, CurrencyChip, "5 CHIP"},
	}
	for _, c := range cases {
		m, _ := MoneyFromMinor(c.minor, c.cur)
		if got := m.String(); got != c.want {
			t.Errorf("Money{%d,%s}.String() = %q, want %q", c.minor, c.cur, got, c.want)
		}
	}
}
