/**
 * 벡터 검색 서비스
 * 단일 책임 원칙(SRP) 적용 - 검색 기능만 담당
 */
import { VECTOR_SEARCH_CONFIG } from '../../../../shared/config/vector-search.config.js';
import { UnifiedEmbeddingService } from '../../../embedding/services/unified-embedding-service.js';
import { vectorSearchResultNormalizer } from './vector-search-result-normalizer.js';
export class VectorSearchService {
    repository;
    config;
    constructor(repository, config = VECTOR_SEARCH_CONFIG) {
        this.repository = repository;
        this.config = config;
    }
    /**
     * 벡터 검색 실행
     */
    async search(query) {
        this.validateQuery(query);
        try {
            return await this.repository.search(query);
        }
        catch (error) {
            throw new Error(`벡터 검색 실패: ${error}`);
        }
    }
    /**
     * 하이브리드 검색 실행
     */
    async hybridSearch(query) {
        this.validateQuery(query);
        try {
            return await this.repository.hybridSearch(query);
        }
        catch (error) {
            throw new Error(`하이브리드 검색 실패: ${error}`);
        }
    }
    async providerHybridSearch(embeddingService, baseQuery) {
        this.validateProviderHybridQuery(baseQuery);
        const providers = new Set();
        if (baseQuery.overrideProviders && baseQuery.overrideProviders.length > 0) {
            baseQuery.overrideProviders.forEach(provider => providers.add(provider));
        }
        else if (baseQuery.useAvailableProviders) {
            embeddingService.getAvailableProviders().forEach(provider => providers.add(provider));
        }
        else if (baseQuery.query.provider) {
            providers.add(baseQuery.query.provider);
        }
        if (providers.size === 0) {
            providers.add('minilm');
        }
        const results = [];
        for (const provider of providers) {
            let providerVector = baseQuery.query.queryVector;
            if (baseQuery.text) {
                const embedding = await embeddingService.generateEmbedding(baseQuery.text, provider);
                if (!embedding || !embedding.embedding) {
                    continue;
                }
                providerVector = embedding.embedding;
            }
            if (!providerVector || providerVector.length === 0) {
                continue;
            }
            const expectedDimensions = this.getExpectedDimensions(provider);
            if (providerVector.length !== expectedDimensions) {
                continue;
            }
            const providerOptions = {
                ...baseQuery.query.options,
                limit: baseQuery.query.options.limit ?? this.config.defaultLimit,
                threshold: baseQuery.query.options.threshold ?? this.config.defaultThreshold
            };
            const providerQuery = {
                ...baseQuery.query,
                provider,
                queryVector: providerVector,
                options: providerOptions
            };
            const vectorStart = Date.now();
            const vectorResults = await this.repository.search(providerQuery);
            const vectorLatency = Date.now() - vectorStart;
            const vectorLimit = baseQuery.vectorLimit ?? providerOptions.limit ?? this.config.defaultLimit;
            const topVector = vectorResults.slice(0, vectorLimit);
            let hybridResults = [];
            let hybridLatency;
            if (baseQuery.useHybrid === true) {
                const hybridStart = Date.now();
                hybridResults = await this.repository.hybridSearch(providerQuery);
                hybridLatency = Date.now() - hybridStart;
            }
            results.push({
                provider,
                vectorResults: topVector,
                hybridResults,
                vectorLatencyMs: vectorLatency,
                hybridLatencyMs: hybridLatency
            });
        }
        return results;
    }
    async unifiedSearch(embeddingService, baseQuery) {
        const providerResults = await this.providerHybridSearch(embeddingService, baseQuery);
        const unified = vectorSearchResultNormalizer.normalize(providerResults);
        return {
            providers: providerResults,
            unified
        };
    }
    /**
     * 검색 쿼리 유효성 검증
     */
    validateQuery(query) {
        const expectedDimensions = this.getExpectedDimensions(query.provider);
        if (!query.queryVector || query.queryVector.length !== expectedDimensions) {
            throw new Error(`벡터 차원 불일치: 예상 ${expectedDimensions}, 실제 ${query.queryVector?.length || 0}`);
        }
        if (query.options.limit && (query.options.limit < 1 || query.options.limit > 100)) {
            throw new Error('검색 제한은 1-100 사이여야 합니다');
        }
    }
    validateProviderHybridQuery(query) {
        if (!query.query) {
            throw new Error('ProviderHybridQuery.query가 필요합니다');
        }
        this.validateQuery(query.query);
    }
    getExpectedDimensions(provider) {
        // provider가 없을 때도 repository의 getTableName과 동일하게 'tfidf'의 차원을 사용
        // 왜 필요한가? repository에서 getTableName(provider ?? 'tfidf')는 'tfidf' 테이블(512차원)을 선택하므로
        // 차원 계산도 동일하게 'tfidf'의 차원(512)을 사용해야 차원 불일치 오류를 방지할 수 있음
        const effectiveProvider = provider ?? 'tfidf';
        return this.config.providerDimensions[effectiveProvider] ?? this.config.defaultDimensions;
    }
    /**
     * 기본 옵션 적용
     */
    applyDefaultOptions(options) {
        return {
            limit: this.config.defaultLimit,
            threshold: this.config.defaultThreshold,
            includeContent: true,
            includeMetadata: false,
            ...options
        };
    }
}
//# sourceMappingURL=vector-search.service.js.map