// Copyright © 2016 Abcum Ltd
//
// 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 keys_test

import (
	"testing"
	"time"

	"golang.org/x/text/collate"
	"golang.org/x/text/language"

	"github.com/abcum/surreal/util/keys"
	. "github.com/smartystreets/goconvey/convey"
)

func TestEncodeDecode(t *testing.T) {

	timer, _ := time.Parse(time.RFC3339, "1987-06-22T08:00:00.123456789Z")

	tests := []struct {
		str string
		obj keys.Key
		new keys.Key
	}{
		{
			str: "/surreal/!/n/abcum",
			obj: &keys.NS{KV: "surreal", NS: "abcum"},
			new: &keys.NS{},
		},
		{
			str: "/surreal/!/d/abcum/database",
			obj: &keys.DB{KV: "surreal", NS: "abcum", DB: "database"},
			new: &keys.DB{},
		},
		{
			str: "/surreal/!/t/abcum/database/person",
			obj: &keys.TB{KV: "surreal", NS: "abcum", DB: "database", TB: "person"},
			new: &keys.TB{},
		},
		{
			str: "/surreal/!/f/abcum/database/person/fullname",
			obj: &keys.FD{KV: "surreal", NS: "abcum", DB: "database", TB: "person", FD: "fullname"},
			new: &keys.FD{},
		},
		{
			str: "/surreal/!/i/abcum/database/person/teenagers",
			obj: &keys.IX{KV: "surreal", NS: "abcum", DB: "database", TB: "person", IX: "teenagers"},
			new: &keys.IX{},
		},
		{
			str: "/surreal/abcum/database/person/¤/[firstname,lastname]",
			obj: &keys.Index{KV: "surreal", NS: "abcum", DB: "database", TB: "person", What: []string{"firstname", "lastname"}},
			new: &keys.Index{},
		},
		{
			str: "/surreal/abcum/database/person/873c2f37-ea03-4c5e-843e-cf393af44155",
			obj: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "873c2f37-ea03-4c5e-843e-cf393af44155"},
			new: &keys.Thing{},
		},
		{
			str: "/surreal/abcum/database/person/•/873c2f37-ea03-4c5e-843e-cf393af44155/1987-06-22T08:00:00.123456789Z",
			obj: &keys.Trail{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "873c2f37-ea03-4c5e-843e-cf393af44155", Time: timer},
			new: &keys.Trail{},
		},
		{
			str: "/surreal/abcum/database/person/‡/873c2f37-ea03-4c5e-843e-cf393af44155/friend/1987-06-22T08:00:00.123456789Z",
			obj: &keys.Event{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "873c2f37-ea03-4c5e-843e-cf393af44155", Type: "friend", Time: timer},
			new: &keys.Event{},
		},
		{
			str: "/surreal/abcum/database/person/«»/873c2f37-ea03-4c5e-843e-cf393af44155/clicked/b38d7aa1-60d6-4f2d-8702-46bd0fa961fe",
			obj: &keys.Edge{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "873c2f37-ea03-4c5e-843e-cf393af44155", Type: "clicked", Edge: "b38d7aa1-60d6-4f2d-8702-46bd0fa961fe"},
			new: &keys.Edge{},
		},
		{
			str: "/surreal/abcum/database/person/«/873c2f37-ea03-4c5e-843e-cf393af44155/clicked/b38d7aa1-60d6-4f2d-8702-46bd0fa961fe",
			obj: &keys.Edge{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "873c2f37-ea03-4c5e-843e-cf393af44155", TK: "«", Type: "clicked", Edge: "b38d7aa1-60d6-4f2d-8702-46bd0fa961fe"},
			new: &keys.Edge{},
		},
		{
			str: "/surreal/abcum/database/person/»/873c2f37-ea03-4c5e-843e-cf393af44155/clicked/b38d7aa1-60d6-4f2d-8702-46bd0fa961fe",
			obj: &keys.Edge{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "873c2f37-ea03-4c5e-843e-cf393af44155", TK: "»", Type: "clicked", Edge: "b38d7aa1-60d6-4f2d-8702-46bd0fa961fe"},
			new: &keys.Edge{},
		},
		{
			str: "/surreal/abcum/database/person/«»/873c2f37-ea03-4c5e-843e-cf393af44155/clicked/b38d7aa1-60d6-4f2d-8702-46bd0fa961fe",
			obj: &keys.Edge{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "873c2f37-ea03-4c5e-843e-cf393af44155", TK: "«»", Type: "clicked", Edge: "b38d7aa1-60d6-4f2d-8702-46bd0fa961fe"},
			new: &keys.Edge{},
		},
	}

	for _, test := range tests {

		Convey(test.str, t, func() {

			Convey("String should match", func() {
				So(test.obj.String(), ShouldEqual, test.str)
			})

			Convey("String should be a string", func() {
				So(test.obj.String(), ShouldHaveSameTypeAs, "")
			})

			Convey("Encode should be a byte slice", func() {
				So(test.obj.Encode(), ShouldHaveSameTypeAs, []byte{})
			})

			Convey("Key should encode and decode", func() {
				enc := test.obj.Encode()
				test.new.Decode(enc)
				// So(test.new.String(), ShouldEqual, test.str)
			})

		})

	}

}

func TestSorting(t *testing.T) {

	c := collate.New(language.English, collate.Force, collate.Numeric)

	tests := []struct {
		res int
		one keys.Key
		two keys.Key
	}{
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "1"},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "2"},
		},
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "2"},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "12"},
		},
		{
			res: 1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "12"},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "2"},
		},
		{
			res: -1, // Fullwidth is sorted as usual.
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "2"},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "12"},
		},
		{
			res: 1, // Circled is not sorted as numbers.
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "②"},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "①②"},
		},
		{
			res: 1, // Subscript is not sorted as numbers.
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "₂"},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "₁₂"},
		},
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "abc"},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "ABC"},
		},
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "ABC"},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "zbc"},
		},
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "ÄBC"},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "zbc"},
		},
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "ÁBC"},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "zbc"},
		},
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "zbc"},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "ZBC"},
		},
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: keys.Prefix},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: keys.Suffix},
		},
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: keys.Prefix},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "a"},
		},
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: keys.Prefix},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "①"},
		},
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: keys.Prefix},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "会"},
		},
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: keys.Prefix},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "😀"},
		},
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "a"},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: keys.Suffix},
		},
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "①"},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: keys.Suffix},
		},
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "会"},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: keys.Suffix},
		},
		{
			res: -1,
			one: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: "😀"},
			two: &keys.Thing{KV: "surreal", NS: "abcum", DB: "database", TB: "person", ID: keys.Suffix},
		},
	}

	for _, test := range tests {

		Convey("Strings should sort correctly", t, func() {
			one := string(test.one.Encode())
			two := string(test.two.Encode())
			res := c.CompareString(one, two)
			So(res, ShouldEqual, test.res)
		})

	}

}