package vuetifyx_test

import (
	"testing"
	"time"

	. "github.com/goplaid/ui/vuetifyx"
	"github.com/theplant/testingutils"
)

func mustParseDatetimePickerValue(v string) time.Time {
	t, err := time.ParseInLocation("2006-01-02 15:04", v, time.Local)
	if err != nil {
		panic(err)
	}
	return t
}

func mustParseDatePickerValue(v string) time.Time {
	t, err := time.ParseInLocation("2006-01-02", v, time.Local)
	if err != nil {
		panic(err)
	}
	return t
}

var setByQueryCases = []struct {
	name             string
	data             FilterData
	qs               string
	expected         FilterData
	expectedSQLConds string
	expectedSQLArgs  []interface{}
}{
	{
		name: "between",
		data: FilterData([]*FilterItem{
			{
				Key:          "created",
				ItemType:     ItemTypeDatetimeRange,
				SQLCondition: "created_at %s ?",
			},
		}),
		qs: "created.lt=2019-04-11 00:00&created.gte=2019-04-10 00:00",
		expected: FilterData([]*FilterItem{
			{
				Key:       "created",
				ItemType:  ItemTypeDatetimeRange,
				Modifier:  ModifierBetween,
				Selected:  true,
				ValueFrom: "2019-04-10 00:00",
				ValueTo:   "2019-04-11 00:00",
			},
		}),
		expectedSQLConds: "created_at >= ? AND created_at < ?",
		expectedSQLArgs:  []interface{}{mustParseDatetimePickerValue("2019-04-10 00:00"), mustParseDatetimePickerValue("2019-04-11 00:00")},
	},
	{
		name: "between2",
		data: FilterData([]*FilterItem{
			{
				Key:          "created",
				Label:        "Created",
				ItemType:     ItemTypeDatetimeRange,
				SQLCondition: `extract(epoch from created_at) %s ?`,
			},
			{
				Key:          "name",
				Label:        "Name",
				ItemType:     ItemTypeString,
				SQLCondition: `name %s ?`,
			},
		}),
		qs: "created.lt=2019-08-09 00:00&created.gte=2019-08-07 00:00",
		expected: FilterData([]*FilterItem{
			{
				Key:       "created",
				Label:     "Created",
				ItemType:  ItemTypeDatetimeRange,
				Modifier:  ModifierBetween,
				Selected:  true,
				ValueFrom: "2019-08-07 00:00",
				ValueTo:   "2019-08-09 00:00",
			},
			{
				Key:          "name",
				Label:        "Name",
				ItemType:     ItemTypeString,
				SQLCondition: `name %s ?`,
			},
		}),
		expectedSQLConds: "extract(epoch from created_at) >= ? AND extract(epoch from created_at) < ?",
		expectedSQLArgs:  []interface{}{mustParseDatetimePickerValue("2019-08-07 00:00"), mustParseDatetimePickerValue("2019-08-09 00:00")},
	},
	{
		name: "date_range_has_two_params",
		data: FilterData([]*FilterItem{
			{
				Key:          "created",
				ItemType:     ItemTypeDateRange,
				SQLCondition: "created_at %s ?",
			},
		}),
		qs: "created.lte=2019-04-11&created.gte=2019-04-10",
		expected: FilterData([]*FilterItem{
			{
				Key:       "created",
				ItemType:  ItemTypeDateRange,
				Modifier:  ModifierBetween,
				Selected:  true,
				ValueFrom: "2019-04-10",
				ValueTo:   "2019-04-11",
			},
		}),
		expectedSQLConds: "created_at >= ? AND created_at < ?",
		expectedSQLArgs:  []interface{}{mustParseDatePickerValue("2019-04-10"), mustParseDatePickerValue("2019-04-12")},
	},
	{
		name: "date_range_has_left_param",
		data: FilterData([]*FilterItem{
			{
				Key:          "created",
				ItemType:     ItemTypeDateRange,
				SQLCondition: "created_at %s ?",
			},
		}),
		qs: "created.gte=2019-04-10",
		expected: FilterData([]*FilterItem{
			{
				Key:       "created",
				ItemType:  ItemTypeDateRange,
				Selected:  true,
				ValueIs:   "2019-04-10",
				ValueFrom: "2019-04-10",
			},
		}),
		expectedSQLConds: "created_at >= ?",
		expectedSQLArgs:  []interface{}{mustParseDatePickerValue("2019-04-10")},
	},
	{
		name: "date_range_has_right_param",
		data: FilterData([]*FilterItem{
			{
				Key:          "created",
				ItemType:     ItemTypeDateRange,
				SQLCondition: "created_at %s ?",
			},
		}),
		qs: "created.lte=2019-04-10",
		expected: FilterData([]*FilterItem{
			{
				Key:      "created",
				ItemType: ItemTypeDateRange,
				Selected: true,
				ValueIs:  "2019-04-10",
				ValueTo:  "2019-04-10",
			},
		}),
		expectedSQLConds: "created_at < ?",
		expectedSQLArgs:  []interface{}{mustParseDatePickerValue("2019-04-11")},
	},
	{
		name: "customize SQLCondition",
		data: FilterData([]*FilterItem{
			{
				Key:      "missing-source-url",
				ItemType: ItemTypeSelect,
				Options: []*SelectItem{
					{Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND source_b IS NULL"},
					{Text: "No", Value: "0", SQLCondition: "source_a IS NOT NULL OR source_b IS NOT NULL"},
				},
			},
		}),
		qs: "missing-source-url=1",
		expected: FilterData([]*FilterItem{
			{
				Key:      "missing-source-url",
				ItemType: ItemTypeSelect,
				Modifier: ModifierEquals,
				Selected: true,
				ValueIs:  "1",
				Options: []*SelectItem{
					{Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND source_b IS NULL"},
					{Text: "No", Value: "0", SQLCondition: "source_a IS NOT NULL OR source_b IS NOT NULL"},
				},
			},
		}),
		expectedSQLConds: "source_a IS NULL AND source_b IS NULL",
		expectedSQLArgs:  nil,
	},
	{
		name: "customize SQLCondition with single value",
		data: FilterData([]*FilterItem{
			{
				Key:      "missing-source-url",
				ItemType: ItemTypeSelect,
				Options: []*SelectItem{
					{Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND source_b = ?"},
					{Text: "No", Value: "0", SQLCondition: "source_a IS NOT NULL OR source_b IS NOT NULL"},
				},
			},
		}),
		qs: "missing-source-url=1",
		expected: FilterData([]*FilterItem{
			{
				Key:      "missing-source-url",
				ItemType: ItemTypeSelect,
				Modifier: ModifierEquals,
				Selected: true,
				ValueIs:  "1",
				Options: []*SelectItem{
					{Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND source_b = ?"},
					{Text: "No", Value: "0", SQLCondition: "source_a IS NOT NULL OR source_b IS NOT NULL"},
				},
			},
		}),
		expectedSQLConds: "source_a IS NULL AND source_b = ?",
		expectedSQLArgs:  []interface{}{"1"},
	},
	{
		name: "customize SQLCondition with multiple value",
		data: FilterData([]*FilterItem{
			{
				Key:      "missing-source-url",
				ItemType: ItemTypeSelect,
				Options: []*SelectItem{
					{Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND (source_b = ? OR source_c = ?)"},
					{Text: "No", Value: "0", SQLCondition: "source_a IS NOT NULL OR source_b IS NOT NULL"},
				},
			},
		}),
		qs: "missing-source-url=1",
		expected: FilterData([]*FilterItem{
			{
				Key:      "missing-source-url",
				ItemType: ItemTypeSelect,
				Modifier: ModifierEquals,
				Selected: true,
				ValueIs:  "1",
				Options: []*SelectItem{
					{Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND (source_b = ? OR source_c = ?)"},
					{Text: "No", Value: "0", SQLCondition: "source_a IS NOT NULL OR source_b IS NOT NULL"},
				},
			},
		}),
		expectedSQLConds: "source_a IS NULL AND (source_b = ? OR source_c = ?)",
		expectedSQLArgs:  []interface{}{"1", "1"},
	},
	{
		name: "customize SQLCondition with default operator and single value",
		data: FilterData([]*FilterItem{
			{
				Key:      "missing-source-url",
				ItemType: ItemTypeSelect,
				Options: []*SelectItem{
					{Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND source_b %s ?"},
				},
			},
		}),
		qs: "missing-source-url=1",
		expected: FilterData([]*FilterItem{
			{
				Key:      "missing-source-url",
				ItemType: ItemTypeSelect,
				Modifier: ModifierEquals,
				Selected: true,
				ValueIs:  "1",
				Options: []*SelectItem{
					{Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND source_b %s ?"},
				},
			},
		}),
		expectedSQLConds: "source_a IS NULL AND source_b = ?",
		expectedSQLArgs:  []interface{}{"1"},
	},
	{
		name: "customize SQLCondition with extra operator and multiple value",
		data: FilterData([]*FilterItem{
			{
				Key:      "missing-source-url",
				ItemType: ItemTypeSelect,
				Options: []*SelectItem{
					{Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND (source_b {op} ? OR source_c {op} ?)"},
				},
			},
		}),
		qs: "missing-source-url.gt=1",
		expected: FilterData([]*FilterItem{
			{
				Key:      "missing-source-url",
				ItemType: ItemTypeSelect,
				Selected: true,
				ValueIs:  "1",
				Options: []*SelectItem{
					{Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND (source_b {op} ? OR source_c {op} ?)"},
				},
			},
		}),
		expectedSQLConds: "source_a IS NULL AND (source_b > ? OR source_c > ?)",
		expectedSQLArgs:  []interface{}{"1", "1"},
	},
	{
		name: "ItemTypeMultipleSelect",
		data: FilterData([]*FilterItem{
			{
				Key:          "state",
				ItemType:     ItemTypeMultipleSelect,
				SQLCondition: "state %s ?",
				Options: []*SelectItem{
					{Text: "Draft", Value: "draft"},
					{Text: "Approved", Value: "approved"},
					{Text: "Rejected", Value: "rejected"},
				},
			},
		}),
		qs: "state.in=draft,rejected",
		expected: FilterData([]*FilterItem{
			{
				Key:      "state",
				ItemType: ItemTypeMultipleSelect,
				Selected: true,
				Modifier: ModifierIn,
				ValuesAre: []string{
					"draft",
					"rejected",
				},
				Options: []*SelectItem{
					{Text: "Draft", Value: "draft"},
					{Text: "Approved", Value: "approved"},
					{Text: "Rejected", Value: "rejected"},
				},
			},
		}),
		expectedSQLConds: "state IN ?",
		expectedSQLArgs:  []interface{}{[]string{"draft", "rejected"}},
	},
	{
		name: "ItemTypeLinkageSelect",
		data: FilterData([]*FilterItem{
			{
				Key:      "province_city_district",
				ItemType: ItemTypeLinkageSelect,
				LinkageSelectData: FilterLinkageSelectData{
					Items: [][]*LinkageSelectItem{
						{
							{ID: "1", Name: "浙江", ChildrenIDs: []string{"1", "2"}},
							{ID: "2", Name: "江苏", ChildrenIDs: []string{"3", "4"}},
						},
						{
							{ID: "1", Name: "杭州", ChildrenIDs: []string{"1", "2"}},
							{ID: "2", Name: "宁波", ChildrenIDs: []string{"3", "4"}},
							{ID: "3", Name: "南京", ChildrenIDs: []string{"5", "6"}},
							{ID: "4", Name: "苏州", ChildrenIDs: []string{"7", "8"}},
						},
						{

							{ID: "1", Name: "拱墅区"},
							{ID: "2", Name: "西湖区"},
							{ID: "3", Name: "镇海区"},
							{ID: "4", Name: "鄞州区"},
							{ID: "5", Name: "鼓楼区"},
							{ID: "6", Name: "玄武区"},
							{ID: "7", Name: "常熟区"},
							{ID: "8", Name: "吴江区"},
						},
					},
					Labels: []string{"Province", "City", "District"},
					SQLConditions: []string{
						"province_id = ?",
						"city_id = ?",
						"district_id = ?",
					},
				},
			},
		}),
		qs: "province_city_district=2,3,7",
		expected: FilterData([]*FilterItem{
			{
				Key:      "province_city_district",
				ItemType: ItemTypeLinkageSelect,
				Selected: true,
				Modifier: ModifierEquals,
				ValuesAre: []string{
					"2",
					"3",
					"7",
				},
				LinkageSelectData: FilterLinkageSelectData{
					Items: [][]*LinkageSelectItem{
						{
							{ID: "1", Name: "浙江", ChildrenIDs: []string{"1", "2"}},
							{ID: "2", Name: "江苏", ChildrenIDs: []string{"3", "4"}},
						},
						{
							{ID: "1", Name: "杭州", ChildrenIDs: []string{"1", "2"}},
							{ID: "2", Name: "宁波", ChildrenIDs: []string{"3", "4"}},
							{ID: "3", Name: "南京", ChildrenIDs: []string{"5", "6"}},
							{ID: "4", Name: "苏州", ChildrenIDs: []string{"7", "8"}},
						},
						{
							{ID: "1", Name: "拱墅区"},
							{ID: "2", Name: "西湖区"},
							{ID: "3", Name: "镇海区"},
							{ID: "4", Name: "鄞州区"},
							{ID: "5", Name: "鼓楼区"},
							{ID: "6", Name: "玄武区"},
							{ID: "7", Name: "常熟区"},
							{ID: "8", Name: "吴江区"},
						},
					},
					Labels: []string{"Province", "City", "District"},
				},
			},
		}),
		expectedSQLConds: "province_id = ? AND city_id = ? AND district_id = ?",
		expectedSQLArgs:  []interface{}{"2", "3", "7"},
	},
	{
		name: "ItemTypeDate",
		data: FilterData([]*FilterItem{
			{
				Key:          "created",
				ItemType:     ItemTypeDate,
				SQLCondition: "created_at %s ?",
			},
		}),
		qs: "created=2019-04-10",
		expected: FilterData([]*FilterItem{
			{
				Key:      "created",
				ItemType: ItemTypeDate,
				Modifier: ModifierEquals,
				Selected: true,
				ValueIs:  "2019-04-10",
			},
		}),
		expectedSQLConds: "created_at >= ? AND created_at < ?",
		expectedSQLArgs:  []interface{}{mustParseDatetimePickerValue("2019-04-10 00:00"), mustParseDatetimePickerValue("2019-04-11 00:00")},
	},
}

func TestSetByQueryString(t *testing.T) {
	for _, c := range setByQueryCases {
		t.Run(c.name, func(t *testing.T) {
			conds, args := c.data.SetByQueryString(c.qs)
			diff := testingutils.PrettyJsonDiff(c.expected, c.data)
			if len(diff) > 0 {
				t.Error(c.name, diff)
			}

			diff1 := testingutils.PrettyJsonDiff(c.expectedSQLConds, conds)
			if len(diff1) > 0 {
				t.Error(c.name, "conds", diff1)
			}

			diff2 := testingutils.PrettyJsonDiff(c.expectedSQLArgs, args)
			if len(diff2) > 0 {
				t.Error(c.name, "args", diff2)
			}
		})
	}
}
