// Package trackers is the registry of concrete tracker adapters. Each
// adapter implements the scrape.Adapter interface and registers a factory
// (via init() and Register()) keyed by the `type:` string used in
// config/trackers.yaml.
//
// The Adapter contract itself lives in internal/scrape so that the scheduler
// can use it without an import cycle.
package trackers

import (
	"context"
	"fmt"
	"sync"

	"github.com/operator/command-center/internal/scrape"
)

// SecretsAccessor is the narrow subset of *secrets.Store that bearer-auth
// tracker adapters need at request time. Cookie-auth adapters (mam/td/tl)
// don't use this — their cookie is attached by *scrape.Hygiene from the
// canonical `tracker:<id>:cookie` key. Bearer-auth adapters (lst) read their
// token directly via this accessor and set the Authorization header
// themselves; *scrape.Hygiene stays cookie-only by design.
type SecretsAccessor interface {
	Get(ctx context.Context, key string) ([]byte, error)
}

// Config is the shape passed to adapter constructors. Adapter-specific
// settings live in TypeConfigJSON (which the adapter parses into its own
// typed struct).
type Config struct {
	ID                    string
	Name                  string
	Type                  string
	BaseURL               string
	ScrapeIntervalSeconds int
	ScrapeJitterSeconds   int
	UseByparr             bool
	TypeConfigJSON        []byte
	// Secrets is optional for cookie-auth adapters and required for
	// bearer-auth adapters (lst). Adapters that need it should check for
	// nil in their factory and return an error if absent.
	Secrets SecretsAccessor
}

// Factory constructs a scrape.Adapter for a given Config. Adapters register
// their factory via Register() in init().
type Factory func(cfg Config, h *scrape.Hygiene) (scrape.Adapter, error)

var (
	factoryMu sync.RWMutex
	factories = map[string]Factory{}
)

// Register associates a tracker type string (e.g. "mam") with its factory.
// Adapters call this from init().
func Register(typ string, f Factory) {
	factoryMu.Lock()
	defer factoryMu.Unlock()
	factories[typ] = f
}

// New constructs an adapter for cfg.Type. Returns an error if no factory is
// registered for that type.
func New(cfg Config, h *scrape.Hygiene) (scrape.Adapter, error) {
	factoryMu.RLock()
	f, ok := factories[cfg.Type]
	factoryMu.RUnlock()
	if !ok {
		return nil, fmt.Errorf("trackers: unknown tracker type %q (registered: %v)", cfg.Type, RegisteredTypes())
	}
	return f(cfg, h)
}

// RegisteredTypes returns the list of registered adapter types.
func RegisteredTypes() []string {
	factoryMu.RLock()
	defer factoryMu.RUnlock()
	out := make([]string, 0, len(factories))
	for k := range factories {
		out = append(out, k)
	}
	return out
}
