import os from 'node:os';
import path from 'node:path';
import { readFile } from 'node:fs/promises';
import { sessionsDb } from '../../../../modules/database/index.js';
import { buildLookupMap, extractFirstValidJsonlData, findFilesRecursivelyCreatedAfter, normalizeSessionName, readFileTimestamps, } from '../../../../shared/utils.js';
/**
 * Session indexer for Codex transcript artifacts.
 */
export class CodexSessionSynchronizer {
    provider = 'codex';
    codexHome = path.join(os.homedir(), '.codex');
    /**
     * Scans ~/.codex/sessions and upserts discovered sessions into DB.
     */
    async synchronize(since, ownerUserId) {
        const nameMap = await buildLookupMap(path.join(this.codexHome, 'session_index.jsonl'), 'id', 'thread_name');
        const files = await findFilesRecursivelyCreatedAfter(path.join(this.codexHome, 'sessions'), '.jsonl', since ?? null);
        let processed = 0;
        for (const filePath of files) {
            const parsed = await this.processSessionFile(ownerUserId, filePath, nameMap);
            if (!parsed) {
                continue;
            }
            const existingSession = sessionsDb.getSessionById(ownerUserId, parsed.sessionId);
            if (existingSession) {
                // If session name is untitled and we now have a name, update it
                if (existingSession.custom_name === 'Untitled Codex Session' && parsed.sessionName && parsed.sessionName !== 'Untitled Codex Session') {
                    sessionsDb.updateSessionCustomName(ownerUserId, parsed.sessionId, parsed.sessionName);
                }
            }
            const timestamps = await readFileTimestamps(filePath);
            sessionsDb.createSession(ownerUserId, parsed.sessionId, this.provider, parsed.projectPath, parsed.sessionName, timestamps.createdAt, timestamps.updatedAt, filePath);
            processed += 1;
        }
        return processed;
    }
    /**
     * Parses and upserts one Codex session JSONL file.
     */
    async synchronizeFile(filePath, ownerUserId) {
        if (!filePath.endsWith('.jsonl')) {
            return null;
        }
        const nameMap = await buildLookupMap(path.join(this.codexHome, 'session_index.jsonl'), 'id', 'thread_name');
        const parsed = await this.processSessionFile(ownerUserId, filePath, nameMap);
        if (!parsed) {
            return null;
        }
        const timestamps = await readFileTimestamps(filePath);
        return sessionsDb.createSession(ownerUserId, parsed.sessionId, this.provider, parsed.projectPath, parsed.sessionName, timestamps.createdAt, timestamps.updatedAt, filePath);
    }
    /**
     * Extracts session metadata from one Codex JSONL session file.
     */
    async processSessionFile(ownerUserId, filePath, nameMap) {
        const parsed = await extractFirstValidJsonlData(filePath, (rawData) => {
            const data = rawData;
            const payload = data.payload;
            const sessionId = typeof payload?.id === 'string' ? payload.id : undefined;
            const projectPath = typeof payload?.cwd === 'string' ? payload.cwd : undefined;
            if (!sessionId || !projectPath) {
                return null;
            }
            return {
                sessionId,
                projectPath,
            };
        });
        if (!parsed) {
            return null;
        }
        const existingSession = sessionsDb.getSessionById(ownerUserId, parsed.sessionId);
        const existingSessionName = existingSession?.custom_name;
        if (existingSessionName && existingSessionName !== 'Untitled Codex Session') {
            return {
                ...parsed,
                sessionName: normalizeSessionName(existingSessionName, 'Untitled Codex Session'),
            };
        }
        let sessionName = nameMap.get(parsed.sessionId);
        if (!sessionName) {
            sessionName = await this.extractLastAgentMessageFromEnd(filePath);
        }
        return {
            ...parsed,
            sessionName: normalizeSessionName(sessionName, 'Untitled Codex Session'),
        };
    }
    async extractLastAgentMessageFromEnd(filePath) {
        try {
            const content = await readFile(filePath, 'utf8');
            const lines = content.split(/\r?\n/);
            for (let index = lines.length - 1; index >= 0; index -= 1) {
                const line = lines[index]?.trim();
                if (!line) {
                    continue;
                }
                let parsed;
                try {
                    parsed = JSON.parse(line);
                }
                catch {
                    continue;
                }
                const data = parsed;
                const eventType = typeof data.type === 'string' ? data.type : undefined;
                const payload = data.payload;
                const payloadType = typeof payload?.type === 'string' ? payload.type : undefined;
                const lastAgentMessage = typeof payload?.last_agent_message === 'string'
                    ? payload.last_agent_message
                    : undefined;
                if (eventType === 'event_msg' && payloadType === 'task_complete' && lastAgentMessage?.trim()) {
                    return lastAgentMessage;
                }
            }
        }
        catch {
            // Ignore missing/unreadable files so sync can continue.
        }
        return undefined;
    }
}
//# sourceMappingURL=codex-session-synchronizer.provider.js.map