/**
 * Reranker Registry
 *
 * Centralized registry for all reranker implementations with metadata
 * and discovery capabilities. Follows the BaseRegistry pattern.
 */
import { BaseRegistry } from "../../core/infrastructure/index.js";
import { logger } from "../../utils/logger.js";
import { RAGErrorCodes, RerankerError } from "../errors/RAGError.js";
/**
 * Default reranker metadata entries
 */
const DEFAULT_RERANKER_METADATA = {
    llm: {
        description: "LLM-powered semantic reranking with multi-factor scoring",
        defaultConfig: {
            topK: 3,
            weights: { semantic: 0.4, vector: 0.4, position: 0.2 },
        },
        supportedOptions: ["model", "provider", "topK", "weights"],
        useCases: [
            "High-quality semantic reranking",
            "Complex query understanding",
            "Context-aware scoring",
        ],
        aliases: ["semantic", "ai", "model-based"],
        requiresModel: true,
        requiresExternalAPI: false,
    },
    "cross-encoder": {
        description: "Cross-encoder model for query-document relevance scoring",
        defaultConfig: {
            topK: 3,
            model: "ms-marco-MiniLM-L-6-v2",
        },
        supportedOptions: ["model", "topK"],
        useCases: [
            "High-precision reranking",
            "Search result refinement",
            "Academic/research applications",
        ],
        aliases: ["cross", "encoder", "bi-encoder"],
        requiresModel: true,
        requiresExternalAPI: false,
    },
    cohere: {
        description: "Cohere Rerank API for production-grade relevance scoring",
        defaultConfig: {
            topK: 3,
            model: "rerank-v3.5",
        },
        supportedOptions: ["model", "topK", "apiKey"],
        useCases: [
            "Production search systems",
            "Enterprise applications",
            "High-volume reranking",
        ],
        aliases: ["cohere-rerank", "cohere-api"],
        requiresModel: false,
        requiresExternalAPI: true,
    },
    simple: {
        description: "Position and vector score-based reranking (no LLM required)",
        defaultConfig: {
            topK: 3,
            weights: { vector: 0.8, position: 0.2 },
        },
        supportedOptions: ["topK", "weights"],
        useCases: [
            "Fast reranking",
            "Low-latency requirements",
            "When LLM is unavailable",
        ],
        aliases: ["fast", "basic", "position-based"],
        requiresModel: false,
        requiresExternalAPI: false,
    },
    batch: {
        description: "Batch LLM reranking for efficient multi-document scoring",
        defaultConfig: {
            topK: 3,
            weights: { semantic: 0.4, vector: 0.4, position: 0.2 },
        },
        supportedOptions: ["model", "provider", "topK", "weights"],
        useCases: [
            "Large result sets",
            "Cost-efficient LLM usage",
            "Batch processing pipelines",
        ],
        aliases: ["batch-llm", "efficient", "bulk"],
        requiresModel: true,
        requiresExternalAPI: false,
    },
};
/**
 * Reranker Registry
 *
 * Manages registration and discovery of all reranker implementations.
 * Extends BaseRegistry for consistent lifecycle management.
 */
export class RerankerRegistry extends BaseRegistry {
    static instance = null;
    aliasMap = new Map();
    constructor() {
        super();
    }
    /**
     * Get singleton instance
     */
    static getInstance() {
        if (!RerankerRegistry.instance) {
            RerankerRegistry.instance = new RerankerRegistry();
        }
        return RerankerRegistry.instance;
    }
    /**
     * Reset singleton (for testing)
     */
    static resetInstance() {
        if (RerankerRegistry.instance) {
            RerankerRegistry.instance.clear();
            RerankerRegistry.instance = null;
        }
    }
    /**
     * Register all built-in rerankers
     */
    async registerAll() {
        // Import reranker functions
        const rerankerModule = await import("./reranker.js");
        // Register LLM reranker
        this.registerReranker("llm", async () => this.createLLMRerankerInstance(rerankerModule.rerank), DEFAULT_RERANKER_METADATA.llm);
        // Register cross-encoder reranker
        this.registerReranker("cross-encoder", async () => this.createCrossEncoderInstance(rerankerModule.CrossEncoderReranker), DEFAULT_RERANKER_METADATA["cross-encoder"]);
        // Register Cohere reranker
        this.registerReranker("cohere", async () => this.createCohereInstance(rerankerModule.CohereRelevanceScorer), DEFAULT_RERANKER_METADATA.cohere);
        // Register simple reranker
        this.registerReranker("simple", async () => this.createSimpleRerankerInstance(rerankerModule.simpleRerank), DEFAULT_RERANKER_METADATA.simple);
        // Register batch reranker
        this.registerReranker("batch", async () => this.createBatchRerankerInstance(rerankerModule.batchRerank), DEFAULT_RERANKER_METADATA.batch);
        logger.debug(`[RerankerRegistry] Registered ${this.items.size} reranker types`);
    }
    /**
     * Create LLM reranker instance wrapper
     */
    createLLMRerankerInstance(_rerankFn) {
        return {
            type: "llm",
            async rerank() {
                throw new RerankerError("LLM reranker requires model provider. Use RerankerFactory.createReranker() instead.", {
                    rerankerType: "llm",
                });
            },
        };
    }
    /**
     * Create cross-encoder instance wrapper
     */
    createCrossEncoderInstance(CrossEncoderClass) {
        const encoder = new CrossEncoderClass();
        return {
            type: "cross-encoder",
            async rerank(results, query, options) {
                const documents = results.map((r) => r.text || r.metadata?.text || "");
                const scores = await encoder.rerank(query, documents);
                const topK = options?.topK ?? 3;
                return scores
                    .map((s) => ({
                    result: results[s.index],
                    score: s.score,
                    details: {
                        semantic: s.score,
                        vector: results[s.index].score ?? 0,
                        position: 1 - s.index / results.length,
                    },
                }))
                    .sort((a, b) => b.score - a.score)
                    .slice(0, topK);
            },
        };
    }
    /**
     * Create Cohere instance wrapper
     */
    createCohereInstance(CohereClass) {
        const scorer = new CohereClass();
        return {
            type: "cohere",
            async rerank(results, query, options) {
                const documents = results.map((r) => r.text || r.metadata?.text || "");
                const scores = await scorer.score(query, documents);
                const topK = options?.topK ?? 3;
                return scores
                    .map((s) => ({
                    result: results[s.index],
                    score: s.score,
                    details: {
                        semantic: s.score,
                        vector: results[s.index].score ?? 0,
                        position: 1 - s.index / results.length,
                    },
                }))
                    .sort((a, b) => b.score - a.score)
                    .slice(0, topK);
            },
        };
    }
    /**
     * Create simple reranker instance wrapper
     */
    createSimpleRerankerInstance(simpleRerankFn) {
        return {
            type: "simple",
            async rerank(results, _query, options) {
                return simpleRerankFn(results, {
                    topK: options?.topK,
                    vectorWeight: options?.weights?.vector,
                    positionWeight: options?.weights?.position,
                });
            },
        };
    }
    /**
     * Create batch reranker instance wrapper
     */
    createBatchRerankerInstance(_batchRerankFn) {
        return {
            type: "batch",
            async rerank() {
                throw new RerankerError("Batch reranker requires model provider. Use RerankerFactory.createReranker() instead.", {
                    rerankerType: "batch",
                });
            },
        };
    }
    /**
     * Register a reranker with aliases
     */
    registerReranker(type, factory, metadata) {
        this.register(type, factory, metadata.aliases, { metadata });
        // Register aliases in local alias map for type resolution
        for (const alias of metadata.aliases) {
            this.aliasMap.set(alias.toLowerCase(), type);
            logger.debug(`[RerankerRegistry] Registered alias '${alias}' -> '${type}'`);
        }
    }
    /**
     * Resolve type from alias
     */
    resolveType(nameOrAlias) {
        const lower = nameOrAlias.toLowerCase();
        // Check if it's a direct type
        if (this.items.has(lower)) {
            return lower;
        }
        // Check aliases
        const resolved = this.aliasMap.get(lower);
        if (resolved) {
            return resolved;
        }
        throw new RerankerError(`Unknown reranker type: '${nameOrAlias}'. Available types: ${this.list()
            .map((item) => item.id)
            .join(", ")}`, {
            code: RAGErrorCodes.RERANKER_NOT_FOUND,
            rerankerType: nameOrAlias,
            details: {
                requestedType: nameOrAlias,
                availableTypes: this.list().map((item) => item.id),
            },
        });
    }
    /**
     * Get a reranker by type or alias
     */
    async getReranker(typeOrAlias) {
        await this.ensureInitialized();
        const type = this.resolveType(typeOrAlias);
        const reranker = await this.get(type);
        if (!reranker) {
            throw new RerankerError(`Reranker not found: ${type}`, {
                code: RAGErrorCodes.RERANKER_NOT_FOUND,
                rerankerType: type,
                details: { type },
            });
        }
        return reranker;
    }
    /**
     * Get list of available reranker types
     */
    async getAvailableRerankers() {
        await this.ensureInitialized();
        return this.list().map((item) => item.id);
    }
    /**
     * Get metadata for a specific reranker
     */
    getRerankerMetadata(typeOrAlias) {
        const type = this.resolveType(typeOrAlias);
        const entry = this.list().find((item) => item.id === type);
        return entry?.metadata;
    }
    /**
     * Get all aliases for a type
     */
    getAliasesForType(type) {
        const metadata = DEFAULT_RERANKER_METADATA[type];
        return metadata?.aliases ?? [];
    }
    /**
     * Get all registered aliases
     */
    getAllAliases() {
        return new Map(this.aliasMap);
    }
    /**
     * Check if a type or alias exists
     */
    hasReranker(typeOrAlias) {
        try {
            this.resolveType(typeOrAlias);
            return true;
        }
        catch {
            return false;
        }
    }
    /**
     * Get rerankers by use case
     */
    getRerankersByUseCase(useCase) {
        const matches = [];
        const useCaseLower = useCase.toLowerCase();
        for (const [type, metadata] of Object.entries(DEFAULT_RERANKER_METADATA)) {
            const hasMatchingUseCase = metadata.useCases.some((uc) => uc.toLowerCase().includes(useCaseLower));
            if (hasMatchingUseCase) {
                matches.push(type);
            }
        }
        return matches;
    }
    /**
     * Get default configuration for a reranker
     */
    getDefaultConfig(typeOrAlias) {
        const metadata = this.getRerankerMetadata(typeOrAlias);
        return metadata?.defaultConfig;
    }
    /**
     * Get rerankers that don't require external APIs
     */
    getLocalRerankers() {
        const matches = [];
        for (const [type, metadata] of Object.entries(DEFAULT_RERANKER_METADATA)) {
            if (!metadata.requiresExternalAPI) {
                matches.push(type);
            }
        }
        return matches;
    }
    /**
     * Get rerankers that don't require AI models
     */
    getModelFreeRerankers() {
        const matches = [];
        for (const [type, metadata] of Object.entries(DEFAULT_RERANKER_METADATA)) {
            if (!metadata.requiresModel) {
                matches.push(type);
            }
        }
        return matches;
    }
    /**
     * Clear the registry (also clears aliases)
     */
    clear() {
        super.clear();
        this.aliasMap.clear();
    }
}
/**
 * Global reranker registry singleton
 */
export const rerankerRegistry = RerankerRegistry.getInstance();
/**
 * Convenience function to get available rerankers
 */
export async function getAvailableRerankers() {
    return rerankerRegistry.getAvailableRerankers();
}
/**
 * Convenience function to get reranker by type
 */
export async function getReranker(typeOrAlias) {
    return rerankerRegistry.getReranker(typeOrAlias);
}
/**
 * Convenience function to get reranker metadata
 */
export function getRegisteredRerankerMetadata(typeOrAlias) {
    return rerankerRegistry.getRerankerMetadata(typeOrAlias);
}
