package jsonpointer

import (
	"encoding/json"
	"reflect"
	"testing"
)

func TestGet(t *testing.T) {
	type target struct {
		path string
		want interface{}
	}

	tests := []struct {
		body   string
		fields []target
	}{
		{
			body: `{"key":"value"}`,
			fields: []target{
				{path: "/key", want: string("value")},
			},
		},
		{
			body: `{"path":{"to":{"value":5}}}`,
			fields: []target{
				{path: "/path/to/value", want: int64(5)},
			},
		},
		{
			body: `[1,2,3]`,
			fields: []target{
				{path: "", want: []uint16{1, 2, 3}},
			},
		},
		{
			body: `"hi"`,
			fields: []target{
				{path: "", want: string("hi")},
			},
		},
		{
			body: `{"mixed":[null,{"1":0.2}]}`,
			fields: []target{
				{path: "/mixed/1/1", want: float64(0.2)},
			},
		},
	}

	for _, tt := range tests {
		for _, field := range tt.fields {
			dest := reflect.New(reflect.TypeOf(field.want))
			v := dest.Interface()

			jp, err := NewPointer(field.path)
			if err != nil {
				t.Errorf("NewPointer: %v", err)
				continue
			}
			err = Get([]byte(tt.body), jp, v)
			if err != nil {
				t.Logf("body: %s", tt.body)
				t.Errorf("Get: %v", err)
				continue
			}
			if have, want := dest.Elem().Interface(), field.want; !reflect.DeepEqual(have, want) {
				t.Errorf("Get(%s, %q, %T); %#v != %#v", tt.body, jp, dest.Interface(),
					have, want)
			}
		}
	}
}

func TestBuild(t *testing.T) {
	jp := func(path string) Pointer {
		ptr, err := NewPointer(path)
		if err != nil {
			t.Fatalf("jp(%q): %v", path, err)
		}
		return ptr
	}

	tests := []struct {
		values Values
		body   string
	}{
		{
			values: Values{},
			body:   `null`,
		},
		{
			values: Values{
				{Location: jp(""), Value: 5},
			},
			body: `5`,
		},
		{
			values: Values{
				{Location: jp("/key"), Value: "value"},
			},
			body: `{"key":"value"}`,
		},
		{
			values: Values{
				{Location: jp("/foo"), Value: 7},
				{Location: jp("/bar"), Value: nil},
				{Location: jp("/baz"), Value: "hi"},
				{Location: jp("/quux/more/0"), Value: "hello"},
			},
			body: `{"foo":7,"bar":null,"baz":"hi","quux":{"more":{"0":"hello"}}}`,
		},
		// {
		// 	values: values{
		// 		{Location: jp("/foo"), Value: 7},
		// 		{Location: jp("/bar"), Value: nil},
		// 		{Location: jp("/baz"), Value: "hi"},
		// 		{Location: jp("/quux/more/0"), Value: "hello"},
		// 		{Location: jp("/good"), Value: "bye"},
		// 	},
		// 	body: `{"foo":7,"bar":null,"baz":"hi","quux":{"more":{"0":"hello"}},"good":"bye"}`,
		// },
	}

	for _, tt := range tests {
		buf, err := json.Marshal(tt.values)
		if err != nil {
			t.Errorf("json.Marshal(%q): %v", tt.values, err)
			continue
		}

		if have, want := string(buf), tt.body; have != want {
			t.Errorf("json.Marshal(%q);\n%s\n!=\n%s", tt.values, have, want)
		}
	}
}
