// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pgparse

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"
	"testing"

	"github.com/youtube/vitess/go/testfiles"
)

func TestFunction(t *testing.T) {
	sql := `SELECT "version" ()`
	_, err := Parse(sql)
	if err != nil {
		t.Error(err)
	}
}

func TestQuotedIdentifiers(t *testing.T) {
	sql := `SELECT "pg_stat_activity".* FROM "pg_stat_activity" WHERE ("now" ()- "state_change")> ?`
	_, err := Parse(sql)
	if err != nil {
		t.Error(err)
	}
}

func TestNestedFunctions(t *testing.T) {
	sql := `SELECT "max" ("age" ("datfrozenxid")) AS "datfrozenxid_age" FROM "pg_database" WHERE "datallowconn"`
	_, err := Parse(sql)
	if err != nil {
		t.Error(err)
	}
}

func TestColonCasts(t *testing.T) {
	sql := `SELECT "vods"."id", "GREATEST" ("vods"."started_on" :: "timestamp" + "COALESCE" ("user_vod_properties"."vod_storage_days" :: "integer", ?)* "interval" ?, "vods"."delete_at" :: "timestamp") AT TIME ZONE ? AS "adjusted_expires", ? :: "timestamp" AS "start_time", ? :: "timestamp" AS "end_time" FROM "vods" LEFT JOIN "user_vod_properties" ON "vods"."owner_id" = "user_vod_properties"."user_id" LEFT JOIN "vod_appeals" ON ("vods"."id" = "vod_appeals"."vod_id") WHERE "vods"."broadcast_type" = ? AND "user_vod_properties"."save_vods_forever" IS NOT TRUE AND "vod_appeals"."vod_id" IS NULL AND "vods"."id" > ? ORDER BY "vods"."id" LIMIT ?`
	_, err := Parse(sql)
	if err != nil {
		t.Error(err)
	}
}

func TestNotLike(t *testing.T) {
	sql := "SELECT C.relname FROM pg_class C WHERE C.relkind <> 'i' AND nspname !~ '^pg_toast'"
	_, err := Parse(sql)
	if err != nil {
		t.Error(err)
	}
}

func TestOrder(t *testing.T) {
	sql := "SELECT a.attname FROM pg_attribute a LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND NOT a.attisdropped  ORDER BY a.attnum"
	_, err := Parse(sql)
	if err != nil {
		t.Error(err)
	}
}

func TestIndex(t *testing.T) {
	sql := "SELECT conkey[?] FROM pg_constraint"
	_, err := Parse(sql)
	if err != nil {
		t.Error(err)
	}
}

func TestGen(t *testing.T) {
	_, err := Parse("select :a from a where a in (:b)")
	if err != nil {
		t.Error(err)
	}
}

func TestParse(t *testing.T) {
	t.Skip("VTROOT not set")
	for tcase := range iterateFiles("sqlparser_test/*.sql") {
		if tcase.output == "" {
			tcase.output = tcase.input
		}
		tree, err := Parse(tcase.input)
		var out string
		if err != nil {
			out = err.Error()
		} else {
			out = String(tree)
		}
		if out != tcase.output {
			t.Error(fmt.Sprintf("File:%s Line:%v\n%q\n%q", tcase.file, tcase.lineno, tcase.output, out))
		}
	}
}

func BenchmarkParse1(b *testing.B) {
	sql := "select 'abcd', 20, 30.0, eid from a where 1=eid and name='3'"
	for i := 0; i < b.N; i++ {
		_, err := Parse(sql)
		if err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkParse2(b *testing.B) {
	sql := "select aaaa, bbb, ccc, ddd, eeee, ffff, gggg, hhhh, iiii from tttt, ttt1, ttt3 where aaaa = bbbb and bbbb = cccc and dddd+1 = eeee group by fff, gggg having hhhh = iiii and iiii = jjjj order by kkkk, llll limit 3, 4"
	for i := 0; i < b.N; i++ {
		_, err := Parse(sql)
		if err != nil {
			b.Fatal(err)
		}
	}
}

type testCase struct {
	file   string
	lineno int
	input  string
	output string
}

func iterateFiles(pattern string) (testCaseIterator chan testCase) {
	names := testfiles.Glob(pattern)
	testCaseIterator = make(chan testCase)
	go func() {
		defer close(testCaseIterator)
		for _, name := range names {
			fd, err := os.OpenFile(name, os.O_RDONLY, 0)
			if err != nil {
				panic(fmt.Sprintf("Could not open file %s", name))
			}

			r := bufio.NewReader(fd)
			lineno := 0
			for {
				line, err := r.ReadString('\n')
				lines := strings.Split(strings.TrimRight(line, "\n"), "#")
				lineno++
				if err != nil {
					if err != io.EOF {
						panic(fmt.Sprintf("Error reading file %s: %s", name, err.Error()))
					}
					break
				}
				input := lines[0]
				output := ""
				if len(lines) > 1 {
					output = lines[1]
				}
				if input == "" {
					continue
				}
				testCaseIterator <- testCase{name, lineno, input, output}
			}
		}
	}()
	return testCaseIterator
}
