package streaming

import (
	"context"

	"github.com/nexuscrm/kernel/pkg/ports"
	domainEvents "github.com/nexuscrm/shared/pkg/events"
	"github.com/rs/zerolog/log"
)

// WebSocketBroadcaster interface for pushing events to WebSocket clients
type WebSocketBroadcaster interface {
	Broadcast(channel string, data interface{})
}

// EventChannelMapping binds a platform event type to a registered streaming channel.
// Filter, when non-nil, is consulted once per event before broadcast:
// returning false drops the event from every broadcaster while leaving other
// event-bus subscribers (audit, automation, …) untouched. The mechanism is
// product-blind — which payloads a channel carries is decided where the
// mapping is registered.
type EventChannelMapping struct {
	EventType domainEvents.EventType
	Channel   string
	Filter    func(ctx context.Context, payload interface{}) bool
}

// EventBridge subscribes to EventBus events and forwards them to registered broadcasters
type EventBridge struct {
	eventBus     ports.EventPublisher
	broadcasters []WebSocketBroadcaster
	mappings     []EventChannelMapping
	unsubFuncs   []func()
}

// NewEventBridge creates a bridge between EventBus and broadcasters
func NewEventBridge(eventBus ports.EventPublisher, broadcasters ...WebSocketBroadcaster) *EventBridge {
	return &EventBridge{
		eventBus:     eventBus,
		broadcasters: broadcasters,
		unsubFuncs:   make([]func(), 0),
	}
}

// RegisterEventChannelMappings adds product-owned event/channel mappings.
func (b *EventBridge) RegisterEventChannelMappings(mappings ...EventChannelMapping) {
	b.mappings = append(b.mappings, mappings...)
}

// EventChannelMappings returns the registered event/channel mappings.
func (b *EventBridge) EventChannelMappings() []EventChannelMapping {
	return append([]EventChannelMapping(nil), b.mappings...)
}

// Start subscribes to relevant events and bridges them to WebSocket
func (b *EventBridge) Start() {
	for _, mapping := range b.mappings {
		if mapping.EventType == "" || mapping.Channel == "" {
			continue
		}
		b.subscribeAndBridge(mapping)
	}

	log.Info().Msg("EventBridge started - bridging events to WebSocket")
}

// subscribeAndBridge subscribes to an event type and broadcasts to a channel
func (b *EventBridge) subscribeAndBridge(mapping EventChannelMapping) {
	unsub := b.eventBus.Subscribe(mapping.EventType, func(ctx context.Context, payload interface{}) error {
		if mapping.Filter != nil && !mapping.Filter(ctx, payload) {
			return nil
		}
		log.Debug().
			Str("event_type", string(mapping.EventType)).
			Str("ws_channel", mapping.Channel).
			Int("broadcasters", len(b.broadcasters)).
			Msg("Bridging event to broadcasters")

		for _, broadcaster := range b.broadcasters {
			broadcaster.Broadcast(mapping.Channel, payload)
		}
		return nil
	})
	b.unsubFuncs = append(b.unsubFuncs, unsub)
}

// Stop unsubscribes from all events
func (b *EventBridge) Stop() {
	for _, unsub := range b.unsubFuncs {
		unsub()
	}
	b.unsubFuncs = nil
	log.Info().Msg("EventBridge stopped")
}
