package queue

import (
	"context"
	"errors"
	"log/slog"
)

type ProducerMetrics struct {
	published         int64
	publishFailures   int64
	secondaryFailures int64
}

type EmailEnqueuer struct {
	serializer MessageSerializer
	producer   QueueProducer
	logger     *slog.Logger
}

func NewEmailEnqueuer(serializer MessageSerializer, producer QueueProducer, logger *slog.Logger) *EmailEnqueuer {
	if logger == nil {
		logger = slog.Default()
	}
	return &EmailEnqueuer{
		serializer: serializer,
		producer:   producer,
		logger:     logger,
	}
}

func (e *EmailEnqueuer) Enqueue(ctx context.Context, job EmailJob) error {
	message, err := e.serializer.Serialize(ctx, job)
	if err != nil {
		return err
	}
	if err := e.producer.Publish(ctx, *message); err != nil {
		e.logger.Error("queue publish failed",
			slog.String("backend", e.producer.Backend()),
			slog.String("message_id", message.ID),
			slog.String("job_type", job.NormalizedType()),
			slog.String("recipient_hash", job.RecipientHash()),
			slog.String("correlation_id", message.Attributes["correlation_id"]),
			slog.Any("error", err),
		)
		return err
	}
	e.logger.Info("queue published",
		slog.String("backend", e.producer.Backend()),
		slog.String("message_id", message.ID),
		slog.String("job_type", job.NormalizedType()),
		slog.String("recipient_hash", job.RecipientHash()),
		slog.String("correlation_id", message.Attributes["correlation_id"]),
	)
	return nil
}

type DualProducer struct {
	primary           QueueProducer
	secondary         QueueProducer
	secondaryRequired bool
	logger            *slog.Logger
}

func NewDualProducer(primary, secondary QueueProducer, secondaryRequired bool, logger *slog.Logger) *DualProducer {
	if logger == nil {
		logger = slog.Default()
	}
	return &DualProducer{
		primary:           primary,
		secondary:         secondary,
		secondaryRequired: secondaryRequired,
		logger:            logger,
	}
}

func (p *DualProducer) Backend() string {
	return "dual"
}

func (p *DualProducer) Publish(ctx context.Context, message OutboundMessage) error {
	if p.primary == nil {
		return errors.New("primary producer is required")
	}
	if err := p.primary.Publish(ctx, message); err != nil {
		return err
	}
	if p.secondary == nil {
		return nil
	}
	if err := p.secondary.Publish(ctx, message); err != nil {
		p.logger.Error("queue secondary publish failed",
			slog.String("primary_backend", p.primary.Backend()),
			slog.String("secondary_backend", p.secondary.Backend()),
			slog.String("message_id", message.ID),
			slog.Any("error", err),
		)
		if p.secondaryRequired {
			return err
		}
	}
	return nil
}

type DualEmailJobProducer struct {
	primary           EmailJobProducer
	secondary         EmailJobProducer
	secondaryRequired bool
	logger            *slog.Logger
}

func NewDualEmailJobProducer(primary, secondary EmailJobProducer, secondaryRequired bool, logger *slog.Logger) *DualEmailJobProducer {
	if logger == nil {
		logger = slog.Default()
	}
	return &DualEmailJobProducer{
		primary:           primary,
		secondary:         secondary,
		secondaryRequired: secondaryRequired,
		logger:            logger,
	}
}

func (p *DualEmailJobProducer) Enqueue(ctx context.Context, job EmailJob) error {
	if p.primary == nil {
		return errors.New("primary email producer is required")
	}
	if err := p.primary.Enqueue(ctx, job); err != nil {
		return err
	}
	if p.secondary == nil {
		return nil
	}
	if err := p.secondary.Enqueue(ctx, job); err != nil {
		p.logger.Error("queue secondary enqueue failed",
			slog.String("job_type", job.NormalizedType()),
			slog.String("recipient_hash", job.RecipientHash()),
			slog.Any("error", err),
		)
		if p.secondaryRequired {
			return err
		}
	}
	return nil
}
