// voice/errors.go — wire error codes + sentinel rehydration.
package voice

import (
	"encoding/json"
	"errors"
	"fmt"
)

// Sentinel errors. Mirrored 1:1 to wire codes below; the client rehydrates a
// wire RpcError into one of these so callers can use errors.Is the same way
// in-process and over-the-wire.
var (
	ErrUnknownMethod = errors.New("voice: unknown method")
	ErrBadParams     = errors.New("voice: bad params")
	ErrForbidden     = errors.New("voice: forbidden")
	ErrNoSession     = errors.New("voice: no live client session") // voicesink unable to push
)

// Sentinel wire codes. Stable; do not rename.
const (
	codeUnknownMethod = "unknown_method"
	codeBadParams     = "bad_params"
	codeForbidden     = "forbidden"
	codeInternal      = "internal"
)

// codeOf maps a server-side sentinel to its wire code. Unknown ⇒
// codeInternal; the server logs the real text and the client sees a
// generic internal code (no authority-bearing text exposure).
func codeOf(err error) string {
	switch {
	case err == nil:
		return ""
	case errors.Is(err, ErrUnknownMethod):
		return codeUnknownMethod
	case errors.Is(err, ErrBadParams):
		return codeBadParams
	case errors.Is(err, ErrForbidden):
		return codeForbidden
	default:
		return codeInternal
	}
}

func rpcErr(err error) *RpcError {
	c := codeOf(err)
	if c == codeInternal || c == codeBadParams {
		return &RpcError{Code: c, Message: err.Error()}
	}
	return &RpcError{Code: c}
}

// hydrate rehydrates a wire RpcError into a package sentinel.
func hydrate(e *RpcError) error {
	switch e.Code {
	case codeUnknownMethod:
		return fmt.Errorf("%w: %s", ErrUnknownMethod, e.Message)
	case codeBadParams:
		return fmt.Errorf("%w: %s", ErrBadParams, e.Message)
	case codeForbidden:
		return ErrForbidden
	default:
		if e.Message != "" {
			return fmt.Errorf("voice: %s: %s", e.Code, e.Message)
		}
		return fmt.Errorf("voice: %s", e.Code)
	}
}

// json helpers kept local so call sites read clean.
func jsonMarshal(v any) ([]byte, error)   { return json.Marshal(v) }
func jsonUnmarshal(b []byte, v any) error { return json.Unmarshal(b, v) }
