package protosocket

import (
	"context"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"sync"
)

type HandlerFunc func(context.Context, Envelope) Envelope

type Dispatcher struct {
	mu       sync.RWMutex
	handlers map[string]HandlerFunc
}

func NewDispatcher() *Dispatcher {
	return &Dispatcher{
		handlers: make(map[string]HandlerFunc),
	}
}

func (d *Dispatcher) Register(action string, handler HandlerFunc) {
	d.mu.Lock()
	defer d.mu.Unlock()
	d.handlers[action] = handler
}

func (d *Dispatcher) Dispatch(ctx context.Context, env Envelope) Envelope {
	d.mu.RLock()
	handler, ok := d.handlers[env.Action]
	d.mu.RUnlock()

	if !ok {
		return Envelope{
			ProtocolVersion: ProtocolVersion,
			ID:              generateID(),
			CorrelationID:   env.ID,
			Type:            "error",
			Channel:         env.Channel,
			Action:          env.Action,
			Error: &EnvelopeError{
				Code:    "UNSUPPORTED_ACTION",
				Message: fmt.Sprintf("unsupported action: %s", env.Action),
			},
		}
	}

	return handler(ctx, env)
}

func generateID() string {
	b := make([]byte, 16)
	if _, err := rand.Read(b); err != nil {
		return "error-id-fallback"
	}
	return hex.EncodeToString(b)
}
