package encoding

import (
	"fmt"

	"google.golang.org/grpc/encoding"
	"google.golang.org/protobuf/proto"
)

// ProtoCodec is easyproto codec used for code generated by protogen.
// It is binary-level compatible with the standard proto codec, thus uses the same name.
type ProtoCodec struct{}

// ProtoMarshaler is an interface accepted by ProtoCodec.Marshal.
type ProtoMarshaler interface {
	MarshalProtobuf([]byte) []byte
}

// ProtoUnmarshaler is an interface accepted by ProtoCodec.Unmarshal.
type ProtoUnmarshaler interface {
	UnmarshalProtobuf([]byte) error
}

var _ encoding.Codec = ProtoCodec{}

func init() {
	encoding.RegisterCodec(ProtoCodec{})
}

// Name implements the encoding.Codec interface.
func (ProtoCodec) Name() string { return "proto" }

// Marshal implements the encoding.Codec interface.
func (ProtoCodec) Marshal(v any) ([]byte, error) {
	switch v := v.(type) {
	case ProtoMarshaler:
		return v.MarshalProtobuf(nil), nil
	default:
		if v := messageV2Of(v); v != nil {
			return proto.Marshal(v)
		}
		return nil, fmt.Errorf("failed to marshal, message is %T, want proto.Message", v)
	}
}

// Unmarshal implements the encoding.Codec interface.
func (ProtoCodec) Unmarshal(data []byte, v any) error {
	switch v := v.(type) {
	case ProtoUnmarshaler:
		return v.UnmarshalProtobuf(data)
	default:
		if v := messageV2Of(v); v != nil {
			return proto.Unmarshal(data, v)
		}
		return fmt.Errorf("failed to unmarshal, message is %T, want proto.Message", v)
	}
}