// Package mq exposes the broker-agnostic messaging seam used by services.
package mq

import (
	"context"
	"encoding/json"
	"errors"
	"time"
)

var ErrUnavailable = errors.New("message broker unavailable")

const (
	AtMostOnce  byte = 0
	AtLeastOnce byte = 1
	ExactlyOnce byte = 2
)

type Message struct {
	Topic   string
	Payload []byte
	QoS     byte
	Retain  bool
}

func (m Message) DecodeJSON(target any) error { return json.Unmarshal(m.Payload, target) }

type Handler func(context.Context, Message)

type Client interface {
	Publish(context.Context, string, []byte, byte, bool) error
	Subscribe(context.Context, string, byte, Handler) error
	Unsubscribe(context.Context, ...string) error
	Connected() bool
	Close() error
}

// Registry exposes named broker clients while preserving Client as the
// default EMQX/MQTT boundary for existing modules.
type Registry interface {
	Client(provider string) Client
	PublishTo(context.Context, string, string, []byte, byte, bool) error
	SubscribeTo(context.Context, string, string, byte, Handler) error
	UnsubscribeFrom(context.Context, string, ...string) error
	ConnectedTo(provider string) bool
}

type Config struct {
	Enabled        bool
	Broker         string
	ClientID       string
	Username       string
	Password       string
	KeepAlive      time.Duration
	CleanSession   bool
	ConnectTimeout time.Duration
}

func PublishJSON(ctx context.Context, client Client, topic string, value any, qos byte, retain bool) error {
	if client == nil {
		return ErrUnavailable
	}
	payload, err := json.Marshal(value)
	if err != nil {
		return err
	}
	return client.Publish(ctx, topic, payload, qos, retain)
}
