package events

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"
)

// EventBus définit l'interface pour le système d'événements
type EventBus interface {
	Publish(ctx context.Context, topic string, payload interface{}) error
	Subscribe(ctx context.Context, topic string, handler func(payload []byte) error)
}

// RedisEventBus implémente EventBus avec Redis Pub/Sub
type RedisEventBus struct {
	client *redis.Client
	logger *zap.Logger
}

// NewRedisEventBus crée une nouvelle instance de RedisEventBus
func NewRedisEventBus(client *redis.Client, logger *zap.Logger) *RedisEventBus {
	return &RedisEventBus{
		client: client,
		logger: logger,
	}
}

// Publish publie un événement sur un topic
func (b *RedisEventBus) Publish(ctx context.Context, topic string, payload interface{}) error {
	data, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("failed to marshal payload: %w", err)
	}

	if err := b.client.Publish(ctx, topic, data).Err(); err != nil {
		b.logger.Error("Failed to publish event", zap.String("topic", topic), zap.Error(err))
		return err
	}

	b.logger.Debug("Event published", zap.String("topic", topic))
	return nil
}

// Subscribe souscrit à un topic et exécute le handler pour chaque message
// Note: Cette méthode est bloquante ou doit être lancée dans une goroutine
func (b *RedisEventBus) Subscribe(ctx context.Context, topic string, handler func(payload []byte) error) {
	pubsub := b.client.Subscribe(ctx, topic)
	defer pubsub.Close()

	ch := pubsub.Channel()

	b.logger.Info("Subscribed to topic", zap.String("topic", topic))

	for msg := range ch {
		if err := handler([]byte(msg.Payload)); err != nil {
			b.logger.Error("Error handling event",
				zap.String("topic", topic),
				zap.Error(err))
		}
	}
}
