package collect

import (
	"reflect"
	"strconv"
)

func ArrayColumn[R any, T any](arr []T, column string) []R {
	defer func() {
		if err := recover(); err != nil {
			panic(err)
		}
	}()

	var result []R
	s := reflect.ValueOf(arr)

	for i := 0; i < s.Len(); i++ {
		e := s.Index(i)
		if e.Kind() == reflect.Ptr {
			e = e.Elem()
		}
		k := e.FieldByName(column)
		if k.Kind() == reflect.Ptr {
			k = k.Elem()
		}
		result = append(result, k.Interface().(R))
	}

	return result
}

func ArrayColumnTag[R any, T any](arrSrc []T, tag string) []R {
	defer func() {
		if err := recover(); err != nil {
			panic(err)
		}
	}()

	var result []R
	arr := reflect.ValueOf(arrSrc)
	if arr.Len() == 0 {
		return result
	}

	ele := arr.Index(0)
	if ele.Kind() == reflect.Ptr {
		ele = ele.Elem()
	}
	eleType := ele.Type()

	for j := 0; j < eleType.NumField(); j++ {
		if value, ok := eleType.Field(j).Tag.Lookup("json"); ok && value == tag {

			for i := 0; i < arr.Len(); i++ {
				srcv := arr.Index(i)
				if srcv.Kind() == reflect.Ptr {
					srcv = srcv.Elem()
				}
				fv := srcv.Field(j)
				if fv.Kind() == reflect.Ptr {
					fv = fv.Elem()
				}
				result = append(result, fv.Interface().(R))
			}

			return result
		}
	}

	return result
}

func ArrayIndex[T any](arr []T, index int) (result T, ok bool) {

	if index < len(arr) {
		result = arr[index]
		ok = true
		return
	}
	ok = false
	return
}

func ArrayString2Int(arr interface{}) (result []int64) {
	defer func() {
		if err := recover(); err != nil {
			panic(err)
		}
	}()

	for _, a := range arr.([]string) {
		v, err := strconv.ParseInt(a, 10, 64)
		if err != nil {
			panic(err)
		}
		result = append(result, v)
	}
	return result
}

func Array2MapByKey[KEY comparable, VALUE any](arrSrc []VALUE, fieldName string) (result map[KEY]VALUE) {
	defer func() {
		if err := recover(); err != nil {
			panic(err)
		}
	}()

	result = make(map[KEY]VALUE)
	arr := reflect.ValueOf(arrSrc)

	for i := 0; i < arr.Len(); i++ {
		srcv := arr.Index(i)
		if srcv.Kind() == reflect.Ptr {
			if srcv.IsNil() {
				continue
			}
			srcv = srcv.Elem()
		}
		fv := srcv.FieldByName(fieldName)
		k := fv.Interface().(KEY)
		result[k] = srcv.Interface().(VALUE)
	}

	return result
}

// 一个数组slice转换成 map[tag]slice ,以slice元素的某个tag为map
func Array2MapByKeyTag[KEY comparable, VALUE any](arrSrc []VALUE, tag string) (result map[KEY]VALUE) {

	defer func() {
		if err := recover(); err != nil {
			panic(err)
		}
	}()

	arr := reflect.ValueOf(arrSrc)

	if arr.Len() == 0 {
		return result
	}
	result = make(map[KEY]VALUE)

	ele := arr.Index(0)
	if ele.Kind() == reflect.Ptr {
		ele = ele.Elem()
	}
	eleType := ele.Type()

	for j := 0; j < eleType.NumField(); j++ {
		if value, ok := eleType.Field(j).Tag.Lookup("json"); ok && value == tag {

			for i := 0; i < arr.Len(); i++ {
				srcv := arr.Index(i)
				var fv reflect.Value
				if srcv.Kind() == reflect.Ptr {
					if srcv.IsNil() {
						continue
					}
					fv = srcv.Elem().Field(j)
				} else {
					fv = srcv.Field(j)
				}

				if fv.Kind() == reflect.Ptr {
					fv = fv.Elem()
				}
				k := fv.Interface().(KEY)
				result[k] = srcv.Interface().(VALUE)
			}

			return
		}
	}

	return result
}

func StructJson2Map(s interface{}) map[string]interface{} {
	t := reflect.TypeOf(s)
	v := reflect.ValueOf(s)
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}

	var data = make(map[string]interface{})
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		if tag, ok := field.Tag.Lookup("json"); ok {
			val := v.Field(i)
			if val.Kind() == reflect.Ptr && !val.IsNil() {
				val = val.Elem()
			}
			data[tag] = val.Interface()
		}
	}
	return data
}

func StructSliceJson2Maps(s interface{}) []map[string]interface{} {
	slice := reflect.ValueOf(s)
	var maps []map[string]interface{}

	for i := 0; i < slice.Len(); i++ {
		s := slice.Index(i)

		if s.Kind() == reflect.Ptr && !s.IsNil() {
			s = s.Elem()
		}
		structValue := s.Interface()
		m := StructJson2Map(structValue)
		maps = append(maps, m)
	}
	return maps
}

// LoadJsonTag 根据loader的json tag 赋值 给拥有相同json tag
func LoadJsonTag(v interface{}, loaded interface{}) {
	vtype := reflect.TypeOf(v)
	if vtype.Kind() != reflect.Ptr {
		panic("v must is a pointer")
	}
	vtype = vtype.Elem()

	var vvalue reflect.Value
	if vtype.Kind() == reflect.Ptr {
		vtype = vtype.Elem()
		vvalue = reflect.ValueOf(v).Elem().Elem()
	} else {
		vvalue = reflect.ValueOf(v).Elem()
	}

	ltype := reflect.TypeOf(loaded)
	if ltype.Kind() != reflect.Ptr {
		panic("loaded must is a pointer")
	}
	ltype = ltype.Elem()
	var lvalue reflect.Value
	if ltype.Kind() == reflect.Ptr {
		ltype = ltype.Elem()
		lvalue = reflect.ValueOf(loaded).Elem().Elem()
	} else {
		lvalue = reflect.ValueOf(loaded).Elem()
	}

	for i := 0; i < vtype.NumField(); i++ {
		vfield := vtype.Field(i)

		if vtag, ok := vfield.Tag.Lookup("json"); ok {

			for j := 0; j < ltype.NumField(); j++ {
				lfield := ltype.Field(j)
				if ltag, ok := lfield.Tag.Lookup("json"); ok && vtag == ltag {
					vv := vvalue.Field(i)
					lv := lvalue.Field(j)

					if vv.Kind() == reflect.Ptr {

						if lv.Kind() == reflect.Ptr {
							vv.Set(lv)
						} else {
							x := reflect.New(vv.Type().Elem())
							x.Elem().Set(lv)
							vv.Set(x)
						}

					} else {

						if lv.Kind() != reflect.Ptr {
							vv.Set(lv)
						} else {
							vv.Set(lv.Elem())
						}

					}
				}
			}
		}
	}
}