package toolbox_test

import (
	"reflect"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/viant/toolbox"
)

func TestProcessStruct(t *testing.T) {

	type Super struct {
		Parent int
	}
	type User struct {
		*Super
		Name        string    `column:"name"`
		DateOfBirth time.Time `column:"date" dateFormat:"2006-01-02 15:04:05.000000"`
		Id          int       `autogenrated:"true"`
		prv         int
		Other       string `transient:"true"`
	}

	user := User{Id: 1, Other: "!@#", Name: "foo", Super: &Super{12}}
	var userMap = make(map[string]interface{})
	err := toolbox.ProcessStruct(&user, func(fieldType reflect.StructField, field reflect.Value) error {
		value := field.Interface()
		userMap[fieldType.Name] = value
		return nil
	})
	assert.Nil(t, err)
	assert.Equal(t, 5, len(userMap))

	assert.Equal(t, 12, userMap["Parent"])
	assert.Equal(t, 1, userMap["Id"])
	assert.Equal(t, "!@#", userMap["Other"])
}

func TestBuildTagMapping(t *testing.T) {

	type User struct {
		Name        string    `column:"name"`
		DateOfBirth time.Time `column:"date" dateFormat:"2006-01-02 15:04:05.000000"`
		Id          int       `autogenrated:"true"`
		Other       string    `transient:"true"`
	}

	{
		tags := []string{"column", "autogenrated"}
		result := toolbox.BuildTagMapping((*User)(nil), "column", "transient", true, true, tags)

		{
			actual := len(result)
			expected := 3
			assert.Equal(t, actual, expected, "Extract mapping count")
		}
		{
			actual, _ := result["name"]["fieldName"]
			expected := "Name"
			assert.Equal(t, actual, expected, "Extract name mapping")
		}

		{
			actual, _ := result["id"]["autogenrated"]
			expected := "true"
			assert.Equal(t, actual, expected, "Extract id flaged as autogenerated")
		}
	}
	{
		tags := []string{"column", "autogenrated"}
		result := toolbox.BuildTagMapping((*User)(nil), "fieldName", "transient", true, false, tags)
		actual, _ := result["Name"]["fieldName"]
		expected := "Name"
		assert.Equal(t, actual, expected, "Extract name mapping")
	}

	{
		tags := []string{"column", "autogenrated"}
		result := toolbox.BuildTagMapping((*User)(nil), "column", "transient", false, false, tags)
		{
			actual := len(result)
			expected := 2
			assert.Equal(t, actual, expected, "Extract mapping count")
		}
	}

	type User2 struct {
		Name        string    `json:"name" column:"name"`
		DateOfBirth time.Time `json:"date" column:"date" dateFormat:"2006-01-02 15:04:05.000000"`
		Id          int       `json:"id" autogenrated:"true"`
		Other       string    `json:"other" transient:"true"`
	}

	{
		tags := []string{"column", "autogenrated"}
		result := toolbox.BuildTagMapping((*User)(nil), "column", "transient", true, true, tags)

		{
			actual := len(result)
			expected := 3
			assert.Equal(t, actual, expected, "Extract mapping count")
		}
		{
			actual, _ := result["name"]["fieldName"]
			expected := "Name"
			assert.Equal(t, actual, expected, "Extract name mapping")
		}

		{
			actual, _ := result["id"]["autogenrated"]
			expected := "true"
			assert.Equal(t, actual, expected, "Extract id flaged as autogenerated")
		}
	}

}

func TestBuildEmbededStructTagMapping(t *testing.T) {

	type Super struct {
		Id   int    `autogenrated:"true"`
		Name string `column:"name"`
	}

	type User struct {
		*Super
		DateOfBirth time.Time `column:"date" dateFormat:"2006-01-02 15:04:05.000000"`
		Other       string    `transient:"true"`
	}

	tags := []string{"column", "autogenrated"}
	result := toolbox.BuildTagMapping((*User)(nil), "column", "transient", true, true, tags)

	{
		actual := len(result)
		expected := 3
		assert.Equal(t, actual, expected, "Extract mapping count")
	}
	{
		actual, _ := result["name"]["fieldName"]
		expected := "Name"
		assert.Equal(t, actual, expected, "Extract name mapping")
	}

	{
		actual, _ := result["id"]["autogenrated"]
		expected := "true"
		assert.Equal(t, actual, expected, "Extract id flaged as autogenerated")
	}

}

type Type4 struct {
	Id int
}

type Type3 struct {
	Name  map[string]string
	Type4 map[string]*Type4
}

type Type2 struct {
	F1 int
	F3 *Type3
}

type Type1 struct {
	F1 int
	F2 *Type2
	F3 []interface{}
	F4 map[string]interface{}
	F5 []*Type3
}

type SuperType1 struct {
	*Type1
}

type SuperType2 struct {
	Type1
}

func Test_InitStruct(t *testing.T) {

	{
		var t1 = &Type1{}
		toolbox.InitStruct(t1)
		assert.NotNil(t, t1.F2)
		assert.NotNil(t, t1.F3)
		assert.NotNil(t, t1.F4)
		assert.NotNil(t, t1.F5)

	}
	{
		var t1 = &SuperType1{}
		toolbox.InitStruct(t1)
		assert.NotNil(t, t1.F2)
		assert.NotNil(t, t1.F3)
		assert.NotNil(t, t1.F4)
		assert.NotNil(t, t1.F5)
	}
	{
		var t1 = &SuperType2{}
		toolbox.InitStruct(t1)
		assert.NotNil(t, t1.F2)
		assert.NotNil(t, t1.F3)
		assert.NotNil(t, t1.F4)
		assert.NotNil(t, t1.F5)
	}

}

func Test_GetStructMeta(t *testing.T) {

	var t1 = &Type1{}
	toolbox.InitStruct(t1)
	meta := toolbox.GetStructMeta(t1)
	assert.NotNil(t, meta)

}
