package workers

import (
	"log/slog"
	"sync"
)

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

// EstablishFn is a callback function for establishing new clients.
type EstablishFn func(id string) error

// CloseFn is a callback function for closing all listener clients.
type CloseFn func(id string)

// Worker .
type Worker interface {
	ID() string              // returns ID in string format
	Type() string            // returns the type of the worker
	Init(*slog.Logger) error //
	OneTime() error          //
	Serve()                  // starting actively listening for new connections
	Close()                  // stop and close the worker
}

// Workers contains the network workers for the app.
type Workers struct {
	ClientsWg sync.WaitGroup    // a waitgroup that waits for all clients in all workers to finish.
	internal  map[string]Worker // a map of active workers.
	sync.RWMutex
}

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

// Add adds a new worker to the workers map, keyed on id.
func (w *Workers) Add(val Worker) {
	w.Lock()
	defer w.Unlock()
	w.internal[val.ID()] = val
}

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

// Len returns the length of the workers map.
func (w *Workers) Len() int {
	w.RLock()
	defer w.RUnlock()
	return len(w.internal)
}

// Delete removes a worker from the internal map.
func (w *Workers) Delete(id string) {
	w.Lock()
	defer w.Unlock()
	delete(w.internal, id)
}

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

	go func() {
		worker.Serve()
		worker.OneTime()
	}()
}

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

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

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

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

	for _, id := range ids {
		w.Close(id)
	}
}
