// NERF Provider Fallback Resolver
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
export class FallbackResolver {
    constructor(settingsManager, authStorage, modelRegistry) {
        this.settingsManager = settingsManager;
        this.authStorage = authStorage;
        this.modelRegistry = modelRegistry;
    }
    /**
     * Find the next available fallback for a model that just failed.
     * Searches all chains for entries matching the current model's provider+id,
     * then returns the next available entry with lower priority (higher number).
     *
     * @returns FallbackResult if a fallback is available, null otherwise
     */
    async findFallback(currentModel, errorType) {
        const { enabled, chains } = this.settingsManager.getFallbackSettings();
        if (!enabled)
            return null;
        // Mark the current provider as exhausted at the provider level
        this.authStorage.markProviderExhausted(currentModel.provider, errorType);
        // Search all chains for one containing the current model
        for (const [chainName, entries] of Object.entries(chains)) {
            const currentIndex = entries.findIndex((e) => e.provider === currentModel.provider && e.model === currentModel.id);
            if (currentIndex === -1)
                continue;
            // Try entries after the current one (already sorted by priority)
            const result = await this._findAvailableInChain(chainName, entries, currentIndex + 1);
            if (result)
                return result;
            // Wrap around: try entries before the current one
            const wrapResult = await this._findAvailableInChain(chainName, entries, 0, currentIndex);
            if (wrapResult)
                return wrapResult;
        }
        return null;
    }
    /**
     * Check if a higher-priority provider in the chain has recovered.
     * Called before each LLM request to restore the best available provider.
     *
     * @returns FallbackResult if a better provider is available, null if current is best
     */
    async checkForRestoration(currentModel) {
        const { enabled, chains } = this.settingsManager.getFallbackSettings();
        if (!enabled)
            return null;
        for (const [chainName, entries] of Object.entries(chains)) {
            const currentIndex = entries.findIndex((e) => e.provider === currentModel.provider && e.model === currentModel.id);
            if (currentIndex === -1)
                continue;
            // Only check entries with higher priority (lower index = higher priority)
            if (currentIndex === 0)
                continue; // Already at highest priority
            const result = await this._findAvailableInChain(chainName, entries, 0, currentIndex);
            if (result) {
                return {
                    ...result,
                    reason: `${result.model.provider}/${result.model.id} recovered, restoring from fallback`,
                };
            }
        }
        return null;
    }
    /**
     * Get the best available model from a named chain.
     * Useful for initial model selection.
     */
    async getBestAvailable(chainName) {
        const { enabled, chains } = this.settingsManager.getFallbackSettings();
        if (!enabled)
            return null;
        const entries = chains[chainName];
        if (!entries || entries.length === 0)
            return null;
        return this._findAvailableInChain(chainName, entries, 0);
    }
    /**
     * Find the chain(s) a model belongs to.
     */
    findChainsForModel(provider, modelId) {
        const { chains } = this.settingsManager.getFallbackSettings();
        const result = [];
        for (const [chainName, entries] of Object.entries(chains)) {
            if (entries.some((e) => e.provider === provider && e.model === modelId)) {
                result.push(chainName);
            }
        }
        return result;
    }
    /**
     * Search a chain for the first available entry starting from startIndex.
     */
    async _findAvailableInChain(chainName, entries, startIndex, endIndex) {
        const end = endIndex ?? entries.length;
        for (let i = startIndex; i < end; i++) {
            const entry = entries[i];
            // Check provider-level backoff
            if (!this.authStorage.isProviderAvailable(entry.provider)) {
                continue;
            }
            // Check if model exists in registry
            const model = this.modelRegistry.find(entry.provider, entry.model);
            if (!model)
                continue;
            // Check if provider is request-ready for fallback (authMode-aware)
            if (!this.modelRegistry.isProviderRequestReady(entry.provider))
                continue;
            return {
                model,
                chainName,
                reason: `falling back to ${entry.provider}/${entry.model}`,
            };
        }
        return null;
    }
}
//# sourceMappingURL=fallback-resolver.js.map