package gjson

import (
	"fmt"

	json "github.com/goccy/go-json"
	glua "github.com/yuin/gopher-lua"
)

// decodeJSONValue converts a JSON string to a Lua value.
func decodeJSONValue(L *glua.LState, data []byte, config *Config, nullValue *glua.LUserData, depth int) (glua.LValue, error) {
	// Parse JSON using go-json
	var result interface{}
	if err := json.Unmarshal(data, &result); err != nil {
		return glua.LNil, fmt.Errorf("JSON parse error: %w", err)
	}

	return convertToLua(L, result, config, nullValue, depth)
}

// convertToLua converts a Go value (from JSON) to a Lua value.
func convertToLua(L *glua.LState, value interface{}, config *Config, nullValue *glua.LUserData, depth int) (glua.LValue, error) {
	if depth > config.decodeMaxDepth {
		return glua.LNil, fmt.Errorf("maximum nesting depth %d exceeded", config.decodeMaxDepth)
	}

	if value == nil {
		// JSON null -> gjson.null
		return nullValue, nil
	}

	switch v := value.(type) {
	case bool:
		return glua.LBool(v), nil

	case float64:
		// Check if it's actually an integer
		if v == float64(int64(v)) {
			return glua.LNumber(int64(v)), nil
		}
		return glua.LNumber(v), nil

	case string:
		return glua.LString(v), nil

	case []interface{}:
		return convertArrayToLua(L, v, config, nullValue, depth)

	case map[string]interface{}:
		return convertObjectToLua(L, v, config, nullValue, depth)

	case json.Number:
		// Handle UseNumber case - parse the number string
		num, err := parseJSONNumber(string(v))
		if err != nil {
			return glua.LNil, err
		}
		return num, nil

	default:
		return glua.LNil, fmt.Errorf("unknown JSON type: %T", value)
	}
}

// convertArrayToLua converts a JSON array to a Lua table with integer keys (1-based).
func convertArrayToLua(L *glua.LState, arr []interface{}, config *Config, nullValue *glua.LUserData, depth int) (glua.LValue, error) {
	tbl := L.NewTable()

	for i, item := range arr {
		luaVal, err := convertToLua(L, item, config, nullValue, depth+1)
		if err != nil {
			return glua.LNil, err
		}
		// Lua arrays are 1-based
		tbl.RawSetInt(i+1, luaVal)
	}

	return tbl, nil
}

// convertObjectToLua converts a JSON object to a Lua table with string keys.
func convertObjectToLua(L *glua.LState, obj map[string]interface{}, config *Config, nullValue *glua.LUserData, depth int) (glua.LValue, error) {
	tbl := L.NewTable()

	for key, val := range obj {
		luaVal, err := convertToLua(L, val, config, nullValue, depth+1)
		if err != nil {
			return glua.LNil, err
		}
		tbl.RawSetString(key, luaVal)
	}

	return tbl, nil
}

// parseJSONNumber parses a JSON number string to a Lua number.
func parseJSONNumber(s string) (glua.LNumber, error) {
	// Try parsing as integer first
	var intVal int64
	if _, err := fmt.Sscanf(s, "%d", &intVal); err == nil {
		return glua.LNumber(intVal), nil
	}

	// Parse as float
	var floatVal float64
	if _, err := fmt.Sscanf(s, "%f", &floatVal); err == nil {
		return glua.LNumber(floatVal), nil
	}

	return glua.LNumber(0), fmt.Errorf("invalid number format: %s", s)
}
