package handlers

import (
	"encoding/json"
	"fmt"

	"go.starlark.net/starlark"
)

// jsonToStarlark converts an arbitrary Go value (typically from JSON unmarshal)
// to a Starlark value for passing into extension scripts.
func jsonToStarlark(v any) starlark.Value {
	switch val := v.(type) {
	case nil:
		return starlark.None
	case bool:
		return starlark.Bool(val)
	case float64:
		if val == float64(int64(val)) {
			return starlark.MakeInt64(int64(val))
		}
		return starlark.Float(val)
	case string:
		return starlark.String(val)
	case []any:
		elems := make([]starlark.Value, len(val))
		for i, e := range val {
			elems[i] = jsonToStarlark(e)
		}
		return starlark.NewList(elems)
	case map[string]any:
		d := starlark.NewDict(len(val))
		for k, v := range val {
			_ = d.SetKey(starlark.String(k), jsonToStarlark(v))
		}
		return d
	default:
		return starlark.String(fmt.Sprintf("%v", val))
	}
}

// starlarkValueToGo converts a Starlark value back to a Go value suitable
// for JSON serialization.
func starlarkValueToGo(v starlark.Value) any {
	switch val := v.(type) {
	case starlark.NoneType:
		return nil
	case starlark.Bool:
		return bool(val)
	case starlark.Int:
		i, _ := val.Int64()
		return i
	case starlark.Float:
		return float64(val)
	case starlark.String:
		return string(val)
	case *starlark.List:
		out := make([]any, val.Len())
		for i := 0; i < val.Len(); i++ {
			out[i] = starlarkValueToGo(val.Index(i))
		}
		return out
	case *starlark.Dict:
		out := make(map[string]any)
		for _, item := range val.Items() {
			k, _ := item[0].(starlark.String)
			out[string(k)] = starlarkValueToGo(item[1])
		}
		return out
	default:
		return v.String()
	}
}

// ParseSchemaVersion extracts the schema_version from a package manifest.
func ParseSchemaVersion(manifest any) int {
	switch m := manifest.(type) {
	case map[string]any:
		if v, ok := m["schema_version"].(float64); ok {
			return int(v)
		}
	case json.RawMessage:
		var s struct {
			SchemaVersion int `json:"schema_version"`
		}
		if err := json.Unmarshal(m, &s); err == nil {
			return s.SchemaVersion
		}
	}
	return 0
}

// RunSchemaMigrations runs extension schema migrations between versions.
// Stub — full implementation will be added when extension DB schemas are stabilized.
func RunSchemaMigrations(ctx any, sandbox any, stores any, db any, isPostgres bool, packageID string, manifest any, fromVersion, toVersion int) error {
	// TODO: implement extension schema migrations
	return nil
}
