package mapstructure

import (
	"testing"
	"time"
)

func Test_TimeToStringHook(t *testing.T) {
	type Input struct {
		Time time.Time
		Id   int
	}

	type InputTPointer struct {
		Time *time.Time
		Id   int
	}

	type Output struct {
		Time string
		Id   int
	}

	type OutputTPointer struct {
		Time *string
		Id   int
	}
	now := time.Now()
	target := now.Format("2006-01-02 15:04:05")
	idValue := 1
	tests := []struct {
		input  any
		output any
		name   string
		layout string
	}{
		{
			name:   "测试Time.time转string",
			layout: "2006-01-02 15:04:05",
			input: InputTPointer{
				Time: &now,
				Id:   idValue,
			},
			output: Output{},
		},
		{
			name:   "测试*Time.time转*string",
			layout: "2006-01-02 15:04:05",
			input: InputTPointer{
				Time: &now,
				Id:   idValue,
			},
			output: OutputTPointer{},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			decoder, err := NewDecoder(&DecoderConfig{
				DecodeHook: TimeToStringHook(tt.layout),
				Result:     &tt.output,
			})
			if err != nil {
				t.Errorf("NewDecoder() err = %v，want nil", err)
			}

			if i, isOk := tt.input.(Input); isOk {
				err = decoder.Decode(i)
			}
			if i, isOk := tt.input.(InputTPointer); isOk {
				err = decoder.Decode(&i)
			}
			if err != nil {
				t.Errorf("Decode err = %v，want nil", err)
			}
			//验证测试值
			if output, isOk := tt.output.(OutputTPointer); isOk {
				if *output.Time != target {
					t.Errorf("Decode output time = %v，want %v", *output.Time, target)
				}
				if output.Id != idValue {
					t.Errorf("Decode output id = %v，want %v", output.Id, idValue)
				}
			}
			if output, isOk := tt.output.(Output); isOk {
				if output.Time != target {
					t.Errorf("Decode output time = %v，want %v", output.Time, target)
				}
				if output.Id != idValue {
					t.Errorf("Decode output id = %v，want %v", output.Id, idValue)
				}
			}
		})
	}
}

func Test_TimeToUnixIntHook(t *testing.T) {
	type InputTPointer struct {
		Time *time.Time
		Id   int
	}

	type Output[T int | *int | int32 | *int32 | int64 | *int64 | uint | *uint] struct {
		Time T
		Id   int
	}

	type test struct {
		input  any
		output any
		name   string
		layout string
	}

	now := time.Now()
	target := now.Unix()
	idValue := 1
	tests := []test{
		{
			name:   "测试Time.time转int",
			layout: "2006-01-02 15:04:05",
			input: InputTPointer{
				Time: &now,
				Id:   idValue,
			},
			output: Output[int]{},
		},
		{
			name:   "测试Time.time转*int",
			layout: "2006-01-02 15:04:05",
			input: InputTPointer{
				Time: &now,
				Id:   idValue,
			},
			output: Output[*int]{},
		},
		{
			name:   "测试Time.time转int32",
			layout: "2006-01-02 15:04:05",
			input: InputTPointer{
				Time: &now,
				Id:   idValue,
			},
			output: Output[int32]{},
		},
		{
			name:   "测试Time.time转*int32",
			layout: "2006-01-02 15:04:05",
			input: InputTPointer{
				Time: &now,
				Id:   idValue,
			},
			output: Output[*int32]{},
		},
		{
			name:   "测试Time.time转int64",
			layout: "2006-01-02 15:04:05",
			input: InputTPointer{
				Time: &now,
				Id:   idValue,
			},
			output: Output[int64]{},
		},
		{
			name:   "测试Time.time转*int64",
			layout: "2006-01-02 15:04:05",
			input: InputTPointer{
				Time: &now,
				Id:   idValue,
			},
			output: Output[*int64]{},
		},
		{
			name:   "测试Time.time转uint",
			layout: "2006-01-02 15:04:05",
			input: InputTPointer{
				Time: &now,
				Id:   idValue,
			},
			output: Output[uint]{},
		},
		{
			name:   "测试Time.time转*uint",
			layout: "2006-01-02 15:04:05",
			input: InputTPointer{
				Time: &now,
				Id:   idValue,
			},
			output: Output[*uint]{},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			decoder, err := NewDecoder(&DecoderConfig{
				DecodeHook: TimeToUnixIntHook(),
				Result:     &tt.output,
			})
			if err != nil {
				t.Errorf("NewDecoder() err = %v，want nil", err)
			}

			if i, isOk := tt.input.(InputTPointer); isOk {
				err = decoder.Decode(i)
			}
			if i, isOk := tt.input.(InputTPointer); isOk {
				err = decoder.Decode(&i)
			}
			if err != nil {
				t.Errorf("Decode err = %v，want nil", err)
			}

			//验证测试值
			switch v := tt.output.(type) {
			case Output[int]:
				if int64(v.Time) != target {
					t.Errorf("Decode output time = %v，want %v", v.Time, target)
				}
				if v.Id != idValue {
					t.Errorf("Decode output id = %v，want %v", v.Id, idValue)
				}
			case Output[*int]:
				if int64(*v.Time) != target {
					t.Errorf("Decode output time = %v，want %v", v.Time, target)
				}
				if v.Id != idValue {
					t.Errorf("Decode output id = %v，want %v", v.Id, idValue)
				}
			case Output[int32]:
				if int64(v.Time) != target {
					t.Errorf("Decode output time = %v，want %v", v.Time, target)
				}
				if v.Id != idValue {
					t.Errorf("Decode output id = %v，want %v", v.Id, idValue)
				}
			case Output[*int32]:
				if int64(*v.Time) != target {
					t.Errorf("Decode output time = %v，want %v", v.Time, target)
				}
				if v.Id != idValue {
					t.Errorf("Decode output id = %v，want %v", v.Id, idValue)
				}
			case Output[int64]:
				if int64(v.Time) != target {
					t.Errorf("Decode output time = %v，want %v", v.Time, target)
				}
				if v.Id != idValue {
					t.Errorf("Decode output id = %v，want %v", v.Id, idValue)
				}
			case Output[*int64]:
				if int64(*v.Time) != target {
					t.Errorf("Decode output time = %v，want %v", v.Time, target)
				}
				if v.Id != idValue {
					t.Errorf("Decode output id = %v，want %v", v.Id, idValue)
				}
			case Output[uint]:
				if int64(v.Time) != target {
					t.Errorf("Decode output time = %v，want %v", v.Time, target)
				}
				if v.Id != idValue {
					t.Errorf("Decode output id = %v，want %v", v.Id, idValue)
				}
			case Output[*uint]:
				if int64(*v.Time) != target {
					t.Errorf("Decode output time = %v，want %v", v.Time, target)
				}
				if v.Id != idValue {
					t.Errorf("Decode output id = %v，want %v", v.Id, idValue)
				}
			}

		})
	}
}
