import type { AddonContext, AddonInitResult, CapabilitiesAccess, ProviderRegistration } from '../interfaces/addon.js';
import type { ConfigUISchema, ConfigUISchemaWithValues, ConfigField, ConfigSection } from '../interfaces/config-ui.js';
export type { CapabilitiesAccess };
/**
 * Utility type that constrains a ConfigField's `key` to only accept
 * keys of TConfig. Usage inside schema builders:
 *
 * ```ts
 * protected globalSettingsSchema() {
 *   return this.schema({
 *     sections: [{
 *       id: 'main',
 *       title: 'Settings',
 *       fields: [
 *         this.field({ type: 'number', key: 'maxRetries', label: 'Max Retries' }),
 *         //                                ^^^^^^^^^^^ — autocomplete + compile error if wrong
 *       ],
 *     }],
 *   })
 * }
 * ```
 */
type TypedConfigField<TConfig extends object> = ConfigField extends infer F ? F extends {
    readonly key: string;
} ? Omit<F, 'key'> & {
    readonly key: keyof TConfig & string;
} : F : never;
/**
 * A ConfigSection whose fields are key-constrained to TConfig.
 */
interface TypedConfigSection<TConfig extends object> extends Omit<ConfigSection, 'fields'> {
    readonly fields: ReadonlyArray<TypedConfigField<TConfig> | ConfigField>;
}
/**
 * A ConfigUISchema whose sections use typed fields.
 */
interface TypedConfigUISchema<TConfig extends object> extends Omit<ConfigUISchema, 'sections'> {
    readonly sections: ReadonlyArray<TypedConfigSection<TConfig>>;
}
/**
 * Base class for CamStack addons. Eliminates settings boilerplate:
 *
 * - Typed `config` property with automatic resolution from store + defaults
 * - `getGlobalSettings()` / `updateGlobalSettings()` auto-implemented
 * - `getAddonSettings()` / `updateAddonSettings()` auto-implemented
 * - `getDeviceSettings()` / `updateDeviceSettings()` auto-implemented
 * - `ctx` accessor for the AddonContext (no manual `this.ctxRef` storage)
 * - `field()` helper that constrains field keys to TConfig keys
 * - `schema()` helper that validates the full schema structure
 *
 * Subclasses override:
 * - `onInitialize()` — addon-specific init logic, return ProviderRegistration[]
 * - `onShutdown()` — cleanup
 * - `globalSettingsSchema()` / `deviceSettingsSchema()` — UI schemas
 *
 * @example
 * ```ts
 * interface MyConfig {
 *   maxRetries: number
 *   endpoint: string
 * }
 *
 * export default class MyAddon extends BaseAddon<MyConfig> {
 *   constructor() {
 *     super({ maxRetries: 3, endpoint: 'http://localhost' })
 *   }
 *
 *   protected globalSettingsSchema() {
 *     return this.schema({
 *       sections: [{
 *         id: 'main', title: 'Settings',
 *         fields: [
 *           this.field({ type: 'number', key: 'maxRetries', label: 'Max Retries', default: 3 }),
 *           this.field({ type: 'text', key: 'endpoint', label: 'Endpoint' }),
 *         ],
 *       }],
 *     })
 *   }
 *
 *   protected async onInitialize(): Promise<ProviderRegistration[]> {
 *     const client = new Client(this.config.endpoint, this.config.maxRetries)
 *     return [{ capability: 'my-feature', provider: client }]
 *   }
 * }
 * ```
 */
export declare abstract class BaseAddon<TConfig extends object = Record<string, unknown>> {
    private _ctx;
    private _config;
    /**
     * Per-process random id used as the `generation` stamp on every
     * `system.ready-state` event this addon emits. Constant for the
     * lifetime of this addon instance (== one process boot); consumer-
     * side registries derive a monotonic `epoch` by watching for
     * generation transitions.
     */
    private readonly _readinessGeneration;
    /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
    private _registeredCapNames;
    /** Default config values. Provided via constructor. */
    protected readonly defaults: TConfig;
    constructor(defaults: TConfig);
    /**
     * Override to opt out of the automatic `system.ready-state` emission.
     * Returns `true` by default — addons that don't map cleanly to the
     * readiness protocol (e.g. pure collection providers whose readiness
     * is already reported per-device by upstream) can override to `false`
     * and emit manually.
     */
    protected get autoEmitReadiness(): boolean;
    /** The AddonContext, available after initialize(). */
    get ctx(): AddonContext;
    /**
     * Non-throwing ctx accessor for code paths that can legitimately run
     * before `initialize()` has resolved — most commonly cap handlers the
     * hub invokes eagerly at page load (device-details aggregator pings
     * every addon's `getDeviceSettingsContribution` as soon as the page
     * mounts). Prefer `ctx` for the normal case; reach for this only in
     * capability methods that may be queried before the addon is wired up.
     */
    protected get ctxIfReady(): AddonContext | null;
    /** Current resolved config (defaults merged with persisted store values). */
    get config(): Readonly<TConfig>;
    initialize(context: AddonContext): Promise<AddonInitResult | void>;
    /**
     * Called by the isolated-process runner AFTER `broker.start()`.
     * In-process addons never need this because the hub broker is already
     * running when `initialize()` fires. For forked children the broker
     * starts AFTER `initialize()`, so the readiness emit inside initialize()
     * fires before the broker can broadcast it. This method re-emits the
     * ready state once the transport is live.
     */
    postBrokerStart(): void;
    /**
     * Called by the isolated-process runner / agent bootstrap once the broker
     * has started AND the hub node is connected — the moment `ctx.api.*` calls
     * to hub-provided capabilities become safe. Wrap any bootstrap work that
     * must query the hub (device restore, settings fetch, initial sync) in
     * `onHubConnected()` rather than `onInitialize()` — during `initialize()`
     * on a worker the broker is not yet connected and remote cap calls either
     * time out or throw "Service not found".
     *
     * Default implementation is a no-op. Override in subclasses that need it.
     * Never fires on hub-local in-process addons (the hub is its own node).
     */
    onHubReachable(): Promise<void>;
    shutdown(): Promise<void>;
    /**
     * Addon-specific initialization. Called after config is resolved.
     *
     * Subclasses may return:
     * - an array of `ProviderRegistration` (back-compat, most common),
     * - an `AddonInitResult` envelope to also declare `customActions`,
     * - `void` for pure consumers.
     */
    protected abstract onInitialize(): Promise<ProviderRegistration[] | AddonInitResult | void>;
    /** Addon-specific cleanup. Override if needed. */
    protected onShutdown(): Promise<void>;
    /**
     * Called after config is resolved during updateGlobalSettings/updateAddonSettings.
     * Override to react to config changes (e.g. restart a sampler, reconnect a service).
     * Not called during initialize() — use onInitialize() for initial setup.
     */
    protected onConfigChanged(): Promise<void>;
    /**
     * Create a ConfigField with `key` constrained to keys of TConfig.
     * Provides autocomplete and compile-time validation.
     */
    protected field<F extends ConfigField>(field: F extends {
        readonly key: string;
    } ? Omit<F, 'key'> & {
        readonly key: keyof TConfig & string;
    } : F): ConfigField;
    /**
     * Create a full ConfigUISchema with typed sections.
     * Fields created via `this.field()` get key validation automatically.
     */
    protected schema(schema: TypedConfigUISchema<TConfig>): ConfigUISchema;
    /** Override to provide global-level settings UI schema. */
    protected globalSettingsSchema(): ConfigUISchema | null;
    /** Override to provide device-level settings UI schema. */
    protected deviceSettingsSchema(): ConfigUISchema | null;
    getGlobalSettings(overlay?: Record<string, unknown>): Promise<ConfigUISchemaWithValues>;
    updateGlobalSettings(patch: Partial<TConfig>): Promise<void>;
    /**
     * If any field in `patch` is marked `requiresRestart` in `schema`,
     * schedule an addon restart for the next tick. Deferred via
     * `setImmediate` so the tRPC mutation that triggered the write has
     * time to return its response before the addon is torn down and
     * re-initialised by `AddonRegistryService.restartAddon`.
     */
    private maybeAutoRestart;
    getDeviceSettings(deviceId: number): Promise<ConfigUISchemaWithValues>;
    updateDeviceSettings(deviceId: number, patch: Record<string, unknown>): Promise<void>;
    private _subscriptions;
    /**
     * Subscribe to an event bus category. The subscription is automatically
     * unsubscribed on shutdown — no manual cleanup needed.
     *
     * @example
     * ```ts
     * this.subscribe({ category: EventCategory.DeviceRegistered }, (event) => {
     *   void this.handleDevice(event)
     * })
     * ```
     */
    /**
     * Subscribe to `system.ready-state` events for one or more capabilities.
     * Abstracts the boilerplate type-narrowing + nodeId extraction that every
     * resilience subscription duplicates. Cleanup is automatic (registered on
     * `_subscriptions` like a normal `subscribe` call).
     */
    protected watchCapability(capNames: string | readonly string[], handlers: {
        readonly onDown?: (nodeId: string, capName: string) => void;
        readonly onReady?: (nodeId: string, capName: string) => void;
    }): void;
    protected subscribe<C extends import('../interfaces/event-bus.js').KnownEventCategory>(filter: import('../interfaces/event-bus.js').TypedEventFilter<C>, handler: (event: import('../interfaces/event-bus.js').TypedSystemEvent<C>) => void): void;
    protected subscribe(filter: import('../interfaces/event-bus.js').EventFilter, handler: (event: import('../interfaces/event-bus.js').SystemEvent) => void): void;
    private emitLifecycle;
    /**
     * Emit a `system.ready-state` event for every capability this addon
     * registered at init. Scope is `{type:'node', nodeId}` — readiness is
     * tied to the node that hosts the provider. Consumers that care about
     * per-device readiness can subscribe with `{type:'device', ...}` if
     * the provider emits at finer granularity (collection addons may
     * opt-out via `autoEmitReadiness` and emit manually).
     */
    private emitReadinessForProviders;
    /**
     * Resolve the shared models directory path via the storage capability.
     * Falls back to `camstack-data/models` if storage is unavailable.
     * Used by inference addons (detection-pipeline, audio-classifier, embedding-encoder).
     */
    protected resolveModelsDir(): Promise<string>;
    /**
     * Access the runtime capability registry for in-process provider lookups.
     * Returns null if the registry is not available (e.g. on agents).
     * Used by addons that consume other capabilities directly (snapshot, stream-broker, enrichment).
     */
    protected get capabilities(): CapabilitiesAccess | null;
    /**
     * Resolve config by merging defaults with persisted store values.
     * Called automatically during initialize() and after every updateSettings().
     *
     * The merge is shallow: each key in `defaults` is checked against the store.
     * Only keys present in defaults are read — the store can contain extra keys
     * (e.g. from older versions) without polluting the typed config.
     */
    protected resolveConfig(): Promise<void>;
    /**
     * Wrap `ctx.settings.readAddonStore()` with a short retry budget so a
     * transient settings-store outage (mid-restart of sqlite-settings, tsx-watch
     * swap, or any race where the SqliteSettingsBackend is between shutdown and
     * re-initialize) doesn't propagate up into `initialize()` and leave the
     * addon permanently broken.
     *
     * Retry only the two known infra fingerprints. Anything else propagates so
     * real bugs surface immediately. After the budget expires we fall back to
     * `{}` (defaults) — the addon's first successful patch will rehydrate.
     */
    private readAddonStoreWithRetry;
}
/**
 * Normalize an `ICamstackAddon.initialize()` return value into the
 * `AddonInitResult` envelope. Arrays are wrapped into `{ providers }`;
 * envelopes pass through; void stays void.
 *
 * Exported so the kernel boot path and the backend addon registry can
 * share the same normalizer instead of duplicating the shim. Addons that
 * extend `BaseAddon` already emit the envelope, so this helper is only a
 * safety net for direct `ICamstackAddon` implementations (mostly tests).
 */
export declare function normalizeAddonInitResult(result: ProviderRegistration[] | AddonInitResult | void): AddonInitResult | void;
//# sourceMappingURL=base-addon.d.ts.map