// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package util

import (
	"errors"
	"testing"
)

func TestSplitEscaped(t *testing.T) {
	tests := []struct {
		desc string
		in   string
		want []string
	}{
		{
			desc: "empty",
			in:   "",
			want: []string{},
		},
		{
			desc: "no match",
			in:   "foo",
			want: []string{"foo"},
		},
		{
			desc: "first",
			in:   ":foo",
			want: []string{"", "foo"},
		},
		{
			desc: "last",
			in:   "foo:",
			want: []string{"foo", ""},
		},
		{
			desc: "multiple",
			in:   "foo:bar:baz",
			want: []string{"foo", "bar", "baz"},
		},
		{
			desc: "multiple with escapes",
			in:   `foo\:bar:baz\:qux`,
			want: []string{`foo\:bar`, `baz\:qux`},
		},
	}
	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			if got, want := splitEscaped(tt.in, kvSeparatorRune), tt.want; !stringSlicesEqual(got, want) {
				t.Errorf("%s: got:%v, want:%v", tt.desc, got, want)
			}
		})
	}
}

func TestIsNPathElement(t *testing.T) {
	tests := []struct {
		desc   string
		in     string
		expect bool
	}{
		{
			desc:   "empty",
			in:     "",
			expect: false,
		},
		{
			desc:   "negative",
			in:     "[-45]",
			expect: false,
		},
		{
			desc:   "negative-1",
			in:     "[-1]",
			expect: true,
		},
		{
			desc:   "valid",
			in:     "[0]",
			expect: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			if got := IsNPathElement(tt.in); got != tt.expect {
				t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got)
			}
		})
	}
}

func stringSlicesEqual(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}
	for i, aa := range a {
		if aa != b[i] {
			return false
		}
	}
	return true
}

func TestPathFromString(t *testing.T) {
	tests := []struct {
		desc   string
		in     string
		expect Path
	}{
		{
			desc:   "no-path",
			in:     "",
			expect: Path{},
		},
		{
			desc:   "valid-path",
			in:     "a.b.c",
			expect: Path{"a", "b", "c"},
		},
		{
			desc:   "surround-periods",
			in:     ".a.",
			expect: Path{"a"},
		},
	}

	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			if got := PathFromString(tt.in); !got.Equals(tt.expect) {
				t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got)
			}
		})
	}
}

func TestIsKVPathElement(t *testing.T) {
	tests := []struct {
		desc   string
		in     string
		expect bool
	}{
		{
			desc:   "valid",
			in:     "[1:2]",
			expect: true,
		},
		{
			desc:   "invalid",
			in:     "[:2]",
			expect: false,
		},
		{
			desc:   "invalid-2",
			in:     "[1:]",
			expect: false,
		},
		{
			desc:   "empty",
			in:     "",
			expect: false,
		},
		{
			desc:   "no-brackets",
			in:     "1:2",
			expect: false,
		},
		{
			desc:   "one-bracket",
			in:     "[1:2",
			expect: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			if got := IsKVPathElement(tt.in); got != tt.expect {
				t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got)
			}
		})
	}
}

func TestIsVPathElement(t *testing.T) {
	tests := []struct {
		desc   string
		in     string
		expect bool
	}{
		{
			desc:   "valid",
			in:     "[:1]",
			expect: true,
		},
		{
			desc:   "kv-path-elem",
			in:     "[1:2]",
			expect: false,
		},
		{
			desc:   "invalid",
			in:     "1:2",
			expect: false,
		},
		{
			desc:   "empty",
			in:     "",
			expect: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			if got := IsVPathElement(tt.in); got != tt.expect {
				t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got)
			}
		})
	}
}

func TestPathKV(t *testing.T) {
	tests := []struct {
		desc    string
		in      string
		wantK   string
		wantV   string
		wantErr error
	}{
		{
			desc:    "valid",
			in:      "[1:2]",
			wantK:   "1",
			wantV:   "2",
			wantErr: nil,
		},
		{
			desc:    "invalid",
			in:      "[1:",
			wantErr: errors.New(""),
		},
		{
			desc:    "empty",
			in:      "",
			wantErr: errors.New(""),
		},
	}

	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			if k, v, err := PathKV(tt.in); k != tt.wantK || v != tt.wantV || errNilCheck(err, tt.wantErr) {
				t.Errorf("%s: expect %v %v %v got %v %v %v", tt.desc, tt.wantK, tt.wantV, tt.wantErr, k, v, err)
			}
		})
	}
}

func TestPathV(t *testing.T) {
	tests := []struct {
		desc string
		in   string
		want string
		err  error
	}{
		{
			desc: "valid-kv",
			in:   "[1:2]",
			want: "1:2",
			err:  nil,
		},
		{
			desc: "valid-v",
			in:   "[:1]",
			want: "1",
			err:  nil,
		},
		{
			desc: "invalid",
			in:   "083fj",
			want: "",
			err:  errors.New(""),
		},
		{
			desc: "empty",
			in:   "",
			want: "",
			err:  errors.New(""),
		},
	}

	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			if got, err := PathV(tt.in); got != tt.want || errNilCheck(err, tt.err) {
				t.Errorf("%s: expect %v %v got %v %v", tt.desc, tt.want, tt.err, got, err)
			}
		})
	}
}

func TestRemoveBrackets(t *testing.T) {
	tests := []struct {
		desc       string
		in         string
		expect     string
		expectStat bool
	}{
		{
			desc:       "has-brackets",
			in:         "[yo]",
			expect:     "yo",
			expectStat: true,
		},
		{
			desc:       "one-bracket",
			in:         "[yo",
			expect:     "",
			expectStat: false,
		},
		{
			desc:       "other-bracket",
			in:         "yo]",
			expect:     "",
			expectStat: false,
		},
		{
			desc:       "no-brackets",
			in:         "yo",
			expect:     "",
			expectStat: false,
		},
		{
			desc:       "empty",
			in:         "",
			expect:     "",
			expectStat: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			if got, stat := RemoveBrackets(tt.in); got != tt.expect || stat != tt.expectStat {
				t.Errorf("%s: expect %v %v got %v %v", tt.desc, tt.expect, tt.expectStat, got, stat)
			}
		})
	}
}

func errNilCheck(err1, err2 error) bool {
	return (err1 == nil && err2 != nil) || (err1 != nil && err2 == nil)
}
