package listeners

import (
	"crypto/tls"
	"sync"

	"log/slog"
)

// Config contains configuration values for a listener.
type Config struct {
	// TLSConfig is a tls.Config configuration to be used with the listener.
	// See examples folder for basic and mutual-tls use.
	TLSConfig *tls.Config
}

// Listener is an interface for network listeners. A network listener listens
// for incoming client connections and adds them to the server.
type Listener interface {
	Init(*slog.Logger) error // open the network address
	Serve()                  // starting actively listening for new connections
	ID() string              // return the id of the listener
	Address() string         // the address of the listener
	Protocol() string        // the protocol in use by the listener
	Close()                  // stop and close the listener
}

// Listeners contains the network listeners for the broker.
type Listeners struct {
	ClientsWg sync.WaitGroup      // a waitgroup that waits for all clients in all listeners to finish.
	internal  map[string]Listener // a map of active listeners.
	sync.RWMutex
}

// New returns a new instance of Listeners.
func New() *Listeners {
	return &Listeners{
		internal: map[string]Listener{},
	}
}

// Add adds a new listener to the listeners map, keyed on id.
func (l *Listeners) Add(val Listener) {
	l.Lock()
	defer l.Unlock()
	l.internal[val.ID()] = val
}

// Get returns the value of a listener if it exists.
func (l *Listeners) Get(id string) (Listener, bool) {
	l.RLock()
	defer l.RUnlock()
	val, ok := l.internal[id]
	return val, ok
}

// Len returns the length of the listeners map.
func (l *Listeners) Len() int {
	l.RLock()
	defer l.RUnlock()
	return len(l.internal)
}

// Delete removes a listener from the internal map.
func (l *Listeners) Delete(id string) {
	l.Lock()
	defer l.Unlock()
	delete(l.internal, id)
}

// Serve starts a listener serving from the internal map.
func (l *Listeners) Serve(id string) {
	l.RLock()
	defer l.RUnlock()
	listener := l.internal[id]

	go func() {
		listener.Serve()
	}()
}

// ServeAll starts all listeners serving from the internal map.
func (l *Listeners) ServeAll() {
	l.RLock()
	i := 0
	ids := make([]string, len(l.internal))
	for id := range l.internal {
		ids[i] = id
		i++
	}
	l.RUnlock()

	for _, id := range ids {
		l.Serve(id)
	}
}

// Close stops a listener from the internal map.
func (l *Listeners) Close(id string) {
	l.RLock()
	defer l.RUnlock()
	if listener, ok := l.internal[id]; ok {
		listener.Close()
	}
}

// CloseAll iterates and closes all registered listeners.
func (l *Listeners) CloseAll() {
	l.RLock()
	i := 0
	ids := make([]string, len(l.internal))
	for id := range l.internal {
		ids[i] = id
		i++
	}
	l.RUnlock()

	for _, id := range ids {
		l.Close(id)
	}
	l.ClientsWg.Wait()
}
