package support

import (
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"unsafe"

	"github.com/spf13/cast"
)

func GetFieldValue(i any, k string) any {
	v := reflect.ValueOf(i)

	var kv reflect.Value

	if v.Kind() == reflect.Ptr {
		v = reflect.Indirect(v)
	}

	if v.Kind() == reflect.Struct {
		kv = v.FieldByName(k)
	} else if v.Kind() == reflect.Map {
		for _, key := range v.MapKeys() {
			if key.String() == k {
				kv = v.MapIndex(key)
			}
		}
	}

	switch kv.Kind() {
	case reflect.String:
		return kv.String()
	case reflect.Bool:
		return kv.Bool()
	case reflect.Float32, reflect.Float64:
		return kv.Float()
	case reflect.Int, reflect.Int64:
		return int(kv.Int())
	case reflect.Invalid:
		return nil
	}

	return kv.Interface()
}

func GetStructField(item any, key string) (field reflect.StructField, ok bool) {
	vItem := reflect.TypeOf(item)

	if vItem.Kind() == reflect.Ptr {
		vItem = vItem.Elem()
	}

	if vItem.Kind() != reflect.Struct {
		return field, false
	}

	return vItem.FieldByName(UcFirst(key))
}

func InterfaceToSlice(i any) (ret []any) {
	rv := reflect.ValueOf(i)

	if rv.Kind() == reflect.Ptr {
		rv = reflect.Indirect(rv)
	}

	if rv.Kind() != reflect.Slice {
		panic("请传入切片类型数据")
	}

	for i := 0; i < rv.Len(); i++ {
		ret = append(ret, rv.Index(i).Interface())
	}

	return ret
}

func SliceConvert(origSlice any, newSliceType reflect.Type) any {
	sv := reflect.ValueOf(origSlice)

	if sv.Kind() != reflect.Slice {
		panic(fmt.Sprintf("无效切片类型 %T (来源)", origSlice))
	}

	if newSliceType.Kind() != reflect.Slice {
		panic(fmt.Sprintf("无效切片类型 %T (目标)", newSliceType))
	}

	// 生成新类型的切片
	newSlice := reflect.New(newSliceType)

	// hdr指向到新生成切片的SliceHeader
	hdr := (*reflect.SliceHeader)(unsafe.Pointer(newSlice.Pointer()))

	var newElemSize = int(sv.Type().Elem().Size()) / int(newSliceType.Elem().Size())

	// 设置SliceHeader的Cap,Len,以及数组的ptr
	hdr.Cap = sv.Cap() * newElemSize
	hdr.Len = sv.Len() * newElemSize
	hdr.Data = uintptr(sv.Pointer())

	return newSlice.Elem().Interface()
}

func DeepSearch(m map[string]any, path []string) map[string]any {
	for _, k := range path {
		m2, ok := m[k]
		if !ok {
			// intermediate key does not exist
			// => create it and continue from there
			m3 := make(map[string]interface{})
			m[k] = m3
			m = m3
			continue
		}
		m3, ok := m2.(map[string]interface{})
		if !ok {
			// intermediate key is a value
			// => replace with a new map
			m3 = make(map[string]interface{})
			m[k] = m3
		}
		// continue search from here
		m = m3
	}
	return m
}

func MergeMap(shadow map[string]any, m map[string]any) map[string]any {
	if shadow == nil {
		shadow = make(map[string]any)
	}

	var m2 map[string]any

	for k, val := range m {
		switch val.(type) {
		case map[string]any:
			m2 = val.(map[string]any)
		case map[any]any:
			m2 = cast.ToStringMap(val)
		default:
			// immediate value
			shadow[k] = val
			continue
		}
		// recursively merge to shadow map
		shadow[k] = MergeMap(cast.ToStringMap(shadow[k]), m2)
	}
	return shadow
}

func UcFirst(s string) string {
	if s == "" {
		return ""
	}
	return strings.ToUpper(s[:1]) + s[1:]
}

func StudlyCase(s string) string {
	return strings.Replace(strings.Title(strings.Replace(s, "-", " ", -1)), " ", "", -1)
}

func GetAbsPath(s string) string {
	if strings.HasPrefix(s, "/") {
		return s
	} else if dir, err := os.Getwd(); err == nil {
		return filepath.Join(dir, s)
	}

	return s
}