import { createLogger } from '@4runr/shared';
import { getPersistenceMode } from '../config/persistence.js';
import { getPrismaClient } from '../db/prisma.js';
import { getRunStore } from './index.js';
const logger = createLogger('Gateway:RunRetention');
const TERMINAL_STATUSES = ['completed', 'failed', 'killed'];
export function getRunRetentionConfig() {
    const maxCountRaw = process.env['RUN_RETENTION_MAX_COUNT'] ??
        (process.env['NODE_ENV'] === 'production' ? '500' : '100');
    const maxAgeRaw = parseInt(process.env['RUN_RETENTION_MAX_AGE_DAYS'] ?? '0', 10);
    const maxCountParsed = parseInt(maxCountRaw, 10);
    return {
        enabled: process.env['RUN_RETENTION_ENABLED'] !== 'false',
        maxCount: Number.isFinite(maxCountParsed) && maxCountParsed >= 0 ? maxCountParsed : 100,
        maxAgeDays: Number.isFinite(maxAgeRaw) && maxAgeRaw >= 0 ? maxAgeRaw : 0,
    };
}
export function isTerminalRunForRetention(status) {
    return TERMINAL_STATUSES.includes(status);
}
export function scheduleRunRetentionEnforcement() {
    setImmediate(() => {
        enforceRunRetention().catch((err) => {
            const msg = err instanceof Error ? err.message : String(err);
            logger.warn('Run retention enforcement failed', { error: msg });
        });
    });
}
export async function enforceRunRetention(config = getRunRetentionConfig()) {
    if (!config.enabled) {
        return { deletedByCount: 0, deletedByAge: 0, totalAfter: 0 };
    }
    const mode = getPersistenceMode();
    if (mode === 'memory') {
        return enforceMemoryRetention(config);
    }
    return enforcePostgresRetention(config);
}
async function enforceMemoryRetention(config) {
    const store = getRunStore();
    const all = await store.listRuns();
    let deletedByAge = 0;
    let deletedByCount = 0;
    if (config.maxAgeDays > 0) {
        const cutoff = Date.now() - config.maxAgeDays * 24 * 60 * 60 * 1000;
        for (const run of all) {
            if (!isTerminalRunForRetention(run.status))
                continue;
            if (new Date(run.createdAt).getTime() < cutoff) {
                await store.deleteRun(run.id);
                deletedByAge++;
            }
        }
    }
    const remaining = await store.listRuns();
    if (config.maxCount > 0 && remaining.length > config.maxCount) {
        const terminal = remaining
            .filter((r) => isTerminalRunForRetention(r.status))
            .sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
        const excess = remaining.length - config.maxCount;
        const toDelete = terminal.slice(0, excess);
        for (const run of toDelete) {
            await store.deleteRun(run.id);
            deletedByCount++;
        }
    }
    const totalAfter = (await store.listRuns()).length;
    logIfDeleted(deletedByCount, deletedByAge, totalAfter, config);
    return { deletedByCount, deletedByAge, totalAfter };
}
async function enforcePostgresRetention(config) {
    const prisma = await getPrismaClient();
    let deletedByAge = 0;
    let deletedByCount = 0;
    const terminalWhere = {
        status: { in: ['COMPLETED', 'FAILED', 'KILLED'] },
    };
    if (config.maxAgeDays > 0) {
        const cutoff = new Date(Date.now() - config.maxAgeDays * 24 * 60 * 60 * 1000);
        const ageResult = await prisma.run.deleteMany({
            where: {
                ...terminalWhere,
                createdAt: { lt: cutoff },
            },
        });
        deletedByAge = ageResult.count;
    }
    if (config.maxCount > 0) {
        const total = await prisma.run.count();
        if (total > config.maxCount) {
            const excess = total - config.maxCount;
            const oldest = await prisma.run.findMany({
                where: terminalWhere,
                orderBy: { createdAt: 'asc' },
                take: excess,
                select: { id: true },
            });
            if (oldest.length > 0) {
                const countResult = await prisma.run.deleteMany({
                    where: { id: { in: oldest.map((r) => r.id) } },
                });
                deletedByCount = countResult.count;
            }
        }
    }
    const totalAfter = await prisma.run.count();
    logIfDeleted(deletedByCount, deletedByAge, totalAfter, config);
    return { deletedByCount, deletedByAge, totalAfter };
}
function logIfDeleted(deletedByCount, deletedByAge, totalAfter, config) {
    if (deletedByCount > 0 || deletedByAge > 0) {
        logger.info('Run retention applied', {
            deletedByCount,
            deletedByAge,
            totalAfter,
            maxCount: config.maxCount,
            maxAgeDays: config.maxAgeDays,
        });
    }
}
export function sortRunsOldestFirst(runs) {
    return [...runs].sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
}
//# sourceMappingURL=run-retention.js.map