const DEFAULT_RETENTION_DAYS = 90;
const DELETE_BATCH_SIZE = 5e3;
const RECONCILE_TRAILING_DAYS = 7;
const metadata = {
  queue: "ai-token-usage-prune",
  id: "ai_assistant:token-usage-prune",
  concurrency: 1
};
function resolveRetentionDays() {
  const raw = process.env.AI_TOKEN_USAGE_EVENTS_RETENTION_DAYS;
  if (!raw) return DEFAULT_RETENTION_DAYS;
  const parsed = parseInt(raw.trim(), 10);
  return !isNaN(parsed) && parsed > 0 ? parsed : DEFAULT_RETENTION_DAYS;
}
async function pruneOldEvents(connection, cutoff, batchSize) {
  let totalDeleted = 0;
  for (; ; ) {
    const result = await connection.execute(
      `
      delete from ai_token_usage_events
      where id in (
        select id from ai_token_usage_events
        where created_at < ?
        limit ?
      )
      `,
      [cutoff, batchSize],
      "run"
    );
    const deleted = result?.affectedRows ?? result?.rowCount ?? 0;
    totalDeleted += deleted;
    if (deleted < batchSize) break;
  }
  return totalDeleted;
}
async function reconcileSessionCounts(connection, now, trailingDays) {
  const trailingStart = new Date(now);
  trailingStart.setUTCDate(trailingStart.getUTCDate() - trailingDays);
  const from = trailingStart.toISOString().slice(0, 10);
  const rows = await connection.execute(
    `
    select
      d.id,
      count(distinct e.session_id)::bigint as computed_session_count
    from ai_token_usage_daily d
    left join ai_token_usage_events e
      on e.tenant_id = d.tenant_id
      and e.agent_id = d.agent_id
      and e.model_id = d.model_id
      and date_trunc('day', e.created_at)::date = d.day
      and (
        (d.organization_id is null and e.organization_id is null)
        or (d.organization_id is not null and e.organization_id = d.organization_id)
      )
    where d.day >= ?::date
    group by d.id
    `,
    [from],
    "all"
  );
  if (!Array.isArray(rows) || rows.length === 0) return 0;
  let reconciled = 0;
  for (const row of rows) {
    const rowId = row.id;
    const computed = typeof row.computed_session_count === "string" ? parseInt(row.computed_session_count, 10) : row.computed_session_count ?? 0;
    await connection.execute(
      `update ai_token_usage_daily set session_count = ?, updated_at = now() where id = ?`,
      [computed, rowId],
      "run"
    );
    reconciled += 1;
  }
  return reconciled;
}
async function runTokenUsagePrune(options) {
  const now = options.now ?? /* @__PURE__ */ new Date();
  const retentionDays = options.retentionDays ?? resolveRetentionDays();
  const batchSize = options.batchSize ?? DELETE_BATCH_SIZE;
  const cutoff = new Date(now);
  cutoff.setUTCDate(cutoff.getUTCDate() - retentionDays);
  const connection = options.em.getConnection();
  let eventsDeleted = 0;
  try {
    eventsDeleted = await pruneOldEvents(connection, cutoff, batchSize);
  } catch (error) {
    console.error(
      "[ai-token-usage-prune] Failed to prune old events:",
      error instanceof Error ? error.message : error
    );
  }
  let dailyRowsReconciled = 0;
  try {
    dailyRowsReconciled = await reconcileSessionCounts(connection, now, RECONCILE_TRAILING_DAYS);
  } catch (error) {
    console.error(
      "[ai-token-usage-prune] Failed to reconcile session counts:",
      error instanceof Error ? error.message : error
    );
  }
  console.info(
    `[ai-token-usage-prune] Done. eventsDeleted=${eventsDeleted}, dailyRowsReconciled=${dailyRowsReconciled}, retentionDays=${retentionDays}.`
  );
  return { eventsDeleted, dailyRowsReconciled };
}
async function handle(_job, ctx) {
  const em = ctx.resolve("em");
  await runTokenUsagePrune({ em });
}
export {
  handle as default,
  metadata,
  runTokenUsagePrune
};
//# sourceMappingURL=ai-token-usage-prune.js.map
