package params

import (
	"fmt"

	"google.golang.org/protobuf/types/known/structpb"
)

// Reader provides typed helpers around a Struct param payload.
type Reader struct {
	raw map[string]interface{}
}

// New builds a Reader for the provided struct, tolerating nil inputs.
func New(params *structpb.Struct) Reader {
	if params == nil {
		return Reader{}
	}
	return Reader{raw: params.AsMap()}
}

// Value returns the raw value and whether it was present.
func (r Reader) Value(key string) (interface{}, bool) {
	if r.raw == nil {
		return nil, false
	}
	value, ok := r.raw[key]
	return value, ok
}

// String returns the string value for a key, or "" if missing/not a string.
func (r Reader) String(key string) string {
	value, ok := r.Value(key)
	if !ok {
		return ""
	}
	switch v := value.(type) {
	case string:
		return v
	case fmt.Stringer:
		return v.String()
	default:
		return ""
	}
}

// Bool returns the bool value for a key, or false when missing/not a bool.
func (r Reader) Bool(key string) bool {
	value, ok := r.Value(key)
	if !ok {
		return false
	}
	switch v := value.(type) {
	case bool:
		return v
	default:
		return false
	}
}

// Int64 returns the int64 value for a key with a presence flag.
func (r Reader) Int64(key string) (int64, bool) {
	value, ok := r.Value(key)
	if !ok {
		return 0, false
	}
	switch v := value.(type) {
	case int64:
		return v, true
	case int32:
		return int64(v), true
	case int:
		return int64(v), true
	case float64:
		return int64(v), true
	case float32:
		return int64(v), true
	default:
		return 0, false
	}
}

// Float64 returns the float64 value for a key with a presence flag.
func (r Reader) Float64(key string) (float64, bool) {
	value, ok := r.Value(key)
	if !ok {
		return 0, false
	}
	switch v := value.(type) {
	case float64:
		return v, true
	case float32:
		return float64(v), true
	case int:
		return float64(v), true
	case int64:
		return float64(v), true
	default:
		return 0, false
	}
}

// Map returns a nested object value as a map.
func (r Reader) Map(key string) map[string]interface{} {
	value, ok := r.Value(key)
	if !ok {
		return nil
	}
	if m, ok := value.(map[string]interface{}); ok {
		return m
	}
	return nil
}

// List returns a list value as a slice.
func (r Reader) List(key string) []interface{} {
	value, ok := r.Value(key)
	if !ok {
		return nil
	}
	if list, ok := value.([]interface{}); ok {
		return list
	}
	return nil
}

// StringMap converts a nested map into a string map.
func (r Reader) StringMap(key string) map[string]string {
	raw := r.Map(key)
	if len(raw) == 0 {
		return nil
	}
	out := make(map[string]string, len(raw))
	for k, v := range raw {
		out[k] = fmt.Sprint(v)
	}
	return out
}
