import { CapabilityDefinition, CapabilityDeclaration, CapabilityMode, CapabilityInfo, CapabilityProviderMap, SingletonCapabilityMap, CollectionCapabilityMap, IScopedLogger, IEventBus } from '@camstack/types';
import { ConfigManager } from './config-manager.js';
/** Result of creating a capability router (opaque to the kernel). */
export type CapabilityRouter = unknown;
/** Factory that creates a tRPC router from a capability definition + provider getter. */
export type CapabilityRouterFactory = (definition: CapabilityDefinition, getProvider: () => unknown | null) => CapabilityRouter;
/** Reader function that resolves user's singleton preference for a capability. */
export type ConfigReader = (capability: string) => string | undefined;
export declare class CapabilityRegistry {
    private readonly capabilities;
    private readonly logger;
    private readonly eventBus;
    private routerFactory;
    private configReader;
    private configManager;
    private _ready;
    /** Boot-time dependency graph: capability → dependsOn names (from CapabilityDeclaration) */
    private readonly capabilityDeps;
    /** Per-device singleton overrides: deviceId → (capability → addonId) */
    private readonly deviceOverrides;
    /** Per-device collection filters: deviceId → (capability → addonIds[]) */
    private readonly deviceCollectionFilters;
    /** autoActivate flags per (capability, addonId) — from addon manifests */
    private readonly autoActivateFlags;
    /**
     * Per-device native providers: capName → deviceId (numeric) → { addonId, provider }.
     *
     * Backs `DeviceContext.registerNativeCap`. Lets device-driver addons
     * (OnvifCamera, RtspCamera, etc.) publish capability implementations
     * scoped to a single device; the device-proxy router resolves these by
     * `(capName, deviceId)` to dispatch incoming calls.
     *
     * Distinct from the `capabilities[].providers` map (system-wide providers
     * keyed by addonId) — native providers are device-instance-scoped and
     * never participate in singleton/collection resolution.
     */
    private readonly nativeProviders;
    /**
     * Wrapper provider registrations: capName → Set<addonId>. Tracked separately
     * from `capabilities[].providers` because wrappers aren't system-wide
     * providers — they're user-toggleable decorators addons declare via
     * `ProviderRegistration.kind === 'wrapper'`. Consumers query this map (via
     * `getWrappersForCap`) to render the "pick a wrapper for this device"
     * dropdown in the device-bindings UI.
     */
    private readonly wrapperProviders;
    /**
     * Wrapper registrations that declared `ProviderRegistration.defaultActive:
     * true`. Keyed by `capName` and valued by `addonId` — the first wrapper
     * flagged default-active wins per cap. Consumed by `device-manager.getBindings`
     * to synthesise a wrapper binding when no explicit activation has been
     * persisted for a (device, cap) pair and no native provider exists either —
     * otherwise the default wrapper would sit dormant waiting for a manual
     * `setWrapperActive({active: true})` that never comes.
     */
    private readonly wrapperDefaults;
    /**
     * Pending {@link waitForProvider} waiters, keyed by
     * `"${capabilityName}::${addonId}"`. Resolved by {@link registerProvider}
     * as soon as the matching pair is registered — the mechanism that replaces
     * the 100ms polling loop the device-management router used to run while
     * waiting for forked providers to finish announcing their Moleculer service.
     */
    private readonly pendingWaiters;
    constructor(logger: IScopedLogger, eventBus?: IEventBus);
    /** Set the config reader for singleton preference resolution. */
    setConfigReader(reader: ConfigReader): void;
    /** Set the config manager for persisted device activation state. */
    setConfigManager(manager: ConfigManager): void;
    /** Register boot-time dependency edges (from CapabilityDeclaration.dependsOn). */
    setDependencies(capabilityName: string, dependsOn: readonly string[]): void;
    /** Set the router factory — called by the server layer once at boot. */
    setRouterFactory(factory: CapabilityRouterFactory): void;
    /** Mark boot complete. Before this, getProvider() returns null. */
    ready(): void;
    isReady(): boolean;
    /**
     * Declare a capability from its definition.
     * Creates the auto-mounted router (if factory is set).
     * Called during addon manifest loading, before providers are registered.
     */
    declareCapability(definition: CapabilityDefinition): void;
    /**
     * Register manifest metadata (dependsOn + autoActivate) for a capability.
     * The capability itself MUST already be declared via `declareCapability`
     * from its full `CapabilityDefinition` — mode is the responsibility of
     * the cap definition, never the manifest. Session 5 A5 removed the
     * duplicated `mode` field from addon manifests. If the capability isn't
     * pre-declared, this method warns and no-ops instead of synthesizing a
     * minimal definition (that path used to hide mode drift bugs).
     */
    declareFromManifest(declaration: CapabilityDeclaration, addonId?: string): void;
    /**
     * Register a provider for a capability.
     * For singleton: auto-activates if user-preferred or first registered.
     * For collection: adds to the collection.
     *
     * Phase 12 (settings redesign follow-up): throws if the same
     * `(capabilityName, addonId)` pair is registered more than once.
     * Previously the second call would silently replace the first via
     * `state.providers.set(addonId, provider)` — that was the root cause
     * of the "analysis-pipeline registered three times" bug observed in
     * the kernel cleanup log (`analytics` addon called `registerProvider`
     * twice plus the `analysis-pipeline` addon called it a third time).
     *
     * Double-register of the same pair is always a programmer error —
     * two legitimately different addons implementing the same cap keep
     * using different `addonId`s and the user picks one via `configReader`.
     */
    registerProvider(capabilityName: string, addonId: string, provider: unknown): void;
    /** Unregister a provider. For singleton: clears active. For collection: removes from list. */
    unregisterProvider(capabilityName: string, addonId: string): void;
    /** Enable a previously disabled collection provider. */
    enableCollectionProvider(capability: string, addonId: string): void;
    /** Disable a collection provider (keeps it registered but excluded from active list). */
    disableCollectionProvider(capability: string, addonId: string): void;
    /**
     * Register a native capability provider scoped to a single device.
     * Throws if the same `(capName, deviceId)` is registered twice — that is
     * always a programmer error: device-driver addons must call
     * `DeviceContext.registerNativeCap` exactly once per cap per device.
     */
    registerNativeProvider(capName: string, deviceId: number, addonId: string, provider: unknown): void;
    /** Remove a single native provider entry. No-op if absent. */
    unregisterNativeProvider(capName: string, deviceId: number): void;
    /** Remove every native provider entry for a device — called on device removal. */
    unregisterAllNativeForDevice(deviceId: number): void;
    /** Fallback resolver for native providers that aren't registered in-process. */
    private nativeFallback;
    /**
     * Install a fallback resolver for cross-process native caps. When a device's
     * live `IDevice` lives in a forked worker (e.g. `provider-rtsp`), the native
     * cap it published via `DeviceContext.registerNativeCap` is reachable over
     * the broker — but the hub's `nativeProviders` map never saw it. The fallback
     * is called by {@link getNativeProvider} on local-miss and is expected to
     * return a proxy object whose methods issue `broker.call` into the owning
     * worker. Set once at hub boot time.
     */
    setNativeFallback(resolver: (capName: string, deviceId: number) => unknown | null): void;
    /** Resolve the native provider for `(capName, deviceId)` or null. */
    getNativeProvider<T = unknown>(capName: string, deviceId: number): T | null;
    /** Resolve the addonId that registered the native provider for `(capName, deviceId)`. */
    getNativeAddonId(capName: string, deviceId: number): string | null;
    /** List the capability names for which this device has a native provider registered. */
    getNativeCapsForDevice(deviceId: number): readonly string[];
    /**
     * Mark `addonId` as providing a wrapper implementation for `capName`. Called
     * by the kernel boot wiring when an addon's `ProviderRegistration.kind` is
     * `'wrapper'`. Idempotent — re-registration keeps a single entry.
     */
    registerWrapper(capName: string, addonId: string, opts?: {
        defaultActive?: boolean;
    }): void;
    /** Remove a wrapper registration. No-op if absent. */
    unregisterWrapper(capName: string, addonId: string): void;
    /** Remove every wrapper registration owned by `addonId`. Called on addon stop. */
    unregisterAllWrappersForAddon(addonId: string): void;
    /** Return the addon ids that declared a wrapper for `capName`. */
    getWrappersForCap(capName: string): readonly string[];
    /**
     * Return the wrapper addon flagged `defaultActive: true` for `capName`, or
     * null when no wrapper declared itself the default. Used by
     * `device-manager.getBindings` to auto-bind default wrappers on devices
     * without an explicit user activation.
     */
    getDefaultWrapperForCap(capName: string): string | null;
    /** List capability names that have at least one wrapper flagged `defaultActive: true`. */
    getCapsWithDefaultWrapper(): readonly string[];
    /**
     * Get the active provider for a singleton capability.
     *
     * When called with a declared capability name (a key of
     * `CapabilityProviderMap`), the return type is inferred automatically —
     * no caller-side casts needed. Arbitrary string names fall back to
     * `unknown`.
     */
    getProvider<K extends keyof CapabilityProviderMap>(capabilityName: K): CapabilityProviderMap[K] | null;
    getProvider(capabilityName: string): unknown | null;
    /** Get all providers for a collection capability. */
    getAllProviders<K extends keyof CapabilityProviderMap>(capabilityName: K): readonly CapabilityProviderMap[K][];
    getAllProviders(capabilityName: string): readonly unknown[];
    /** Get provider by addon ID (for per-device resolution). */
    getProviderByAddon<K extends keyof CapabilityProviderMap>(capabilityName: K, addonId: string): CapabilityProviderMap[K] | null;
    getProviderByAddon<T = unknown>(capabilityName: string, addonId: string): T | null;
    /**
     * Wait for a specific `(capabilityName, addonId)` provider to become
     * available, up to `timeoutMs`. Resolves synchronously with the live
     * provider if it is already registered; otherwise queues a waiter that
     * {@link registerProvider} fulfils as soon as the matching pair shows up.
     * Resolves to `null` on timeout instead of rejecting — the caller
     * ({@link device-management.router}) already branches on `null` to return
     * a user-facing "provider not available" error.
     *
     * Replaces the 100ms polling loop from the post-settings-redesign
     * `waitForDeviceProvider` helper. On the happy path there is zero
     * overhead and zero behavioural change; on the race path the wait
     * unblocks on the next `registerProvider` call without any intermediate
     * sleeps. See `[WAIT-EVENT]` in the 2026-04-12 handover.
     */
    waitForProvider<K extends keyof CapabilityProviderMap>(capabilityName: K, addonId: string, timeoutMs: number): Promise<CapabilityProviderMap[K] | null>;
    waitForProvider(capabilityName: string, addonId: string, timeoutMs: number): Promise<unknown | null>;
    /**
     * Get the active singleton provider for a capability.
     *
     * Declared capability names resolve to their typed provider via
     * `SingletonCapabilityMap` — a cap declared as `collection` in its
     * `.cap.ts` definition is rejected at the type level. Arbitrary strings
     * (non-declared capabilities) fall back to the caller-provided `T`
     * (default `unknown`) through the second overload for compatibility
     * with downstream packages that extend `CapabilityProviderMap` via
     * declaration merging.
     *
     * Hard-typed narrowing via `SingletonCapabilityMap` was introduced in
     * session 5 Sprint A5 — see audit addendum finding R6.
     */
    getSingleton<K extends keyof SingletonCapabilityMap>(capability: K): SingletonCapabilityMap[K] | null;
    getSingleton<T = unknown>(capability: string): T | null;
    /** Get the addon ID of the active singleton provider. */
    getSingletonAddonId(capability: string): string | null;
    /**
     * Override the active addon for a singleton capability. Called by the
     * pipeline-orchestrator binding system so future `getSingleton(cap)`
     * lookups return the addon the operator explicitly selected. If
     * `addonId` is `null` or no provider is registered under that id,
     * falls back to the first-registered provider (legacy behaviour).
     *
     * No-op when the capability isn't declared, isn't a singleton, or the
     * requested addon hasn't registered a provider for it yet.
     */
    setSingletonActiveAddon(capability: string, addonId: string | null): void;
    /**
     * Get all active collection providers for a capability.
     *
     * Declared capability names resolve to their typed provider via
     * `CollectionCapabilityMap` — a cap declared as `singleton` in its
     * `.cap.ts` definition is rejected at the type level. Arbitrary strings
     * fall back to the caller-provided `T` (default `unknown`) for
     * compatibility with declaration-merged extensions.
     */
    getCollection<K extends keyof CollectionCapabilityMap>(capability: K): readonly CollectionCapabilityMap[K][];
    getCollection<T = unknown>(capability: string): readonly T[];
    /**
     * Like {@link getCollection} but returns `[addonId, provider]` tuples so
     * callers can attribute work back to the contributing addon.
     *
     * Needed by the device-settings aggregator (`device-manager.addon.ts`) —
     * each contributed field must carry the writer's `addonId` so the
     * `deviceManager.updateDeviceField` mutation can route the write to the
     * correct addon-settings store without hardcoding any addon identity.
     */
    getCollectionEntries<K extends keyof CollectionCapabilityMap>(capability: K): readonly (readonly [string, CollectionCapabilityMap[K]])[];
    getCollectionEntries<T = unknown>(capability: string): readonly (readonly [string, T])[];
    /** Get the full CapabilityDefinition for a declared capability. */
    getDefinition(capability: string): CapabilityDefinition | undefined;
    /** Get the mode declared for a capability. */
    getMode(capability: string): CapabilityMode | undefined;
    /** Get a specific addon's provider by addon ID. */
    getProviderByAddonId<K extends keyof CapabilityProviderMap>(capability: K, addonId: string): CapabilityProviderMap[K] | null;
    getProviderByAddonId<T = unknown>(capability: string, addonId: string): T | null;
    /**
     * Set which addon should be the active singleton for a capability.
     * With `immediate: true`, swaps the runtime provider now.
     */
    setActiveSingleton(capability: string, addonId: string, _immediate?: boolean): Promise<void>;
    /**
     * Check if a specific addon's capability is active system-wide.
     *
     * Resolution: 1. explicit system override → 2. autoActivate flag → 3. default true
     */
    isActiveForSystem(capability: string, addonId: string): boolean;
    /** Set system-wide activation for a capability/addon pair. */
    setSystemActivation(capability: string, addonId: string, active: boolean): void;
    /**
     * Check if a specific addon's capability is active for a device.
     *
     * Resolution: 1. explicit device override → 2. system activation → 3. autoActivate → 4. default true
     */
    isActiveForDevice(capability: string, addonId: string, deviceId: string): boolean;
    /** Set device activation for a capability/addon pair. */
    setDeviceActivation(deviceId: string, capability: string, addonId: string, active: boolean): void;
    /**
     * Get all addon/capability activations for a device.
     * Returns every registered provider with its resolved active status.
     */
    getDeviceActivations(deviceId: string): ReadonlyArray<{
        capability: string;
        addonId: string;
        active: boolean;
    }>;
    /**
     * Get all addon/capability activations at system level.
     * Returns every registered provider with its resolved active status.
     */
    getSystemActivations(): ReadonlyArray<{
        capability: string;
        addonId: string;
        active: boolean;
    }>;
    /** Set a per-device singleton override. */
    setDeviceOverride(deviceId: string, capability: string, addonId: string): void;
    /** Clear a per-device singleton override. */
    clearDeviceOverride(deviceId: string, capability: string): void;
    /** Get all per-device singleton overrides for a device. */
    getDeviceOverrides(deviceId: string): Map<string, string>;
    /**
     * Resolve a singleton provider for a specific device.
     * Checks activation (autoActivate + explicit overrides) then per-device override.
     */
    resolveForDevice<T = unknown>(capability: string, deviceId: string): T | null;
    /** Set a per-device collection filter. */
    setDeviceCollectionFilter(deviceId: string, capability: string, addonIds: string[]): void;
    /** Clear a per-device collection filter. */
    clearDeviceCollectionFilter(deviceId: string, capability: string): void;
    /**
     * Resolve collection providers for a specific device.
     * Filters by activation (autoActivate + explicit overrides) and optional collection filter.
     */
    resolveCollectionForDevice<T = unknown>(capability: string, deviceId: string): readonly T[];
    /** Check if all dependencies for a capability are satisfied. */
    areDependenciesMet(capabilityName: string): boolean;
    /** Get topologically sorted boot order based on dependsOn. */
    getBootOrder(): string[];
    /** Get all auto-mounted routers. Key = capability name. */
    getMountedRouters(): ReadonlyMap<string, CapabilityRouter>;
    /** Get a single mounted router by capability name. */
    getRouter(capabilityName: string): CapabilityRouter | null;
    /** List all declared capabilities (v1-compatible shape). */
    listCapabilities(): CapabilityInfo[];
    /** Check if a capability is declared. */
    has(capabilityName: string): boolean;
    /** Check if a capability has at least one provider. */
    isAvailable(capabilityName: string): boolean;
    /**
     * Return every declared device-scoped capability (`scope: 'device'`)
     * whose `deviceTypes` list includes the given type — or caps that declare
     * no explicit `deviceTypes` (treat as applies-to-all). Used by the
     * bindings UI to enumerate which caps the user can wire for a device.
     */
    listDeviceScopedCapsForType(deviceType: string): readonly string[];
    private emitEvent;
}
//# sourceMappingURL=capability-registry.d.ts.map