package xsql

import (
	"errors"
	"fmt"
	"github.com/emqx/kuiper/common"
	"reflect"
	"strings"
	"testing"
)

func TestComparison(t *testing.T) {
	testTime, _ := common.InterfaceToTime(1541152488442, "")
	data := []struct {
		m Message
		r []interface{}
	}{
		{
			m: map[string]interface{}{
				"a": float64(32),
				"b": float64(72),
			},
			r: []interface{}{
				false, true, errors.New("invalid operation float64(32) = string(string literal)"),
				false, true, false, true,
			},
		}, {
			m: map[string]interface{}{
				"a": int64(32),
				"b": int64(72),
			},
			r: []interface{}{
				false, true, errors.New("invalid operation int64(32) = string(string literal)"),
				false, true, false, true,
			},
		}, {
			m: map[string]interface{}{
				"a": "32",
				"b": "72",
			},
			r: []interface{}{
				errors.New("invalid operation string(32) > int64(72)"), errors.New("invalid operation string(32) <= int64(32)"), false,
				false, true, false, true,
			},
		}, {
			m: map[string]interface{}{
				"a": []interface{}{32, 72},
				"b": []interface{}{32, 72},
			},
			r: []interface{}{
				errors.New("> is an invalid operation for []interface {}"), errors.New("<= is an invalid operation for []interface {}"), errors.New("= is an invalid operation for []interface {}"),
				errors.New(">= is an invalid operation for []interface {}"), errors.New("< is an invalid operation for []interface {}"), errors.New("= is an invalid operation for []interface {}"), errors.New("!= is an invalid operation for []interface {}"),
			},
		}, {
			m: map[string]interface{}{
				"a": map[string]interface{}{"c": 5},
				"b": map[string]interface{}{"d": 5},
			},
			r: []interface{}{
				errors.New("> is an invalid operation for map[string]interface {}"), errors.New("<= is an invalid operation for map[string]interface {}"), errors.New("= is an invalid operation for map[string]interface {}"),
				errors.New(">= is an invalid operation for map[string]interface {}"), errors.New("< is an invalid operation for map[string]interface {}"), errors.New("= is an invalid operation for map[string]interface {}"), errors.New("!= is an invalid operation for map[string]interface {}"),
			},
		}, {
			m: map[string]interface{}{
				"a": float64(55),
				"b": int64(55),
			},
			r: []interface{}{
				false, false, errors.New("invalid operation float64(55) = string(string literal)"),
				true, false, true, false,
			},
		}, {
			m: map[string]interface{}{
				"a": testTime,
				"b": int64(1541152388442),
			},
			r: []interface{}{
				true, false, errors.New("invalid operation time.Time(2018-11-02 09:54:48.442 +0000 UTC) = string(string literal)"),
				true, false, false, true,
			},
		}, {
			m: map[string]interface{}{
				"a": testTime,
				"b": "2020-02-26T02:37:21.822Z",
			},
			r: []interface{}{
				true, false, errors.New("invalid operation time.Time(2018-11-02 09:54:48.442 +0000 UTC) = string(string literal)"),
				false, true, false, true,
			},
		}, {
			m: map[string]interface{}{
				"a": int64(1541152388442),
				"b": testTime,
			},
			r: []interface{}{
				true, false, errors.New("invalid operation int64(1541152388442) = string(string literal)"),
				errors.New("invalid operation int64(1541152388442) >= time.Time(2018-11-02 09:54:48.442 +0000 UTC)"), errors.New("invalid operation int64(1541152388442) < time.Time(2018-11-02 09:54:48.442 +0000 UTC)"), errors.New("invalid operation int64(1541152388442) = time.Time(2018-11-02 09:54:48.442 +0000 UTC)"), errors.New("invalid operation int64(1541152388442) != time.Time(2018-11-02 09:54:48.442 +0000 UTC)"),
			},
		}, {
			m: map[string]interface{}{
				"a": "2020-02-26T02:37:21.822Z",
				"b": testTime,
			},
			r: []interface{}{
				errors.New("invalid operation string(2020-02-26T02:37:21.822Z) > int64(72)"), errors.New("invalid operation string(2020-02-26T02:37:21.822Z) <= int64(32)"), false,
				errors.New("invalid operation string(2020-02-26T02:37:21.822Z) >= time.Time(2018-11-02 09:54:48.442 +0000 UTC)"), errors.New("invalid operation string(2020-02-26T02:37:21.822Z) < time.Time(2018-11-02 09:54:48.442 +0000 UTC)"), errors.New("invalid operation string(2020-02-26T02:37:21.822Z) = time.Time(2018-11-02 09:54:48.442 +0000 UTC)"), errors.New("invalid operation string(2020-02-26T02:37:21.822Z) != time.Time(2018-11-02 09:54:48.442 +0000 UTC)"),
			},
		}, {
			m: map[string]interface{}{
				"c": "nothing",
			},
			r: []interface{}{
				false, false, false,
				true, false, true, false,
			},
		}, {
			m: map[string]interface{}{
				"a": 12,
				"c": "nothing",
			},
			r: []interface{}{
				false, true, errors.New("invalid operation int64(12) = string(string literal)"),
				false, false, false, true,
			},
		},
	}
	sqls := []string{
		"select * from src where a > 72",
		"select * from src where a <= 32",
		"select * from src where a = \"string literal\"",
		"select * from src where a >= b",
		"select * from src where a < b",
		"select * from src where a = b",
		"select * from src where a != b",
	}
	var conditions []Expr
	for _, sql := range sqls {
		stmt, _ := NewParser(strings.NewReader(sql)).Parse()
		conditions = append(conditions, stmt.Condition)
	}

	fmt.Printf("The test bucket size is %d.\n\n", len(data)*len(sqls))
	for i, tt := range data {
		for j, c := range conditions {
			tuple := &Tuple{Emitter: "src", Message: tt.m, Timestamp: common.GetNowInMilli(), Metadata: nil}
			ve := &ValuerEval{Valuer: MultiValuer(tuple)}
			result := ve.Eval(c)
			if !reflect.DeepEqual(tt.r[j], result) {
				t.Errorf("%d-%d. \nstmt mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", i, j, tt.r[j], result)
			}
		}
	}
}

func TestCalculation(t *testing.T) {
	data := []struct {
		m Message
		r []interface{}
	}{
		{
			m: map[string]interface{}{
				"a": float64(32),
				"b": float64(72),
			},
			r: []interface{}{
				float64(104), float64(96), float64(0.4444444444444444), float64(32),
			},
		}, {
			m: map[string]interface{}{
				"a": int64(32),
				"b": int64(72),
			},
			r: []interface{}{
				int64(104), int64(96), int64(0), int64(32),
			},
		}, {
			m: map[string]interface{}{
				"a": "32",
				"b": "72",
			},
			r: []interface{}{
				errors.New("invalid operation string(32) + string(72)"), errors.New("invalid operation string(32) * int64(3)"),
				errors.New("invalid operation string(32) / string(72)"), errors.New("invalid operation string(32) % string(72)"),
			},
		}, {
			m: map[string]interface{}{
				"a": float64(55),
				"b": int64(55),
			},
			r: []interface{}{
				float64(110), float64(165), float64(1), float64(0),
			},
		}, {
			m: map[string]interface{}{
				"a": int64(55),
				"b": float64(0),
			},
			r: []interface{}{
				float64(55), int64(165), errors.New("divided by zero"), errors.New("divided by zero"),
			},
		}, {
			m: map[string]interface{}{
				"c": "nothing",
			},
			r: []interface{}{
				nil, nil, nil, nil,
			},
		}, {
			m: map[string]interface{}{
				"a": 12,
				"c": "nothing",
			},
			r: []interface{}{
				nil, int64(36), nil, nil,
			},
		},
	}
	sqls := []string{
		"select a + b as t from src",
		"select a * 3 as t from src",
		"select a / b as t from src",
		"select a % b as t from src",
	}
	var projects []Expr
	for _, sql := range sqls {
		stmt, _ := NewParser(strings.NewReader(sql)).Parse()
		projects = append(projects, stmt.Fields[0].Expr)
	}

	fmt.Printf("The test bucket size is %d.\n\n", len(data)*len(sqls))
	for i, tt := range data {
		for j, c := range projects {
			tuple := &Tuple{Emitter: "src", Message: tt.m, Timestamp: common.GetNowInMilli(), Metadata: nil}
			ve := &ValuerEval{Valuer: MultiValuer(tuple)}
			result := ve.Eval(c)
			if !reflect.DeepEqual(tt.r[j], result) {
				t.Errorf("%d-%d. \nstmt mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", i, j, tt.r[j], result)
			}
		}
	}
}
