// Package events implements a simple event broadcasting mechanism
// for usage in broadcasting error messages, postbacks etc. various
// channels.
package events

import (
	"bytes"
	"fmt"
	"io"
	"sync"
)

const (
	TypeError = "error"
)

// Event represents a single event in the system.
type Event struct {
	ID       string   `json:"id"`
	Type     string   `json:"type"`
	Message  string   `json:"message"`
	Data     any      `json:"data"`
	Channels []string `json:"-"`
}

type Events struct {
	subs map[string]chan Event
	sync.RWMutex
}

// New returns a new instance of Events.
func New() *Events {
	return &Events{
		subs: make(map[string]chan Event),
	}
}

// Subscribe returns a channel to which the given event `types` are streamed.
// id is the unique identifier for the caller. A caller can only register
// for subscription once.
func (ev *Events) Subscribe(id string) (chan Event, error) {
	ev.Lock()
	defer ev.Unlock()

	if ch, ok := ev.subs[id]; ok {
		return ch, nil
	}

	ch := make(chan Event, 100)
	ev.subs[id] = ch

	return ch, nil
}

// Unsubscribe unsubscribes a subscriber (obviously).
func (ev *Events) Unsubscribe(id string) {
	ev.Lock()
	defer ev.Unlock()
	delete(ev.subs, id)
}

// Publish publishes an event to all subscribers.
func (ev *Events) Publish(e Event) error {
	ev.Lock()
	defer ev.Unlock()

	for _, ch := range ev.subs {
		select {
		case ch <- e:
		default:
			return fmt.Errorf("event queue full for type: %s", e.Type)
		}
	}

	return nil
}

// This implements an io.Writer specifically for receiving error messages
// mirrored (io.MultiWriter) from error log writing.
type wri struct {
	ev *Events
}

func (w *wri) Write(b []byte) (n int, err error) {
	// Only broadcast error messages.
	if !bytes.Contains(b, []byte("error")) {
		return 0, nil
	}

	w.ev.Publish(Event{
		Type:    TypeError,
		Message: string(b),
	})

	return len(b), nil
}

func (ev *Events) ErrWriter() io.Writer {
	return &wri{ev: ev}
}
