/**
 * File-backed chat archive repository.
 *
 * Owns archive paths, manifest persistence, archived transcript files, and
 * rolling-summary markdown files for one chat session. Compaction policy stays
 * in the history module; archive file layout stays here.
 */
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
const ARCHIVE_MANIFEST_VERSION = 1;
export class FileChatArchiveRepository {
    stateRoot;
    sessionId;
    paths;
    constructor(args) {
        this.stateRoot = args.stateRoot;
        this.sessionId = args.sessionId;
        this.paths = FileChatArchiveRepository.derivePaths(args.stateRoot, args.sessionId);
    }
    derivePaths() {
        return this.paths;
    }
    ensureArchiveDir() {
        mkdirSync(this.paths.archivesDir, { recursive: true });
        return this.paths;
    }
    loadManifest() {
        if (!existsSync(this.paths.manifestPath)) {
            return FileChatArchiveRepository.emptyManifest(this.sessionId);
        }
        try {
            const parsed = JSON.parse(readFileSync(this.paths.manifestPath, 'utf8'));
            return FileChatArchiveRepository.parseManifest(parsed, this.sessionId);
        }
        catch {
            return FileChatArchiveRepository.emptyManifest(this.sessionId);
        }
    }
    saveManifest(manifest) {
        const paths = this.ensureArchiveDir();
        writeFileSync(paths.manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
    }
    writeMessagesJsonl(archiveId, messages) {
        const paths = this.ensureArchiveDir();
        const displayPath = `${paths.displayArchivesDir}/${archiveId}.jsonl`;
        const filePath = join(paths.archivesDir, `${archiveId}.jsonl`);
        const body = messages.map((message, index) => JSON.stringify({
            index,
            ...message,
        })).join('\n');
        writeFileSync(filePath, body ? `${body}\n` : '', 'utf8');
        return displayPath;
    }
    writeSummaryMarkdown(archiveId, summary) {
        const paths = this.ensureArchiveDir();
        const displayPath = `${paths.displayArchivesDir}/${archiveId}.summary.md`;
        const filePath = join(paths.archivesDir, `${archiveId}.summary.md`);
        writeFileSync(filePath, summary.endsWith('\n') ? summary : `${summary}\n`, 'utf8');
        return displayPath;
    }
    readSummaryMarkdown(path) {
        const filePath = FileChatArchiveRepository.resolveDisplayPath(path, this.stateRoot);
        if (!existsSync(filePath)) {
            return undefined;
        }
        try {
            return readFileSync(filePath, 'utf8');
        }
        catch {
            return undefined;
        }
    }
    static derivePaths(stateRoot, sessionId) {
        const sessionDir = join(stateRoot, 'chat-sessions', sessionId);
        const archivesDir = join(sessionDir, 'archives');
        return {
            sessionDir,
            archivesDir,
            manifestPath: join(archivesDir, 'manifest.json'),
            displaySessionDir: `.heddle/chat-sessions/${sessionId}`,
            displayArchivesDir: `.heddle/chat-sessions/${sessionId}/archives`,
        };
    }
    static createArchiveId(now = new Date()) {
        return `archive-${now.toISOString().replaceAll(':', '-')}`;
    }
    static appendManifestArchive(current, archive) {
        return {
            version: ARCHIVE_MANIFEST_VERSION,
            sessionId: current.sessionId,
            currentSummaryPath: archive.summaryPath,
            archives: [...current.archives, archive],
        };
    }
    static emptyManifest(sessionId) {
        return {
            version: ARCHIVE_MANIFEST_VERSION,
            sessionId,
            archives: [],
        };
    }
    static parseManifest(value, sessionId) {
        if (!value || typeof value !== 'object' || Array.isArray(value)) {
            return FileChatArchiveRepository.emptyManifest(sessionId);
        }
        const candidate = value;
        const archives = Array.isArray(candidate.archives) ?
            candidate.archives.flatMap((archive) => FileChatArchiveRepository.parseArchiveRecord(archive))
            : [];
        return {
            version: ARCHIVE_MANIFEST_VERSION,
            sessionId: typeof candidate.sessionId === 'string' ? candidate.sessionId : sessionId,
            currentSummaryPath: typeof candidate.currentSummaryPath === 'string' ? candidate.currentSummaryPath : undefined,
            archives,
        };
    }
    static parseArchiveRecord(value) {
        if (!value || typeof value !== 'object' || Array.isArray(value)) {
            return [];
        }
        const candidate = value;
        if (typeof candidate.id !== 'string'
            || typeof candidate.path !== 'string'
            || typeof candidate.summaryPath !== 'string'
            || typeof candidate.messageCount !== 'number'
            || typeof candidate.createdAt !== 'string') {
            return [];
        }
        return [{
                id: candidate.id,
                path: candidate.path,
                summaryPath: candidate.summaryPath,
                shortDescription: typeof candidate.shortDescription === 'string' ? candidate.shortDescription : undefined,
                messageCount: candidate.messageCount,
                createdAt: candidate.createdAt,
                summaryModel: typeof candidate.summaryModel === 'string' ? candidate.summaryModel : undefined,
            }];
    }
    static resolveDisplayPath(displayPath, stateRoot) {
        if (displayPath.startsWith('.heddle/')) {
            return join(stateRoot, displayPath.slice('.heddle/'.length));
        }
        return displayPath;
    }
}
//# sourceMappingURL=file-chat-archive-repository.js.map