package json

import (
	"encoding/json"
	"fmt"
	"log"
	"reflect"
	"strconv"
)

// Map type
type Map map[string]interface{}

// New init Map object
func NewMap() Map {
	return make(Map)
}

// FromInterface convert map[string]interface{} or Map interface to Map
func MapFromInterface(iface interface{}) (res Map) {
	switch val := iface.(type) {
	case map[string]interface{}:
		res = FromMap(val)
	case Map:
		res = val
	default:
		res = NewMap()
	}
	return
}

// FromMap convert map[string]interface{} to Map object
func FromMap(m map[string]interface{}) Map {
	return Map(m)
}

// Bool returns bool value by key
func (s Map) Bool(key string, defaultVal bool) bool {
	if res, check := s[key].(bool); check {
		return res
	}
	return defaultVal
}

// KeyExists check value exists by key
func (s Map) KeyExists(key string) bool {
	_, check := s[key]
	return check
}

// KeysExists check values exists by keys list.
// Returns the first blank key found.
// If all keys are defined, an empty string will be returned
func (s Map) KeysExists(keys []string) string {
	for _, key := range keys {
		if _, check := s[key]; !check {
			return key
		}
	}
	return ""
}

func val(l, r interface{}) (res reflect.Value) {
	lVal, rVal := reflect.ValueOf(l), reflect.ValueOf(r)
	if lVal.Kind() == reflect.Ptr && rVal.Kind() != reflect.Ptr {
		return val(lVal.Elem().Interface(), r)
	}
	defer func() {
		if r := recover(); r != nil {
			res = rVal
		}
	}()
	res = lVal.Convert(rVal.Type())
	return
}

func isType(l, r any) bool {
	if l == nil || r == nil {
		return false
	}
	rType := reflect.ValueOf(r).Type()
	lType := reflect.ValueOf(l).Type()
	//log.Println(rType, lType, reflect.ValueOf(iface).Convert(rType))
	return lType.ConvertibleTo(rType)
}

func (s Map) IsInt(key string) bool {
	if iface, check := s[key]; check {
		return isType(iface, int(0))
	}
	return false
}

func (s Map) IsBoolean(key string) bool {
	if iface, check := s[key]; check {
		return isType(iface, false)
	}
	return false
}

// Int returns int64 value by key.
// If key isn't defined will be returned defaultVal arg value
func (s Map) Int(key string, defaultVal int64) int64 {
	if iface, check := s[key]; check {
		return val(iface, defaultVal).Int()
	}
	return defaultVal
}

func (s Map) Int32(key string, defaultVal int) int {
	if iface, check := s[key]; check {
		return val(iface, defaultVal).Interface().(int)
	}
	return defaultVal
}

func (s Map) Float32(key string, defaultVal float32) float32 {
	if iface, check := s[key]; check {
		return val(iface, defaultVal).Interface().(float32)
	}
	return defaultVal
}

func (s Map) Float64(key string, defaultVal float64) float64 {
	if iface, check := s[key]; check {
		return val(iface, defaultVal).Interface().(float64)
	}
	return defaultVal
}

// Value returns interface object with attempt to convert to defaultVal type.
// If key isn't defined will be returned defaultVal arg value
func (s Map) Value(key string, defaultVal interface{}) interface{} {

	// check value exists by key
	if iface, check := s[key]; check {

		// check defaultVal is valid
		dVal := reflect.ValueOf(defaultVal)
		if !dVal.IsValid() {
			// invalid, return arrived interface
			return iface
		}
		// defaultVal is valid, attempt to convert found value to defaultVal type
		lVal := reflect.ValueOf(iface)

		switch {
		case !lVal.IsValid():
			return defaultVal // invalid found value, return defaultVal
		case lVal.Kind() == dVal.Kind():
			return iface // types of found value and defaultVal is match. return found value
		case lVal.Type().ConvertibleTo(dVal.Type()):
			return lVal.Convert(dVal.Type()).Interface() // found value type can be converted to defaultVal type. return converted found value
		default:
			{
				// found value type can't be converted to defaultVal type. If found value is string, attempt to convert to number value
				if lVal.Kind() == reflect.String {
					switch dVal.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						val, err := strconv.Atoi(lVal.String())
						if err != nil {
							return defaultVal
						}
						lVal = reflect.ValueOf(val)
						return lVal.Convert(dVal.Type())
					case reflect.Float32, reflect.Float64:
						val, err := strconv.ParseFloat(lVal.String(), 64)
						if err != nil {
							return defaultVal
						}
						lVal = reflect.ValueOf(val)
						return lVal.Convert(dVal.Type())
					}
				}
			}
		}
	}
	return defaultVal
}

// ValueJSON returns json source object by key
// If key isn't defined will be returned defaultVal arg value
func (s Map) ValueJSON(key string, defaultVal []byte) (res []byte) {
	res = defaultVal
	if s.KeyExists(key) {
		if r, err := json.Marshal(s[key]); err == nil {
			res = r
		}
	}
	return
}

// String returns string value by key
// If key isn't defined will be returned defaultVal arg value
func (s Map) String(key, defaultVal string) string {
	if iface, check := s[key]; check {
		return val(iface, defaultVal).String()
	}
	return defaultVal
}

func (s Map) EachSlice(key string, each func(int, any) bool) {
	sl := s.Slice(key, []any{})
	for i, val := range sl {
		if !each(i, val) {
			return
		}
	}
}

func (s Map) EachMap(key string, each func(int, Map) bool) {
	s.EachSlice(key, func(i int, val any) bool {
		switch val.(type) {
		case map[string]any:
			return each(i, Map(val.(map[string]any)))
		case Map:
			return each(i, val.(Map))
		default:
			return true
		}
	})
}

func (s Map) StringWithError(key, defaultVal string) (string, error) {
	if iface, check := s[key]; check {
		val := val(iface, defaultVal).String()
		if val == defaultVal {
			return defaultVal, fmt.Errorf("FIELD [%s] IS EMPTY", key)
		}
		return val, nil
	}
	return defaultVal, fmt.Errorf("FIELD [%s] IS NOT EXISTS", key)
}

func (s Map) Bytes(key string, defaultVal []byte) []byte {
	if iface, check := s[key]; check {
		return []byte(val(iface, defaultVal).String())
	}
	return defaultVal
}

func (s Map) BytesWithError(key string, defaultVal []byte) ([]byte, error) {
	if iface, check := s[key]; check {
		return []byte(val(iface, defaultVal).String()), nil
	}
	return defaultVal, fmt.Errorf("FIELD [%s] IS NOT EXISTS", key)
}

func (s Map) StringVal(key, defaultVal string) string {
	if iface, check := s[key]; check {
		return fmt.Sprint(iface)
	}
	return defaultVal
}

// Slce returns slice of interface{} by key
// If key isn't defined or have a different type will be returned defaultVal arg value
func (s Map) Slice(key string, defaultVal []interface{}) (res []interface{}) {
	if arr, check := s[key].([]interface{}); check {
		res = arr
	} else {
		res = defaultVal
	}
	return
}

// StringSlice returns string slice by key
// If key isn't defined will be returned defaultVal arg value
func (s Map) StringSlice(key string, defaultVal []string) (res []string) {
	if arr, check := s[key].([]interface{}); check {
		res = make([]string, len(arr))
		for i, v := range arr {
			res[i] = fmt.Sprint(v)
		}
	} else {
		res = defaultVal
	}
	return
}

// Map returns Map object by key
// If key isn't defined or have other type will be returned defaultVal arg value
func (s Map) Map(key string, defaultVal Map) (res Map) {
	val := s[key]
	switch iface := val.(type) {
	case Map:
		res = iface
	case map[string]interface{}:
		res = Map(iface)
	default:
		res = defaultVal
	}
	return
}

// JSON returns JSON source of the self object
func (s Map) JSON() (res []byte) {
	res, _ = json.Marshal(s)
	return
}

// JSONIndent returns JSON source of the self object with indent
func (s Map) JSONIndent(prefix, indent string) (res []byte) {
	res, _ = json.MarshalIndent(s, prefix, indent)
	return
}

func (s Map) JSONString() (res string) {
	if data, err := json.Marshal(s); err == nil {
		res = string(data)
	}
	return
}

func (s Map) JSONPrettyString() (res string) {
	if data, err := json.MarshalIndent(s, "", "\t"); err == nil {
		res = string(data)
	}
	return
}

func (s Map) LogPretty() {
	log.Println(s.JSONPrettyString())
}

// ToMap returns map[string]interface{} of the self object
func (s Map) ToMap() map[string]interface{} { return map[string]interface{}(s) }

// Copy returns copied map (todo dipcopy)
func (s Map) Copy() (res Map) {
	res = make(Map)
	for key, val := range s {
		res[key] = val
	}
	return
}

func (s Map) IsEmpty() bool {
	return len(s) == 0
}

func (s Map) Update(m Map) {
	for key, val := range m {
		s[key] = val
	}
}

func (s Map) StorePtr(key string, val interface{}) interface{} {
	s[key] = &val
	return &val
}

func (s Map) Variable(key string, ptr interface{}) bool {
	val, check := s[key]
	if !check {
		// value is not exists
		return false
	}
	vVal := reflect.ValueOf(ptr)
	if vVal.Kind() != reflect.Ptr {
		// ptr is not a pointer
		return false
	}
	vElem := vVal.Elem()
	rVal := reflect.ValueOf(val)
	if !rVal.CanConvert(vElem.Type()) {
		// type is not converted
		return false
	}
	vElem.Set(rVal.Convert(vElem.Type()))
	return true
}