package object

import (
	"encoding/json"
	"fmt"

	"github.com/jinzhu/copier"
)

// Pointer returns a pointer to v.
func Pointer[T any](v T) *T {
	return &v
}

// Deref returns the value pointed to by v, or def when v is nil.
func Deref[T any](v *T, def T) T {
	if v == nil {
		return def
	}
	return *v
}

// OrDefault returns def when v is the zero value of T.
func OrDefault[T comparable](v T, def T) T {
	var zero T
	if v == zero {
		return def
	}
	return v
}

// DerefAny returns v asserted as T, or def when v has a different type.
func DerefAny[T any](v any, def T) T {
	t, ok := v.(T)
	if !ok {
		return def
	}
	return t
}

// DeepCopy returns a deep copy of the value pointed to by v.
func DeepCopy[T any](v *T) (*T, error) {
	if v == nil {
		return nil, fmt.Errorf("nil pointer")
	}

	var n T
	err := copier.CopyWithOption(&n, v, copier.Option{DeepCopy: true})
	if err != nil {
		return nil, err
	}
	return &n, nil
}

// StructToMap converts v to a map by marshaling and unmarshaling it as JSON.
func StructToMap(v any) (map[string]any, error) {
	b, err := json.Marshal(v)
	if err != nil {
		return nil, err
	}

	var m map[string]any
	if err := json.Unmarshal(b, &m); err != nil {
		return nil, err
	}
	return m, nil
}

// MapToStruct fills v from m by marshaling and unmarshaling it as JSON.
func MapToStruct(m map[string]any, v any) error {
	b, err := json.Marshal(m)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(b, v); err != nil {
		return err
	}
	return nil
}

// MarshalWithoutError returns the JSON encoding of v, or the marshal error text as bytes.
func MarshalWithoutError[T any](v T) []byte {
	data, err := json.Marshal(v)
	if err != nil {
		return []byte(err.Error())
	}

	return data
}
