//go:build kafka

// The Avro codec depends on the confluent Schema Registry serializer, so it is
// compiled only with the `kafka` build tag. The default (CGO-free) build uses
// JSON; selecting CODEC_PROVIDER=avro there returns a clear error (avro_stub.go).
package codec

import (
	"context"
	"errors"

	"github.com/warmbly/warmbly/internal/infrastructure/kafka"
)

// AvroCodec wraps the existing kafka.Avrov2 Schema Registry serializer so the
// EventBus can hold a transport-agnostic Codec without growing a hard
// dependency on the kafka package. Behavior is intentionally identical to
// calling kafka.Avrov2.Ser.Serialize / Deser.DeserializeInto directly: this
// type adds no framing, no headers, no per-message branching.
//
// The underlying Avrov2 holds a Schema Registry client which manages its own
// HTTP connection pool and is safe for concurrent use.
type AvroCodec struct {
	a *kafka.Avrov2
}

// NewAvro constructs an AvroCodec from Schema Registry credentials. The
// arguments mirror kafka.NewAvrov2Client so call sites that already source
// these from secrets can pass them through unchanged.
func NewAvro(schemaRegistryURL, schemaRegistryKey, schemaRegistrySecret string) (*AvroCodec, error) {
	client, err := kafka.NewAvrov2Client(schemaRegistryURL, schemaRegistryKey, schemaRegistrySecret)
	if err != nil {
		return nil, err
	}
	return &AvroCodec{a: client}, nil
}

// NewAvroFromClient wraps an already-constructed kafka.Avrov2. Useful during
// migration so callers that still hold a *kafka.Avrov2 directly can share the
// same Schema Registry connection rather than opening a second one.
func NewAvroFromClient(client *kafka.Avrov2) *AvroCodec {
	return &AvroCodec{a: client}
}

// Underlying returns the wrapped kafka.Avrov2. Exposed so callers in the
// middle of the transport refactor can still reach the original serializer /
// deserializer (e.g. the kafka.Producer.WithAvrov2 hookup) without a second
// Schema Registry handshake. Will be removed once all call sites go through
// the Codec interface.
func (c *AvroCodec) Underlying() *kafka.Avrov2 { return c.a }

// Name satisfies Codec.
func (c *AvroCodec) Name() string { return "avro" }

// Compile-time interface check (kept with the type, which is kafka-tagged).
var _ Codec = (*AvroCodec)(nil)

// Serialize satisfies Codec. The Schema Registry serializer ignores ctx
// internally, but the parameter is part of the interface so future codecs
// (gRPC-style schema registries, remote codecs) can honour cancellation.
func (c *AvroCodec) Serialize(_ context.Context, topic string, value any) ([]byte, error) {
	if c == nil || c.a == nil || c.a.Ser == nil {
		return nil, errors.New("codec: avro serializer not configured")
	}
	return c.a.Ser.Serialize(topic, value)
}

// Deserialize satisfies Codec. target must be a non-nil pointer to the
// expected message type, matching avrov2.Deserializer.DeserializeInto.
func (c *AvroCodec) Deserialize(_ context.Context, topic string, payload []byte, target any) error {
	if c == nil || c.a == nil || c.a.Deser == nil {
		return errors.New("codec: avro deserializer not configured")
	}
	return c.a.Deser.DeserializeInto(topic, payload, target)
}
