import { flushAccountDigests } from "../notify/deliver";
import { releaseStatements } from "../metering/quota";
import { sendAccountBudgetAlerts } from "../metering/cron";
import { gaugeStatement, periodOf, resolveAccount } from "../metering/record";
import { sumScopeGauges } from "../metering/scope-gauges";
import { publishUsageLeaderboard } from "../metering/leaderboard";
import { accountDb } from "../scoped-db";
import { nextRunnerOffline, reportOfflineRunners } from "../notify/runners";
import { closeExpiredContainerSessions, EXPIRING_SESSION_DUE } from "../editor/container-meter";
import { accountTaskHelpers, mark, registerAccountTasks } from "./housekeeping";
import type { AccountTask } from "./housekeeping";
const { insertInto, writeTo, scalar } = accountTaskHelpers;
const MINUTE_MS = 60 * 1000;
/** Budget alerts look at the period summary a few minutes after usage moves, not on every event. */
const ALERT_DEBOUNCE_MS = 5 * MINUTE_MS;
const GAUGE_DEBOUNCE_MS = MINUTE_MS;
const RESERVATIONS_PER_RUN = 200;
/** Debounced tasks that follow the account's usage and gauges. */
const USAGE_MOVED = ["alerts", "usage-leaderboard"];
/** First instant of the month after `now` (UTC), when gauges carry into the new billing period. */
function nextPeriodStart(now: number): number {
const date = new Date(now);
return Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 1);
}
/**
* The account's deadline-driven work (see housekeeping.ts for the machinery). Gauge and usage writers mark
* `alerts`, so budget alerts follow usage without any sweep over accounts.
*/
const TASKS: AccountTask[] = [
{
name: "digest",
arms: insertInto("notification_digest_items"),
due: (sql) => scalar(sql, "SELECT MIN(flush_after) AS due FROM notification_digest_items"),
run: (accountId, now) => flushAccountDigests(accountId, now),
},
{