// Copyright 2021 Molecula Corp. All rights reserved.
package pilosa

import (
	"reflect"
	"testing"
)

func TestPlanLike(t *testing.T) {
	cases := []struct {
		name            string
		like            string
		plan            []filterStep
		match, nonmatch []string
	}{
		{
			name:     "Empty",
			like:     "",
			plan:     []filterStep{},
			match:    []string{""},
			nonmatch: []string{"a", " "},
		},
		{
			name: "Exact",
			like: "x",
			plan: []filterStep{
				{
					kind: filterStepPrefix,
					str:  []byte("x"),
				},
			},
			match:    []string{"x"},
			nonmatch: []string{"", "y", "z", "xy", "yx"},
		},
		{
			name: "Anything",
			like: "%",
			plan: []filterStep{
				{
					kind: filterStepMinLength,
					n:    0,
				},
			},
			match: []string{"", "a", "b", "ab"},
		},
		{
			name: "Prefix",
			like: "x%",
			plan: []filterStep{
				{
					kind: filterStepPrefix,
					str:  []byte("x"),
				},
				{
					kind: filterStepMinLength,
					n:    0,
				},
			},
			match:    []string{"xy", "xyz", "xyzzy"},
			nonmatch: []string{"plugh", "yx", ""},
		},
		{
			name: "Suffix",
			like: "%x",
			plan: []filterStep{
				{
					kind: filterStepSuffix,
					str:  []byte("x"),
				},
			},
			match:    []string{"x", "xx", "ax"},
			nonmatch: []string{"", "a", "x^"},
		},
		{
			name: "Sandwich",
			like: "x%y",
			plan: []filterStep{
				{
					kind: filterStepPrefix,
					str:  []byte("x"),
				},
				{
					kind: filterStepSuffix,
					str:  []byte("y"),
				},
			},
			match:    []string{"xy", "xzy", "xyzzy"},
			nonmatch: []string{"plugh", ".xy.", ".x.y", "x.y."},
		},
		{
			name: "DoubleDeckerSandwich",
			like: "x%y%z",
			plan: []filterStep{
				{
					kind: filterStepPrefix,
					str:  []byte("x"),
				},
				{
					kind: filterStepSkipThrough,
					str:  []byte("y"),
				},
				{
					kind: filterStepSuffix,
					str:  []byte("z"),
				},
			},
			match:    []string{"xyz", "xzyzz", "x.y.z", "x.y.y..z"},
			nonmatch: []string{"plugh", ".xyz.", ".x.y.z", "x.y.z."},
		},
		{
			name: "Skips",
			like: "a_b_%_c_%_%_d",
			plan: []filterStep{
				{
					kind: filterStepPrefix,
					str:  []byte("a"),
				},
				{
					kind: filterStepSkipN,
					n:    1,
				},
				{
					kind: filterStepPrefix,
					str:  []byte("b"),
				},
				{
					kind: filterStepSkipThrough,
					str:  []byte("c"),
					n:    2,
				},
				{
					kind: filterStepSuffix,
					str:  []byte("d"),
					n:    3,
				},
			},
			match:    []string{"a1b234c5678d"},
			nonmatch: []string{"abcd", "a1b2345678d", "a1b2c5678d"},
		},
		{
			name: "SingleRune",
			like: "_",
			plan: []filterStep{
				{
					kind: filterStepSkipN,
					n:    1,
				},
			},
			match:    []string{"a", "á", "☺"},
			nonmatch: []string{"ab", "á", "h̷", ""},
		},
		{
			name: "DoubleRune",
			like: "__",
			plan: []filterStep{
				{
					kind: filterStepSkipN,
					n:    2,
				},
			},
			match:    []string{"ab", "á", "h̷"},
			nonmatch: []string{"a", "á", "☺", "abc"},
		},
		{
			name: "MiddleBlank",
			like: "x_y",
			plan: []filterStep{
				{
					kind: filterStepPrefix,
					str:  []byte("x"),
				},
				{
					kind: filterStepSkipN,
					n:    1,
				},
				{
					kind: filterStepPrefix,
					str:  []byte("y"),
				},
			},
			match:    []string{"x.y", "xay", "x y", "x⊕y"},
			nonmatch: []string{"x++y", "", "a"},
		},
		{
			name: "MinLength",
			like: "_%_",
			plan: []filterStep{
				{
					kind: filterStepMinLength,
					n:    2,
				},
			},
			match:    []string{"ab", "á", "abc", "pilosa"},
			nonmatch: []string{"h", "á", ".", "☺"},
		},
	}
	t.Run("Plan", func(t *testing.T) {
		t.Parallel()

		for _, c := range cases {
			c := c
			t.Run(c.name, func(t *testing.T) {
				t.Parallel()

				plan := planLike(c.like)
				if !reflect.DeepEqual(plan, c.plan) {
					t.Errorf("incorrect plan: %v", plan)
				}
			})
		}
	})
	t.Run("Match", func(t *testing.T) {
		t.Parallel()

		for _, c := range cases {
			c := c
			t.Run(c.name, func(t *testing.T) {
				t.Parallel()

				for _, m := range c.match {
					if !matchLike([]byte(m), c.plan...) {
						t.Errorf("key %q was not matched", m)
					}
				}
				for _, nm := range c.nonmatch {
					if matchLike([]byte(nm), c.plan...) {
						t.Errorf("key %q was matched", nm)
					}
				}
			})
		}
	})
}
