package crypto

import (
	"errors"
	"fmt"
	"sync"
)

// Sentinel errors for registry operations.
var (
	ErrUnknownScheme   = errors.New("unknown KEM scheme")
	ErrDuplicateScheme = errors.New("duplicate KEM scheme registration")
)

// KEMFactory is a constructor function that returns a fresh KEM instance.
type KEMFactory func() KEM

// Registry is a concurrent-safe map of KEM scheme IDs to their factories.
type Registry interface {
	Register(
		schemeID uint16,
		factory KEMFactory,
	) error
	Lookup(
		schemeID uint16,
	) (
		KEMFactory,
		error,
	)
}

// registry is the private implementation of Registry.
type registry struct {
	mutex     sync.RWMutex
	factories map[uint16]KEMFactory
}

// NewRegistry creates a new empty Registry.
func NewRegistry() Registry {
	return &registry{
		factories: make(map[uint16]KEMFactory),
	}
}

// Register adds a KEMFactory for the given schemeID.
// Returns ErrDuplicateScheme if the schemeID is already registered.
func (r *registry) Register(schemeID uint16, factory KEMFactory) error {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	if _, exists := r.factories[schemeID]; exists {
		return fmt.Errorf(
			"scheme 0x%04x: %w",
			schemeID,
			ErrDuplicateScheme,
		)
	}

	r.factories[schemeID] = factory
	return nil
}

// Lookup retrieves the KEMFactory for the given schemeID.
// Returns ErrUnknownScheme if the schemeID is not registered.
func (r *registry) Lookup(
	schemeID uint16,
) (
	KEMFactory,
	error,
) {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	factory, exists := r.factories[schemeID]
	if !exists {
		return nil, fmt.Errorf(
			"scheme 0x%04x: %w",
			schemeID,
			ErrUnknownScheme,
		)
	}

	return factory, nil
}
