import { all } from "../db";
import { accountDb, stmt } from "../scoped-db";
import { invalidateUnread } from "../user-inbox";
import { randomId } from "../crypto";
import { publicUrl } from "../events/emit";
import { meterDef } from "~/lib/meters";
import { crossedThreshold } from "~/lib/plans";
import { periodOf } from "./record";
import { usageSummary } from "./summary";
type AlertCandidate = { subject: string; threshold: number; title: string };
/** Pure: which thresholds a summary has crossed (per included meter, plus the spending limit). */
export function alertCandidates(summary: Awaited<ReturnType<typeof usageSummary>>): AlertCandidate[] {
const out: AlertCandidate[] = [];
for (const meter of summary.meters) {
const limit = meter.hardCap ?? meter.included;
if (limit === null || limit <= 0) continue;
const threshold = crossedThreshold(meter.quantity, limit);
if (threshold === null) continue;
const label = meterDef(meter.meter)?.label ?? meter.meter;
out.push({ subject: meter.meter, threshold, title: `${label}: ${threshold}% of this month's allowance used` });
}
if (summary.spendingLimitCents !== null && summary.spendingLimitCents > 0) {
const threshold = crossedThreshold(summary.totalCents, summary.spendingLimitCents);
if (threshold !== null) {
out.push({ subject: "spend", threshold, title: `Spending: ${threshold}% of your spending limit used` });
}
}
return out;
}
async function recipients(accountId: string): Promise<{ ids: string[]; url: string }> {
const account = await all<{ kind: string; name: string }>("SELECT kind, name FROM accounts WHERE id = ?", accountId);
if (account[0]?.kind === "org") {
const owners = await all<{ user_id: string }>(
"SELECT user_id FROM org_members WHERE org_id = ? AND role = 'owner' LIMIT 50",
accountId,
);
return { ids: owners.map((row) => row.user_id), url: publicUrl(`/orgs/${account[0].name}/billing`) };