/**
 * Reflection Store — 업무 turn 회고 기록 + Failure Pattern 집계.
 *
 * Self-Evolving OS 마스터 플랜 Phase 1 / Track 2-4 (Reflection Engine v1) +
 * Phase 3 / Track 3-6 (Failure Pattern DB v1 시드). 신뢰 조건 T5
 * "같은 실수를 반복하지 않는다" 의 데이터 기반.
 *
 * v1 은 결정론적 신호만 기록 (LLM 회고 질문은 후속 증분):
 *   업무 turn 종료 → {업무유형, 확신도, 누락 요소, 에스컬레이션 여부, Critic 이슈 수}
 *   를 <brain>/.astra/growth/reflections.jsonl 에 append.
 *
 * 이 파일이 쌓이면:
 *   - summarizeFailurePatterns() → "회의록·기한 누락 N회" 류 반복 실수 집계
 *   - formatGrowthReport() → 기간별 확신도/누락률 추이 = *성장세 그래프의 원천*
 */

import * as fs from 'fs';
import * as path from 'path';

export const REFLECTIONS_REL_PATH = path.join('.astra', 'growth', 'reflections.jsonl');

export interface ReflectionRecord {
    /** ISO timestamp. */
    ts: string;
    taskId: string | null;
    taskLabel: string | null;
    confidenceScore: number;
    confidenceBand: string;
    /** 커버리지 누락 요소 label 목록. */
    missing: string[];
    escalated: boolean;
    /** Critic 검수가 돌았으면 발견 이슈 수, 안 돌았으면 null. */
    criticIssues: number | null;
    /** 요청 미리보기 (디버그·회고용, 120자). */
    promptPreview: string;

    // ── Decision Journal v1 (Track 3-7) — "왜 이 확신도/판단이었나" 역추적 필드 ──
    /** 확신도 기여 요인 label 목록 (confidenceEngine factors). */
    factors?: string[];
    /** 답변에 쓰인 상위 출처 title (citation/selfCheckSources 기준). */
    usedSources?: string[];

    // ── Gap Detector v1 (Track 3-2) — Need Engine 입력 신호 ──
    /** 검색 그라운딩: 청크 수·최고 score. */
    retrieval?: { chunkCount: number; topScore: number };
    /** 검색 근거 없이/약하게 수행한 업무 turn (지식 갭 신호). */
    weakGrounding?: boolean;
    /** 갭 심각도 (none/low/medium/high). */
    gapSeverity?: string;
}

/** 회고 1건 append — 실패해도 throw 하지 않음 (회고가 turn 을 막으면 안 됨). */
export function appendReflection(brainPath: string, record: ReflectionRecord): boolean {
    try {
        if (!brainPath) return false;
        const file = path.join(brainPath, REFLECTIONS_REL_PATH);
        fs.mkdirSync(path.dirname(file), { recursive: true });
        fs.appendFileSync(file, JSON.stringify(record) + '\n', 'utf8');
        return true;
    } catch {
        return false;
    }
}

/** 회고 로드 — 깨진 줄은 무시. limit 은 *최근* N건. */
export function loadReflections(brainPath: string, limit?: number): ReflectionRecord[] {
    try {
        const file = path.join(brainPath, REFLECTIONS_REL_PATH);
        if (!fs.existsSync(file)) return [];
        const lines = fs.readFileSync(file, 'utf8').split('\n').filter((l) => l.trim());
        const records: ReflectionRecord[] = [];
        for (const line of lines) {
            try {
                const obj = JSON.parse(line);
                if (obj && typeof obj.ts === 'string') records.push(obj as ReflectionRecord);
            } catch { /* skip broken line */ }
        }
        return limit && limit > 0 ? records.slice(-limit) : records;
    } catch {
        return [];
    }
}

export interface FailurePattern {
    taskId: string;
    taskLabel: string;
    element: string;
    count: number;
}

/**
 * Failure Pattern 집계 — (업무유형 × 누락 요소) 별 반복 횟수, 많은 순.
 * "시장규모 누락 27회" 류의 반복 실수를 수치로 노출 (설계서 12장).
 */
export function summarizeFailurePatterns(records: ReflectionRecord[]): FailurePattern[] {
    const counts = new Map<string, FailurePattern>();
    for (const r of records) {
        if (!r.taskId) continue;
        for (const el of r.missing || []) {
            const key = `${r.taskId}::${el}`;
            const cur = counts.get(key);
            if (cur) cur.count++;
            else counts.set(key, { taskId: r.taskId, taskLabel: r.taskLabel || r.taskId, element: el, count: 1 });
        }
    }
    return Array.from(counts.values()).sort((a, b) => b.count - a.count);
}

/**
 * 반복 실수 경고 — 같은 (업무 × 요소) 누락이 threshold 회 이상이면 해당 요소를
 * 시스템 프롬프트 강조 대상으로 반환. Requirement Graph 블록이 이걸 받아
 * "특히 자주 누락되는 요소" 로 표시 (T5 루프의 첫 닫힘).
 */
export function recurrentMisses(records: ReflectionRecord[], taskId: string, threshold = 3): string[] {
    return summarizeFailurePatterns(records)
        .filter((p) => p.taskId === taskId && p.count >= threshold)
        .map((p) => p.element);
}

/** 기간(주) 단위 성장 리포트 — 확신도 평균·누락률 추이. */
export function formatGrowthReport(records: ReflectionRecord[]): string {
    if (records.length === 0) return '# 성장 리포트\n\n기록 없음 — 업무 turn 이 쌓이면 추이가 표시됩니다.\n';

    // 주 단위 버킷 (ISO week 근사 — ts 앞 10자의 날짜 기준 7일 묶음).
    const byWeek = new Map<string, ReflectionRecord[]>();
    for (const r of records) {
        const d = new Date(r.ts);
        if (isNaN(d.getTime())) continue;
        const weekStart = new Date(d);
        weekStart.setDate(d.getDate() - d.getDay()); // 일요일 기준
        const key = weekStart.toISOString().slice(0, 10);
        const arr = byWeek.get(key) || [];
        arr.push(r);
        byWeek.set(key, arr);
    }

    const lines: string[] = [];
    lines.push('# ASTRA 성장 리포트 (Reflection 기반)');
    lines.push('');
    lines.push(`총 업무 turn: ${records.length}`);
    lines.push('');
    lines.push('| 주 (시작일) | 업무 수 | 평균 확신도 | 요소 누락률 | 에스컬레이션 |');
    lines.push('|---|---|---|---|---|');
    const weeks = Array.from(byWeek.keys()).sort();
    for (const w of weeks) {
        const rs = byWeek.get(w)!;
        const avgConf = rs.reduce((s, r) => s + (r.confidenceScore || 0), 0) / rs.length;
        const missRate = rs.filter((r) => (r.missing || []).length > 0).length / rs.length;
        const escCount = rs.filter((r) => r.escalated).length;
        lines.push(`| ${w} | ${rs.length} | ${avgConf.toFixed(0)} | ${(missRate * 100).toFixed(0)}% | ${escCount} |`);
    }
    lines.push('');
    lines.push('## 반복 실수 Top (Failure Patterns)');
    const patterns = summarizeFailurePatterns(records).slice(0, 10);
    if (patterns.length === 0) {
        lines.push('- 없음');
    } else {
        for (const p of patterns) lines.push(`- ${p.taskLabel} · **${p.element}** 누락 ${p.count}회`);
    }
    lines.push('');
    return lines.join('\n');
}
