package events

import (
	"context"
	"sync"
	"time"
)

// EventType represents the type of event
type EventType string

const (
	// WhatsApp connection events
	EventWhatsAppConnected    EventType = "whatsapp.connected"
	EventWhatsAppDisconnected EventType = "whatsapp.disconnected"
	EventWhatsAppPairSuccess  EventType = "whatsapp.pair.success"
	EventWhatsAppPairFailed   EventType = "whatsapp.pair.failed"
	EventWhatsAppQRCode       EventType = "whatsapp.qr.code"
	EventWhatsAppQRTimeout    EventType = "whatsapp.qr.timeout"
	EventWhatsAppQRError      EventType = "whatsapp.qr.error"
	EventWhatsAppPairEvent    EventType = "whatsapp.pair.event"

	// WhatsApp message events
	EventMessageReceived  EventType = "message.received"
	EventMessageSent      EventType = "message.sent"
	EventMessageFailed    EventType = "message.failed"
	EventMessageDelivered EventType = "message.delivered"
	EventMessageRead      EventType = "message.read"

	// Template events
	EventTemplateStatusUpdate EventType = "template.status_update"

	// Hook events
	EventHookTriggered EventType = "hook.triggered"
	EventHookSuccess   EventType = "hook.success"
	EventHookFailed    EventType = "hook.failed"
)

// Event represents an event in the system
type Event struct {
	Type      EventType       `json:"type"`
	Timestamp time.Time       `json:"timestamp"`
	Data      map[string]any  `json:"data"`
	Context   context.Context `json:"-"`
}

// Subscriber is a function that handles events
type Subscriber func(event Event)

// EventBus manages event publishing and subscription
type EventBus struct {
	subscribers map[EventType][]Subscriber
	mu          sync.RWMutex
}

// NewEventBus creates a new event bus
func NewEventBus() *EventBus {
	return &EventBus{
		subscribers: make(map[EventType][]Subscriber),
	}
}

// Subscribe registers a subscriber for a specific event type
func (eb *EventBus) Subscribe(eventType EventType, subscriber Subscriber) {
	eb.mu.Lock()
	defer eb.mu.Unlock()

	if eb.subscribers[eventType] == nil {
		eb.subscribers[eventType] = make([]Subscriber, 0)
	}

	eb.subscribers[eventType] = append(eb.subscribers[eventType], subscriber)
}

// SubscribeAll registers a subscriber for all event types
func (eb *EventBus) SubscribeAll(subscriber Subscriber) {
	eb.mu.Lock()
	defer eb.mu.Unlock()

	allTypes := []EventType{
		EventWhatsAppConnected,
		EventWhatsAppDisconnected,
		EventWhatsAppPairSuccess,
		EventWhatsAppPairFailed,
		EventWhatsAppQRCode,
		EventWhatsAppQRTimeout,
		EventWhatsAppQRError,
		EventWhatsAppPairEvent,
		EventMessageReceived,
		EventMessageSent,
		EventMessageFailed,
		EventMessageDelivered,
		EventMessageRead,
		EventTemplateStatusUpdate,
		EventHookTriggered,
		EventHookSuccess,
		EventHookFailed,
	}

	for _, eventType := range allTypes {
		if eb.subscribers[eventType] == nil {
			eb.subscribers[eventType] = make([]Subscriber, 0)
		}
		eb.subscribers[eventType] = append(eb.subscribers[eventType], subscriber)
	}
}

// Publish publishes an event to all subscribers asynchronously
func (eb *EventBus) Publish(event Event) {
	eb.mu.RLock()
	subscribers := make([]Subscriber, len(eb.subscribers[event.Type]))
	copy(subscribers, eb.subscribers[event.Type])
	eb.mu.RUnlock()

	// Use event context if available, otherwise background
	ctx := event.Context
	if ctx == nil {
		ctx = context.Background()
	}

	for _, subscriber := range subscribers {
		go func(sub Subscriber, evt Event) {
			// Check if context is already cancelled
			select {
			case <-ctx.Done():
				return
			default:
				sub(evt)
			}
		}(subscriber, event)
	}
}

// PublishSync publishes an event to all subscribers synchronously
func (eb *EventBus) PublishSync(event Event) {
	eb.mu.RLock()
	subscribers := make([]Subscriber, len(eb.subscribers[event.Type]))
	copy(subscribers, eb.subscribers[event.Type])
	eb.mu.RUnlock()

	// Use event context if available, otherwise background
	ctx := event.Context
	if ctx == nil {
		ctx = context.Background()
	}

	for _, subscriber := range subscribers {
		// Check if context is already cancelled
		select {
		case <-ctx.Done():
			return
		default:
			subscriber(event)
		}
	}
}

// NewEvent creates a new event with the current timestamp and context
func NewEvent(ctx context.Context, eventType EventType, data map[string]any) Event {
	if ctx == nil {
		ctx = context.Background()
	}
	return Event{
		Type:      eventType,
		Timestamp: time.Now(),
		Data:      data,
		Context:   ctx,
	}
}
