package jsonvalue

import (
	"encoding/base64"
	"fmt"
	"math"
	"reflect"
	"strconv"
)

// New generate a new jsonvalue type via given type. If given type is not supported,
// the returned type would equal to NotExist. If you are not sure whether given value
// type is OK in runtime, use Import() instead.
//
// New 函数按照给定参数类型创建一个 jsonvalue 类型。如果给定参数不是 JSON 支持的类型, 那么返回的
// *V 对象的类型为 NotExist。如果在代码中无法确定入参是否是 JSON 支持的类型, 请改用函数
// Import()。
func New(value any) *V {
	v, _ := Import(value)
	return v
}

// NewString returns an initialized string jsonvalue object
//
// NewString 用给定的 string 返回一个初始化好的字符串类型的 jsonvalue 值
func NewString(s string) *V {
	v := new(globalPool{}, String)
	v.valueStr = s
	return v
}

// NewBytes returns an initialized string with Base64 string by given bytes
//
// NewBytes 用给定的字节串，返回一个初始化好的字符串类型的 jsonvalue 值，内容是字节串 Base64 之后的字符串。
func NewBytes(b []byte) *V {
	s := base64.StdEncoding.EncodeToString(b)
	return NewString(s)
}

// NewInt64 returns an initialized num jsonvalue object by int64 type
//
// NewInt64 用给定的 int64 返回一个初始化好的数字类型的 jsonvalue 值
func NewInt64(i int64) *V {
	v := new(globalPool{}, Number)
	// v.num = &num{}
	v.num.floated = false
	v.num.negative = i < 0
	v.num.f64 = float64(i)
	v.num.i64 = i
	v.num.u64 = uint64(i)
	s := strconv.FormatInt(v.num.i64, 10)
	v.srcByte = []byte(s)
	return v
}

// NewUint64 returns an initialized num jsonvalue object by uint64 type
//
// NewUint64 用给定的 uint64 返回一个初始化好的数字类型的 jsonvalue 值
func NewUint64(u uint64) *V {
	v := new(globalPool{}, Number)
	// v.num = &num{}
	v.num.floated = false
	v.num.negative = false
	v.num.f64 = float64(u)
	v.num.i64 = int64(u)
	v.num.u64 = u
	s := strconv.FormatUint(v.num.u64, 10)
	v.srcByte = []byte(s)
	return v
}

// NewInt returns an initialized num jsonvalue object by int type
//
// NewInt 用给定的 int 返回一个初始化好的数字类型的 jsonvalue 值
func NewInt(i int) *V {
	return NewInt64(int64(i))
}

// NewUint returns an initialized num jsonvalue object by uint type
//
// NewUint 用给定的 uint 返回一个初始化好的数字类型的 jsonvalue 值
func NewUint(u uint) *V {
	return NewUint64(uint64(u))
}

// NewInt32 returns an initialized num jsonvalue object by int32 type
//
// NewInt32 用给定的 int32 返回一个初始化好的数字类型的 jsonvalue 值
func NewInt32(i int32) *V {
	return NewInt64(int64(i))
}

// NewUint32 returns an initialized num jsonvalue object by uint32 type
//
// NewUint32 用给定的 uint32 返回一个初始化好的数字类型的 jsonvalue 值
func NewUint32(u uint32) *V {
	return NewUint64(uint64(u))
}

// NewBool returns an initialized boolean jsonvalue object
//
// NewBool 用给定的 bool 返回一个初始化好的布尔类型的 jsonvalue 值
func NewBool(b bool) *V {
	v := new(globalPool{}, Boolean)
	v.valueBool = b
	return v
}

// NewNull returns an initialized null jsonvalue object
//
// NewNull 返回一个初始化好的 null 类型的 jsonvalue 值
func NewNull() *V {
	v := new(globalPool{}, Null)
	return v
}

// NewObject returns an object-typed jsonvalue object. If keyValues is specified, it will also create some key-values in
// the object. Now we supports basic types only. Such as int/uint, int/int8/int16/int32/int64,
// uint/uint8/uint16/uint32/uint64 series, string, bool, nil.
//
// NewObject 返回一个初始化好的 object 类型的 jsonvalue 值。可以使用可选的 map[string]any 类型参数初始化该 object 的下一级键值对，
// 不过目前只支持基础类型，也就是: int/uint, int/int8/int16/int32/int64, uint/uint8/uint16/uint32/uint64, string, bool, nil。
func NewObject(keyValues ...M) *V {
	v := newObject(globalPool{})

	if len(keyValues) > 0 {
		kv := keyValues[0]
		if kv != nil {
			parseNewObjectKV(v, kv)
		}
	}

	return v
}

// M is the alias of map[string]any
type M map[string]any

func parseNewObjectKV(v *V, kv M) {
	for k, val := range kv {
		rv := reflect.ValueOf(val)
		switch rv.Kind() {
		case reflect.Invalid:
			v.MustSetNull().At(k)
		case reflect.Bool:
			v.MustSetBool(rv.Bool()).At(k)
		case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8:
			v.MustSetInt64(rv.Int()).At(k)
		case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8:
			v.MustSetUint64(rv.Uint()).At(k)
		case reflect.Float32, reflect.Float64:
			v.MustSetFloat64(rv.Float()).At(k)
		case reflect.String:
			v.MustSetString(rv.String()).At(k)
		// case reflect.Map:
		// 	if rv.Type().Key().Kind() == reflect.String && rv.Type().Elem().Kind() == reflect.Interface {
		// 		if m, ok := rv.Interface().(M); ok {
		// 			sub := NewObject(m)
		// 			if sub != nil {
		// 				v.Set(sub).At(k)
		// 			}
		// 		}
		// 	}
		default:
			// continue
		}
	}
}

// NewArray returns an empty array-typed jsonvalue object
//
// NewArray 返回一个初始化好的 array 类型的 jsonvalue 值。
func NewArray() *V {
	return newArray(globalPool{})
}

// NewFloat64 returns an initialized num jsonvalue value by float64 type. The format and precision control is the same
// with encoding/json: https://github.com/golang/go/blob/master/src/encoding/json/encode.go#L575
//
// NewFloat64 根据指定的 flout64 类型返回一个初始化好的数字类型的 jsonvalue 值。数字转出来的字符串格式参照 encoding/json 中的逻辑。
func NewFloat64(f float64) *V {
	abs := math.Abs(f)
	format := byte('f')
	if abs < 1e-6 || abs >= 1e21 {
		format = byte('e')
	}

	return newFloat64f(globalPool{}, f, format, -1, 64)
}

// NewFloat64f returns an initialized num jsonvalue value by float64 type. The format and prec parameter are used in
// strconv.FormatFloat(). Only 'f', 'E', 'e', 'G', 'g' formats are supported, other formats will be mapped to 'g'.
//
// NewFloat64f 根据指定的 float64 类型返回一个初始化好的数字类型的 jsonvalue 值。其中参数 format 和 prec 分别用于
// strconv.FormatFloat() 函数. 只有 'f'、'E'、'e'、'G'、'g' 格式是支持的，其他配置均统一映射为 'g'。
func NewFloat64f(f float64, format byte, prec int) *V {
	if err := validateFloat64Format(format); err != nil {
		format = 'g'
	}
	return newFloat64f(globalPool{}, f, format, prec, 64)
}

// NewFloat32 returns an initialized num jsonvalue value by float32 type. The format and precision control is the same
// with encoding/json: https://github.com/golang/go/blob/master/src/encoding/json/encode.go#L575
//
// NewFloat32 根据指定的 float32 类型返回一个初始化好的数字类型的 jsonvalue 值。数字转出来的字符串格式参照 encoding/json 中的逻辑。
func NewFloat32(f float32) *V {
	f64 := float64(f)
	abs := math.Abs(f64)
	format := byte('f')
	if abs < 1e-6 || abs >= 1e21 {
		format = byte('e')
	}

	return newFloat64f(globalPool{}, f64, format, -1, 32)
}

// NewFloat32f returns an initialized num jsonvalue value by float64 type. The format and prec parameter are used in
// strconv.FormatFloat(). Only 'f', 'E', 'e', 'G', 'g' formats are supported, other formats will be mapped to 'g'.
//
// NewFloat32f 根据指定的 float64 类型返回一个初始化好的数字类型的 jsonvalue 值。其中参数 format 和 prec 分别用于
// strconv.FormatFloat() 函数. 只有 'f'、'E'、'e'、'G'、'g' 格式是支持的，其他配置均统一映射为 'g'。
func NewFloat32f(f float32, format byte, prec int) *V {
	if err := validateFloat64Format(format); err != nil {
		format = 'g'
	}
	return newFloat64f(globalPool{}, float64(f), format, prec, 64)
}

// -------- internal functions --------

func new(p pool, t ValueType) *V {
	v := pool.get(p)
	v.valueType = t
	return v
}

func newObject(p pool) *V {
	v := new(p, Object)
	v.children.object = make(map[string]childWithProperty)
	v.children.lowerCaseKeys = nil
	return v
}

func newArray(p pool) *V {
	v := new(p, Array)
	return v
}

func newFloat64f(p pool, f float64, format byte, prec, bitSize int) *V {
	v := new(p, Number)
	// v.num = &num{}
	v.num.negative = f < 0
	v.num.f64 = f
	v.num.i64 = int64(f)
	v.num.u64 = uint64(f)

	if isValidFloat(f) {
		s := strconv.FormatFloat(f, format, prec, bitSize)
		v.srcByte = []byte(s)
	}

	return v
}

func validateFloat64Format(f byte) error {
	switch f {
	case 'f', 'E', 'e', 'G', 'g':
		return nil
	default:
		return fmt.Errorf("unsupported float value in option: %c", f)
	}
}
