/**
 * Distillation Loop — stale Episodic Memory → Long-Term "episode-digest" 승급.
 *
 * 배경: Episodic Memory 가 무한히 누적되면 검색 노이즈. 30일+ 지난 에피소드는
 * "지금 이 순간 관련 가능성" 보다 "역사적 패턴" 가치가 커서, 디테일을 압축해
 * Long-Term 으로 옮기고 원본은 archive 하는 게 효율적.
 *
 * v1 설계 (LLM-less, 예측 가능):
 *   - LLM 호출 없이 기존 EpisodicEntry 의 title/summary/keyDecisions/topics 를
 *     구조적으로 결합해 LongTerm 'episode-digest' content 생성
 *   - 장점: 비용 0, 결정적·재현 가능, LM Studio 다운 시에도 동작
 *   - 단점: LLM 요약보다 농축도 낮음 — 추후 strict 모드에서 LLM 패스 추가 가능
 *
 * 원본 episode 처리: 두 가지 옵션 — 사용자 설정으로 결정.
 *   - 'mark-promoted' (기본): promoted=true 마킹만, 파일 보존. 검색에서 제외되나
 *     히스토리·디버깅용으로 디스크에 남음.
 *   - 'archive-file': promoted 마킹 + 파일을 memory/episodes/archive/ 로 이동.
 *     디스크 정리에 더 깔끔하나 복구 시 수동.
 */

import * as fs from 'fs';
import * as path from 'path';
import { EpisodicMemory } from './EpisodicMemory';
import { LongTermMemory } from './LongTermMemory';
import { EpisodicEntry } from './types';

export type DistillationArchiveMode = 'mark-promoted' | 'archive-file';

export interface DistillationOptions {
    /** 며칠 이상 지난 episode 를 대상으로. 기본 30. */
    ageThresholdDays: number;
    /** Archive 처리 방식. 기본 'mark-promoted'. */
    archiveMode: DistillationArchiveMode;
    /** 한 번에 처리할 최대 episode 수 (안전장치). 기본 50. */
    maxBatchSize: number;
}

export const DEFAULT_DISTILLATION_OPTIONS: DistillationOptions = {
    ageThresholdDays: 30,
    archiveMode: 'mark-promoted',
    maxBatchSize: 50,
};

export interface DistillationReport {
    candidateCount: number;
    promotedCount: number;
    archivedCount: number;
    longTermDigestIds: string[];
    skipped: { episodeId: string; reason: string }[];
    durationMs: number;
}

/**
 * Episode → LongTerm 'episode-digest' content 변환. 결정적·LLM 없음.
 */
function episodeToDigestContent(ep: EpisodicEntry): string {
    const date = new Date(ep.timestamp).toISOString().slice(0, 10);
    const parts: string[] = [];
    parts.push(`[${date}] ${ep.title}`);
    if (ep.summary && ep.summary.trim()) parts.push(`요약: ${ep.summary.trim()}`);
    if (ep.keyDecisions && ep.keyDecisions.length > 0) {
        parts.push(`결정: ${ep.keyDecisions.slice(0, 5).join(' · ')}`);
    }
    if (ep.topics && ep.topics.length > 0) {
        parts.push(`토픽: ${ep.topics.slice(0, 8).join(', ')}`);
    }
    return parts.join('\n');
}

/**
 * Distillation 실행 — stale episodes 를 LongTerm digest 로 승급 + archive.
 *
 * 호출자: `/memory distill` 슬래시 명령 + 세션 종료 시 auto-trigger (선택).
 */
export function distillStaleEpisodes(
    episodicMemory: EpisodicMemory,
    longTermMemory: LongTermMemory,
    brainPath: string,
    options: Partial<DistillationOptions> = {},
): DistillationReport {
    const opts: DistillationOptions = { ...DEFAULT_DISTILLATION_OPTIONS, ...options };
    const start = Date.now();
    const report: DistillationReport = {
        candidateCount: 0,
        promotedCount: 0,
        archivedCount: 0,
        longTermDigestIds: [],
        skipped: [],
        durationMs: 0,
    };

    const candidates = episodicMemory.findStaleEpisodes(opts.ageThresholdDays).slice(0, opts.maxBatchSize);
    report.candidateCount = candidates.length;

    if (candidates.length === 0) {
        report.durationMs = Date.now() - start;
        return report;
    }

    const archiveDir = path.join(brainPath, 'memory', 'episodes', 'archive');
    if (opts.archiveMode === 'archive-file') {
        try { fs.mkdirSync(archiveDir, { recursive: true }); } catch { /* ignore */ }
    }

    for (const ep of candidates) {
        try {
            // 1. LongTerm digest entry 생성. confidence 약간 낮춤 (압축 손실 반영).
            const digestContent = episodeToDigestContent(ep);
            const digest = longTermMemory.addEntry(
                'episode-digest',
                digestContent,
                `episodic:${ep.id}`,
                0.7,
            );
            report.longTermDigestIds.push(digest.id);

            // 2. 원본 episode 처리.
            const marked = episodicMemory.markPromoted(ep.id, digest.id);
            if (!marked) {
                report.skipped.push({ episodeId: ep.id, reason: 'markPromoted failed (file not found)' });
                continue;
            }

            if (opts.archiveMode === 'archive-file') {
                // 파일 위치 찾아서 archive 디렉터리로 이동.
                const moved = tryMoveEpisodeFileToArchive(ep.id, path.join(brainPath, 'memory', 'episodes'), archiveDir);
                if (moved) report.archivedCount++;
            }
            report.promotedCount++;
        } catch (e: any) {
            report.skipped.push({ episodeId: ep.id, reason: e?.message || String(e) });
        }
    }

    report.durationMs = Date.now() - start;
    return report;
}

function tryMoveEpisodeFileToArchive(episodeId: string, episodeDir: string, archiveDir: string): boolean {
    try {
        const files = fs.readdirSync(episodeDir).filter((f) => f.endsWith('.json'));
        for (const file of files) {
            const full = path.join(episodeDir, file);
            try {
                const raw = fs.readFileSync(full, 'utf-8');
                const parsed = JSON.parse(raw) as EpisodicEntry;
                if (parsed.id === episodeId) {
                    fs.renameSync(full, path.join(archiveDir, file));
                    return true;
                }
            } catch { /* skip */ }
        }
    } catch { /* ignore */ }
    return false;
}

/**
 * Distillation 마지막 실행 시각을 저장·조회 — 자동 트리거가 *너무 자주* 안 돌도록.
 * brainPath 의 marker 파일 사용 (vscode.globalState 안 쓰는 이유: 메모리 인프라가
 * BrainProfile-scoped 라 brain 디렉터리에 두는 게 일관성 있음).
 */
const MARKER_FILE = 'distillation_last_run.json';

export interface DistillationMarker {
    timestamp: number;
    report?: Partial<DistillationReport>;
}

export function getLastDistillationRun(brainPath: string): DistillationMarker | null {
    try {
        const fp = path.join(brainPath, 'memory', MARKER_FILE);
        if (!fs.existsSync(fp)) return null;
        return JSON.parse(fs.readFileSync(fp, 'utf-8')) as DistillationMarker;
    } catch { return null; }
}

export function recordDistillationRun(brainPath: string, report: DistillationReport): void {
    try {
        const dir = path.join(brainPath, 'memory');
        fs.mkdirSync(dir, { recursive: true });
        const marker: DistillationMarker = {
            timestamp: Date.now(),
            report: {
                candidateCount: report.candidateCount,
                promotedCount: report.promotedCount,
                archivedCount: report.archivedCount,
                durationMs: report.durationMs,
            },
        };
        fs.writeFileSync(path.join(dir, MARKER_FILE), JSON.stringify(marker, null, 2), 'utf-8');
    } catch { /* ignore */ }
}

/** 자동 트리거 게이트 — 마지막 실행 후 N일 경과 시 true. */
export function shouldAutoDistill(brainPath: string, intervalDays: number): boolean {
    const last = getLastDistillationRun(brainPath);
    if (!last) return true;
    const elapsed = (Date.now() - last.timestamp) / (1000 * 60 * 60 * 24);
    return elapsed >= intervalDays;
}
