package csvimport

import (
	"reflect"
	"testing"
)

func TestReadQuotedFieldSuccess(t *testing.T) {
	f := func(s, resultExpected, tailExpected string) {
		t.Helper()
		result, tail, err := readQuotedField(s)
		if err != nil {
			t.Fatalf("unexpected error: %s", err)
		}
		if result != resultExpected {
			t.Fatalf("unexpected result; got %q; want %q", result, resultExpected)
		}
		if tail != tailExpected {
			t.Fatalf("unexpected tail; got %q; want %q", tail, tailExpected)
		}
	}

	// double quotes
	f(`""`, ``, ``)
	f(`"",`, ``, `,`)
	f(`"",foobar`, ``, `,foobar`)
	f(`"","bc"`, ``, `,"bc"`)
	f(`"a"`, `a`, ``)
	f(`"a"bc`, `a`, `bc`)
	f(`"foo`+"`',\n\t\r"+`bar"baz`, "foo`',\n\t\rbar", "baz")

	// single quotes
	f(`''`, ``, ``)
	f(`'',`, ``, `,`)
	f(`'',foobar`, ``, `,foobar`)
	f(`'','bc'`, ``, `,'bc'`)
	f(`'a'`, `a`, ``)
	f(`'a'bc`, `a`, `bc`)
	f(`'foo"`+"`,\n\t\r"+`bar'baz`, "foo\"`,\n\t\rbar", "baz")

	// escaped double quotes
	f(`" foo""bar"baz`, ` foo"bar`, `baz`)
	f(`""""bar"baz`, `"`, `bar"baz`)
	f(`"a,""b""'c",d,"e"`, `a,"b"'c`, `,d,"e"`)

	// escaped single quotes
	f(`' foo''bar'baz`, ` foo'bar`, `baz`)
	f(`''''bar'baz`, `'`, `bar'baz`)
	f(`'''bar'''baz`, `'bar'`, `baz`)
	f(`'a,''b''"c',d,'e'`, `a,'b'"c`, `,d,'e'`)
}

func TestReadQuotedFieldFailure(t *testing.T) {
	f := func(s string) {
		t.Helper()
		field, tail, err := readQuotedField(s)
		if field != "" {
			t.Fatalf("unexpected non-empty field returned: %q", field)
		}
		if tail != s {
			t.Fatalf("unexpected tail returned; got %q; want %q", tail, s)
		}
		if err == nil {
			t.Fatalf("expecting non-nil error")
		}
	}
	f(`"`)
	f(`'`)
	f(`"foo""`)
	f(`'foo''`)
	f(`'foo''`)
}

func TestScannerSuccess(t *testing.T) {
	f := func(s string, rowsExpected [][]string) {
		t.Helper()
		var sc scanner
		sc.Init(s)
		var rows [][]string
		for sc.NextLine() {
			var row []string
			for sc.NextColumn() {
				row = append(row, sc.Column)
			}
			rows = append(rows, row)
		}
		if sc.Error != nil {
			t.Fatalf("unexpected error: %s", sc.Error)
		}
		if !reflect.DeepEqual(rows, rowsExpected) {
			t.Fatalf("unexpected rows;\ngot\n%q\nwant\n%q", rows, rowsExpected)
		}
	}

	f("", nil)
	f("\n", nil)
	f("\r\n\n\r", nil)
	f("foo,bar\n\"aa,\"\"bb\",\"\"", [][]string{
		{"foo", "bar"},
		{`aa,"bb`, ``},
	})
	f(`fo"bar,baz'a,"bc""de",'g''e'`, [][]string{
		{`fo"bar`, `baz'a`, `bc"de`, `g'e`},
	})
	f(`,`, [][]string{
		{``, ``},
	})
	f(`foo`, [][]string{
		{`foo`},
	})
	f(`foo,,`+"\r\n"+`,bar,`+"\n", [][]string{
		{`foo`, ``, ``},
		{``, `bar`, ``},
	})
}

func TestScannerFailure(t *testing.T) {
	f := func(s string) {
		t.Helper()
		var sc scanner
		sc.Init(s)
		for sc.NextLine() {
			for sc.NextColumn() {
			}
			if sc.Error != nil {
				if sc.NextColumn() {
					t.Fatalf("unexpected NextColumn success after the error %v", sc.Error)
				}
				return
			}
		}
		t.Fatalf("expecting at least a single error")
	}
	// Unclosed quote
	f("foo\r\n\"bar,")
	f(`"foo,"bar`)
	f(`foo,"bar",""a`)
	f(`foo,"bar","a""`)
}