package graphiteql

import (
	"reflect"
	"strings"
	"testing"
)

func TestScanStringSuccess(t *testing.T) {
	f := func(s, sExpected string) {
		t.Helper()
		result, err := scanString(s)
		if err != nil {
			t.Fatalf("unexpected error in scanString(%s): %s", s, err)
		}
		if result != sExpected {
			t.Fatalf("unexpected string scanned from %s; got %s; want %s", s, result, sExpected)
		}
		if !strings.HasPrefix(s, result) {
			t.Fatalf("invalid prefix for scanne string %s: %s", s, result)
		}
	}
	f(`""`, `""`)
	f(`''`, `''`)
	f(`""tail`, `""`)
	f(`''tail`, `''`)
	f(`"foo", bar`, `"foo"`)
	f(`'foo', bar`, `'foo'`)
	f(`"foo\.bar"`, `"foo\.bar"`)
	f(`"foo\"bar\1"\"`, `"foo\"bar\1"`)
	f(`"foo\\"bar\1"\"`, `"foo\\"`)
	f(`'foo\\'bar\1"\"`, `'foo\\'`)
}

func TestScanStringFailure(t *testing.T) {
	f := func(s string) {
		t.Helper()
		result, err := scanString(s)
		if err == nil {
			t.Fatalf("expecting non-nil error for scanString(%s)", s)
		}
		if result != "" {
			t.Fatalf("expecting empty result for scanString(%s); got %s", s, result)
		}
	}
	f(``)
	f(`"foo`)
	f(`'bar`)
}

func TestAppendEscapedIdent(t *testing.T) {
	f := func(s, resultExpected string) {
		t.Helper()
		result := appendEscapedIdent(nil, s)
		if string(result) != resultExpected {
			t.Fatalf("unexpected result; got\n%s\nwant\n%s", result, resultExpected)
		}
	}
	f("", "")
	f("a", "a")
	f("fo_o.$b-ar.b[a]*z{aa,bb}", "fo_o.$b-ar.b[a]*z{aa,bb}")
	f("a(b =C)", `a\(b\ \=C\)`)
}

func TestLexerSuccess(t *testing.T) {
	f := func(s string, tokensExpected []string) {
		t.Helper()
		var lex lexer
		var tokens []string
		lex.Init(s)
		for {
			if err := lex.Next(); err != nil {
				t.Fatalf("unexpected error: %s", err)
			}
			if isEOF(lex.Token) {
				break
			}
			tokens = append(tokens, lex.Token)
		}
		if !reflect.DeepEqual(tokens, tokensExpected) {
			t.Fatalf("unexpected tokens; got\n%q\nwant\n%q", tokens, tokensExpected)
		}
	}
	f("", nil)
	f("a", []string{"a"})
	f("*", []string{"*"})
	f("*.a", []string{"*.a"})
	f("[a-z]zx", []string{"[a-z]zx"})
	f("fo_o.$ba-r", []string{"fo_o.$ba-r"})
	f("{foo,bar}", []string{"{foo,bar}"})
	f("[a-z]", []string{"[a-z]"})
	f("fo*.bar10[s-z]as{aaa,bb}.ss{aa*ss,DS[c-D],ss}ss", []string{"fo*.bar10[s-z]as{aaa,bb}.ss{aa*ss,DS[c-D],ss}ss"})
	f("FOO.bar:avg", []string{"FOO.bar:avg"})
	f("FOO.bar\\:avg", []string{"FOO.bar\\:avg"})
	f(`foo.Bar|aaa(b,cd,e=aa)`, []string{"foo.Bar", "|", "aaa", "(", "b", ",", "cd", ",", "e", "=", "aa", ")"})
	f(`foo.Bar\|aaa\(b\,cd\,e\=aa\)`, []string{`foo.Bar\|aaa\(b\,cd\,e\=aa\)`})
	f(`123`, []string{`123`})
	f(`12.34`, []string{`12.34`})
	f(`12.34e4`, []string{`12.34e4`})
	f(`12.34e-4`, []string{`12.34e-4`})
	f(`12E+45`, []string{`12E+45`})
	f(`+12.34`, []string{`+`, `12.34`})
	f("0xABcd", []string{`0xABcd`})
	f("f(0o765,0b1101,0734,12.34)", []string{"f", "(", "0o765", ",", "0b1101", ",", "0734", ",", "12.34", ")"})
	f(`f ( foo, -.54e6,bar)`, []string{"f", "(", "foo", ",", "-", ".54e6", ",", "bar", ")"})
	f(`"foo(b'ar:baz)"`, []string{`"foo(b'ar:baz)"`})
	f(`'a"bc'`, []string{`'a"bc'`})
	f(`"f\"oo\\b"`, []string{`"f\"oo\\b"`})
	f(`a("b,c", 'de')`, []string{`a`, `(`, `"b,c"`, `,`, `'de'`, `)`})
}

func TestLexerError(t *testing.T) {
	f := func(s string) {
		t.Helper()
		var lex lexer
		lex.Init(s)
		for {
			if err := lex.Next(); err != nil {
				// Make sure lex.Next() consistently returns the error.
				if err1 := lex.Next(); err1 != err {
					t.Fatalf("unexpected error returned; got %v; want %v", err1, err)
				}
				return
			}
			if isEOF(lex.Token) {
				t.Fatalf("expecting non-nil error when parsing %q", s)
			}
		}
	}

	// Invalid identifier
	f("foo\\")
	f(`foo[bar`)
	f(`foo{bar`)
	f("~")
	f(",~")

	// Invalid string
	f(`"`)
	f(`"foo`)
	f(`'aa`)

	// Invalid number
	f(`0x`)
	f(`0o`)
	f(`-0b`)
	f(`13.`)
	f(`1e`)
	f(`1E+`)
	f(`1.3e`)
}