package decimal

import (
	"strings"
	"testing"
)

func TestNewFromString(t *testing.T) {
	tests := []struct{ in, want string }{
		{"10.00", "10"},
		{"150.60", "150.6"},
		{".33", "0.33"},
		{"1.", "1"},
		{"0001.2300", "1.23"},
		{"0", "0"},
		{"0.00", "0"},
		{"-0.00", "0"},
		{"-20", "-20"},
		{"-.75", "-0.75"},
		{"1E3", "1000"},
		{"1E-3", "0.001"},
		{"1E10000", "1" + strings.Repeat("0", 10000)},
	}
	for _, tt := range tests {
		got, err := FromString(tt.in)
		if err != nil {
			t.Fatalf("NewFromString(%q) unexpected error: %v", tt.in, err)
		}
		if got.String() != tt.want {
			t.Fatalf("NewFromString(%q).String() = %q, want %q", tt.in, got.String(), tt.want)
		}
	}
}

func TestNewFromStringInvalid(t *testing.T) {
	tests := []string{
		"", ".", "+", "-",
		"1_000.00", "1,000.00",
		"1..0",
		"a1", "1a",
		"1E10001", "1E-10001", "1E1000000000000",
	}
	for _, in := range tests {
		if _, err := FromString(in); err == nil {
			t.Fatalf("NewFromString(%q) expected error", in)
		}
	}
}

func TestStringFixed(t *testing.T) {
	tests := []struct {
		in           string
		places       int
		decSep       byte
		thousandsSep byte
		want         string
	}{
		{"0", 0, 0, 0, "0"},
		{"0", 1, 0, 0, "0.0"},
		{"0", 2, 0, 0, "0.00"},
		{"0", 3, 0, 0, "0.000"},
		{"-0", 2, 0, 0, "0.00"},
		{"10", 2, 0, 0, "10.00"},
		{"10", 0, 0, 0, "10"},
		{"1.2", 2, 0, 0, "1.20"},
		{"1.2", 1, 0, 0, "1.2"},
		{"1.2", 3, 0, 0, "1.200"},
		{"1.234", 2, 0, 0, "1.23"},
		{"1.235", 2, 0, 0, "1.23"},
		{"-1.2", 2, 0, 0, "-1.20"},
		{"-1.234", 2, 0, 0, "-1.23"},
		{"0.001", 2, 0, 0, "0.00"},
		{"0.001", 4, 0, 0, "0.0010"},
		{"123.456789", 3, 0, 0, "123.456"},
		{"-123.456789", 3, 0, 0, "-123.456"},
		{"1.23", 2, ',', 0, "1,23"},
		{"0", 2, ',', 0, "0,00"},
		{"-1.5", 2, ',', 0, "-1,50"},
		{"1234.56", 2, 0, ',', "1,234.56"},
		{"1234567.89", 2, 0, ',', "1,234,567.89"},
		{"123.45", 2, 0, ',', "123.45"},
		{"-1234.56", 2, 0, ',', "-1,234.56"},
		{"1234567.89", 2, ',', '.', "1.234.567,89"},
		{"-1234567.89", 2, ',', '.', "-1.234.567,89"},
		{"0", 2, ',', '.', "0,00"},
	}
	for _, tt := range tests {
		d, err := FromString(tt.in)
		if err != nil {
			t.Fatalf("FromString(%q) unexpected error: %v", tt.in, err)
		}
		if got := d.StringFixed(tt.places, tt.decSep, tt.thousandsSep); got != tt.want {
			t.Fatalf("FromString(%q).StringFixed(%d, %q, %q) = %q, want %q",
				tt.in, tt.places, tt.decSep, tt.thousandsSep, got, tt.want)
		}
	}
}

func TestArithmetic(t *testing.T) {
	a, _ := FromString("1.20")
	b, _ := FromString(".30")

	if got := a.Add(b).String(); got != "1.5" {
		t.Fatalf("a+b = %q, want %q", got, "1.5")
	}
	if got := a.Sub(b).String(); got != "0.9" {
		t.Fatalf("a-b = %q, want %q", got, "0.9")
	}
	if got := a.Mul(b).String(); got != "0.36" {
		t.Fatalf("a*b = %q, want %q", got, "0.36")
	}
}

func TestAbs(t *testing.T) {
	tests := []struct {
		in, want string
	}{
		{"0", "0"},
		{"-0", "0"},
		{"0.00", "0"},
		{"1.5", "1.5"},
		{"-1.5", "1.5"},
		{"100", "100"},
		{"-100", "100"},
		{"0.001", "0.001"},
		{"-0.001", "0.001"},
		{"123.456", "123.456"},
		{"-123.456", "123.456"},
	}
	for _, tt := range tests {
		d, err := FromString(tt.in)
		if err != nil {
			t.Fatalf("FromString(%q) unexpected error: %v", tt.in, err)
		}
		if got := d.Abs().String(); got != tt.want {
			t.Fatalf("Abs(%q) = %q, want %q", tt.in, got, tt.want)
		}
	}
}

func TestDiv(t *testing.T) {
	tests := []struct {
		a, b, want string
	}{
		{"1", "2", "0.5"},
		{"10", "3", "3.3333333333"},
		{"1.50", "3.00", "0.5"},
		{"0.1", "0.3", "0.33333333333"},
		{"100", "10", "10"},
		{"-1", "2", "-0.5"},
		{"1", "-2", "-0.5"},
		{"-1", "-2", "0.5"},
		{"0", "5", "0"},
		{"5.50", "5", "1.1"},
		{"1000", "3", "333.3333333333"},
	}
	for _, tt := range tests {
		a, err := FromString(tt.a)
		if err != nil {
			t.Fatalf("FromString(%q) unexpected error: %v", tt.a, err)
		}
		b, err := FromString(tt.b)
		if err != nil {
			t.Fatalf("FromString(%q) unexpected error: %v", tt.b, err)
		}
		got := a.Div(b).String()
		if got != tt.want {
			t.Fatalf("Div(%q, %q) = %q, want %q", tt.a, tt.b, got, tt.want)
		}
	}
}

func TestDivByZero(t *testing.T) {
	defer func() {
		if r := recover(); r == nil {
			t.Fatal("expected panic on division by zero")
		}
	}()
	d, _ := FromString("1")
	d.Div(Decimal{})
}

func TestCmpAndNeg(t *testing.T) {
	a, _ := FromString("1.0")
	b, _ := FromString("1")
	c, _ := FromString("1.01")

	if a.Cmp(b) != 0 {
		t.Fatalf("expected %q and %q to be equal", a.String(), b.String())
	}
	if b.Cmp(c) >= 0 {
		t.Fatalf("expected %q to be less than %q", b.String(), c.String())
	}
	if got := b.Neg().String(); got != "-1" {
		t.Fatalf("Neg(1) = %q, want %q", got, "-1")
	}
	if got := (Decimal{}).Neg().String(); got != "0" {
		t.Fatalf("Neg(0) = %q, want %q", got, "0")
	}
}
