import { readFile, writeFile, rename, unlink } from 'fs/promises';
import { tmpdir } from 'os';
import { join } from 'path';
import { getSessionsFilePath, getSocketPath, fileExists, ensureDir, getMcpcHome, isProcessAlive, } from './utils.js';
import { withFileLock } from './file-lock.js';
import { createLogger } from './logger.js';
import { ClientError } from './errors.js';
import { removeKeychainSessionHeaders, removeKeychainProxyBearerToken } from './auth/keychain.js';
const logger = createLogger('sessions');
async function loadSessionsInternal() {
    const filePath = getSessionsFilePath();
    if (!(await fileExists(filePath))) {
        logger.debug('Sessions file does not exist, returning empty sessions');
        return { sessions: {} };
    }
    try {
        const content = await readFile(filePath, 'utf-8');
        const storage = JSON.parse(content);
        if (!storage.sessions || typeof storage.sessions !== 'object') {
            logger.warn('Invalid sessions file format, returning empty sessions');
            return { sessions: {} };
        }
        return storage;
    }
    catch (error) {
        logger.warn(`Failed to load sessions: ${error.message}`);
        return { sessions: {} };
    }
}
async function saveSessionsInternal(storage) {
    const filePath = getSessionsFilePath();
    await ensureDir(getMcpcHome());
    const tempFile = join(tmpdir(), `mcpc-sessions-${Date.now()}-${process.pid}.json`);
    try {
        const content = JSON.stringify(storage, null, 2);
        await writeFile(tempFile, content, { encoding: 'utf-8', mode: 0o600 });
        await rename(tempFile, filePath);
        logger.debug('Sessions saved successfully');
    }
    catch (error) {
        try {
            await unlink(tempFile);
        }
        catch {
        }
        throw new ClientError(`Failed to save sessions: ${error.message}`);
    }
}
const SESSIONS_DEFAULT_CONTENT = JSON.stringify({ sessions: {} }, null, 2);
export async function loadSessions() {
    const filePath = getSessionsFilePath();
    return withFileLock(filePath, loadSessionsInternal, SESSIONS_DEFAULT_CONTENT);
}
export async function getSession(sessionName) {
    const storage = await loadSessions();
    return storage.sessions[sessionName];
}
export async function saveSession(sessionName, sessionData) {
    const filePath = getSessionsFilePath();
    return withFileLock(filePath, async () => {
        const storage = await loadSessionsInternal();
        const now = new Date().toISOString();
        const existingSession = storage.sessions[sessionName];
        storage.sessions[sessionName] = {
            name: sessionName,
            ...sessionData,
            createdAt: existingSession?.createdAt || now,
        };
        await saveSessionsInternal(storage);
        logger.debug(`Session ${sessionName} saved`);
    }, SESSIONS_DEFAULT_CONTENT);
}
export async function updateSession(sessionName, updates) {
    const filePath = getSessionsFilePath();
    return withFileLock(filePath, async () => {
        const storage = await loadSessionsInternal();
        const existingSession = storage.sessions[sessionName];
        if (!existingSession) {
            throw new ClientError(`Session not found: ${sessionName}`);
        }
        const merged = {
            ...existingSession,
            ...updates,
            name: sessionName,
        };
        if (updates.notifications) {
            merged.notifications = {
                ...existingSession.notifications,
                tools: { ...existingSession.notifications?.tools, ...updates.notifications.tools },
                prompts: { ...existingSession.notifications?.prompts, ...updates.notifications.prompts },
                resources: { ...existingSession.notifications?.resources, ...updates.notifications.resources },
            };
        }
        storage.sessions[sessionName] = merged;
        await saveSessionsInternal(storage);
        logger.debug(`Session ${sessionName} updated`);
    }, SESSIONS_DEFAULT_CONTENT);
}
export async function deleteSession(sessionName) {
    const filePath = getSessionsFilePath();
    return withFileLock(filePath, async () => {
        const storage = await loadSessionsInternal();
        if (!storage.sessions[sessionName]) {
            throw new ClientError(`Session not found: ${sessionName}`);
        }
        delete storage.sessions[sessionName];
        await saveSessionsInternal(storage);
        try {
            await removeKeychainSessionHeaders(sessionName);
            logger.debug(`Deleted headers from keychain for session: ${sessionName}`);
        }
        catch {
        }
        try {
            await removeKeychainProxyBearerToken(sessionName);
            logger.debug(`Deleted proxy bearer token from keychain for session: ${sessionName}`);
        }
        catch {
        }
        logger.debug(`Session ${sessionName} deleted`);
    }, SESSIONS_DEFAULT_CONTENT);
}
export async function sessionExists(sessionName) {
    const storage = await loadSessions();
    return sessionName in storage.sessions;
}
export async function consolidateSessions(cleanExpired) {
    const result = {
        crashedBridges: 0,
        expiredSessions: 0,
        sessions: {},
    };
    const filePath = getSessionsFilePath();
    const defaultContent = JSON.stringify({ sessions: {} }, null, 2);
    await withFileLock(filePath, async () => {
        const storage = await loadSessionsInternal();
        let hasChanges = false;
        for (const [name, session] of Object.entries(storage.sessions)) {
            if (!session) {
                logger.debug(`Missing record for session: ${name}`);
                hasChanges = true;
                delete storage.sessions[name];
                continue;
            }
            if (cleanExpired && session.status === 'expired') {
                logger.debug(`Removing expired session: ${name}`);
                delete storage.sessions[name];
                result.expiredSessions++;
                hasChanges = true;
                try {
                    await removeKeychainSessionHeaders(name);
                    logger.debug(`Deleted headers from keychain for session: ${name}`);
                }
                catch {
                }
                try {
                    await removeKeychainProxyBearerToken(name);
                    logger.debug(`Deleted proxy bearer token from keychain for session: ${name}`);
                }
                catch {
                }
                if (process.platform !== 'win32') {
                    const socketPath = getSocketPath(name);
                    try {
                        await unlink(socketPath);
                        logger.debug(`Removed stale socket: ${socketPath}`);
                    }
                    catch {
                    }
                }
                continue;
            }
            if (session.pid && !isProcessAlive(session.pid)) {
                logger.debug(`Clearing crashed bridge PID for session: ${name} (PID: ${session.pid})`);
                delete session.pid;
                hasChanges = true;
                if (session.status !== 'crashed' && session.status !== 'expired') {
                    session.status = 'crashed';
                    result.crashedBridges++;
                }
            }
            else if (!session.pid && session.status !== 'crashed' && session.status !== 'expired') {
                session.status = 'crashed';
                result.crashedBridges++;
                hasChanges = true;
            }
        }
        if (hasChanges) {
            await saveSessionsInternal(storage);
        }
        result.sessions = storage.sessions;
    }, defaultContent);
    return result;
}
//# sourceMappingURL=sessions.js.map