/**
 * Anchor Cache Service
 * 캐시 및 임베딩 관리 담당
 * Phase 1.1: anchor-manager.ts 리팩토링
 */
import { logger } from '../../../../shared/utils/logger.js';
/**
 * Anchor Cache Service 구현
 */
export class AnchorCacheService {
    /**
     * 메모리 캐시: agent_id별 슬롯 상태 관리
     * Map<agent_id, {A: memory_id | null, B: memory_id | null, C: memory_id | null}>
     */
    cache = new Map();
    db = null;
    embeddingService = null;
    /**
     * 생성자 (필수 의존성 주입 권장, 미전달 시 setDatabase/setEmbeddingService 호출 필요)
     */
    constructor(db, embeddingService) {
        if (db)
            this.db = db;
        if (embeddingService)
            this.embeddingService = embeddingService;
        logger.info('AnchorCacheService 초기화 완료');
    }
    /**
     * 데이터베이스 설정
     */
    setDatabase(db) {
        if (!db) {
            throw new Error('Database instance is required');
        }
        this.db = db;
    }
    /**
     * 임베딩 서비스 설정
     */
    setEmbeddingService(embeddingService) {
        if (!embeddingService) {
            throw new Error('MemoryEmbeddingService is required');
        }
        this.embeddingService = embeddingService;
    }
    /**
     * 앵커 메모리의 임베딩 조회
     * @param memoryId - 메모리 ID
     * @returns 임베딩 벡터 및 제공자 정보, 없으면 null
     */
    async getAnchorEmbedding(memoryId) {
        if (!this.db) {
            throw new Error('Database is not set.');
        }
        try {
            // Edge Case: 메모리 존재 확인 (메모리 삭제 체크)
            const memoryExists = this.db.prepare(`
        SELECT id FROM memory_item WHERE id = ?
      `).get(memoryId);
            if (!memoryExists) {
                // 메모리가 삭제된 경우
                logger.warn('Memory not found (may have been deleted)', { memoryId });
                return null;
            }
            // 레거시 스키마 호환성: embedding 컬럼 존재 여부 확인
            // "no such column: embedding" 에러 방지를 위해 필수
            // 왜 필요한가? 레거시 데이터베이스는 embedding 컬럼이 없을 수 있음
            const columns = this.db.prepare(`
        SELECT name FROM pragma_table_info('memory_embedding')
      `).all();
            const hasEmbeddingColumn = columns.some(c => c.name === 'embedding');
            // embedding 컬럼이 없으면 null 반환 (레거시 스키마)
            // ensureLegacySchema가 컬럼을 추가하지만, 초기화 전 접근 시 대비
            if (!hasEmbeddingColumn) {
                logger.warn('Embedding column not found in memory_embedding table (legacy schema)', { memoryId });
                return null;
            }
            const embeddingRecord = this.db.prepare(`
        SELECT 
          embedding,
          embedding_provider,
          dimensions,
          dim
        FROM memory_embedding
        WHERE memory_id = ?
        ORDER BY created_at DESC
        LIMIT 1
      `).get(memoryId);
            if (!embeddingRecord || !embeddingRecord.embedding) {
                // Edge Case: 임베딩 없음 (메모리는 존재하지만 임베딩이 없음)
                return null;
            }
            // JSON 문자열로 저장된 임베딩을 배열로 파싱
            let embeddingVector;
            try {
                embeddingVector = typeof embeddingRecord.embedding === 'string'
                    ? JSON.parse(embeddingRecord.embedding)
                    : embeddingRecord.embedding;
                if (!Array.isArray(embeddingVector) || embeddingVector.length === 0) {
                    // Edge Case: 유효하지 않은 임베딩
                    logger.warn('Invalid embedding (empty or not an array)', { memoryId });
                    return null;
                }
            }
            catch (error) {
                // Edge Case: 임베딩 파싱 실패
                logger.error('Embedding parsing failed', { memoryId, error: error instanceof Error ? error.message : String(error) });
                return null;
            }
            const provider = embeddingRecord.embedding_provider || 'tfidf';
            return {
                embedding: embeddingVector,
                provider: provider
            };
        }
        catch (error) {
            // Edge Case: 데이터베이스 오류
            logger.error('Embedding retrieval failed', { memoryId, error: error instanceof Error ? error.message : String(error) });
            return null;
        }
    }
    /**
     * 서버 재시작 시 DB에서 캐시 복원
     * @param db - 데이터베이스 인스턴스
     */
    async restoreCacheFromDB(db) {
        if (!db) {
            throw new Error('Database instance is required');
        }
        try {
            // anchor 테이블 존재 여부 확인
            const tableExists = db.prepare(`
        SELECT name FROM sqlite_master 
        WHERE type='table' AND name='anchor'
      `).get();
            if (!tableExists) {
                // 테이블이 없으면 빈 캐시로 시작 (마이그레이션이 아직 실행되지 않았을 수 있음)
                this.cache.clear();
                logger.warn('Anchor table does not exist yet, starting with empty cache');
                return;
            }
            const anchors = db.prepare(`
        SELECT agent_id, slot, memory_id
        FROM anchor
        ORDER BY agent_id, slot
      `).all();
            // 캐시 초기화
            this.cache.clear();
            // DB 데이터로 캐시 복원
            for (const anchor of anchors) {
                const agentId = anchor.agent_id;
                const slot = anchor.slot;
                const memoryId = anchor.memory_id;
                if (!this.cache.has(agentId)) {
                    this.cache.set(agentId, { A: null, B: null, C: null });
                }
                const agentCache = this.cache.get(agentId);
                agentCache[slot] = memoryId;
            }
            logger.info('Anchor cache restored', { agentCount: this.cache.size });
        }
        catch (error) {
            // 에러 발생 시 빈 캐시로 시작 (테이블이 없거나 다른 문제)
            this.cache.clear();
            logger.warn('Failed to restore anchor cache from DB, starting with empty cache', {
                error: error instanceof Error ? error.message : String(error)
            });
        }
    }
    /**
     * 캐시 업데이트 헬퍼 메서드
     * @param agentId - 에이전트 ID
     * @param slot - 슬롯
     * @param memoryId - 메모리 ID (null이면 제거)
     */
    updateCache(agentId, slot, memoryId) {
        if (!this.cache.has(agentId)) {
            this.cache.set(agentId, { A: null, B: null, C: null });
        }
        const agentCache = this.cache.get(agentId);
        agentCache[slot] = memoryId;
    }
    /**
     * 캐시에서 앵커 조회
     * @param agentId - 에이전트 ID
     * @param _slot - 슬롯 (선택적)
     * @returns 캐시된 앵커 정보
     */
    getCachedAnchor(agentId, _slot) {
        return this.cache.get(agentId);
    }
    /**
     * 캐시에서 특정 슬롯의 메모리 ID 조회
     * @param agentId - 에이전트 ID
     * @param slot - 슬롯
     * @returns 메모리 ID 또는 undefined
     */
    getCachedMemoryId(agentId, slot) {
        const cached = this.cache.get(agentId);
        return cached?.[slot];
    }
    /**
     * 캐시 삭제
     * @param agentId - 에이전트 ID
     */
    deleteCache(agentId) {
        this.cache.delete(agentId);
    }
}
//# sourceMappingURL=anchor-cache-service.js.map