import { accountDb } from "../scoped-db";
import { randomId } from "../crypto";
import type { MeterId } from "~/lib/meters";
import { periodOf } from "./record";
/** Gauge holds outlive the write until the storage rollup (every 5 minutes) has counted the new bytes. */
export const GAUGE_HOLD_TTL_MS = 10 * 60 * 1000;
/**
* BILL-P9: storage gauges only move when the cron rolls them up, so a read-only check let concurrent writes
* each see the same headroom. This holds `quantity` in one conditional INSERT (atomic in the account's object)
* that counts every unexpired hold on the gauge, so two writes can never both fit into the same space. Holds
* expire rather than settle; until then the stored bytes may count twice, which errs on the strict side.
*/
export async function holdGauge(accountId: string, meter: MeterId, quantity: number, cap: number): Promise<boolean> {
const now = Date.now();
const period = periodOf();
const result = await accountDb(accountId).run(
`INSERT INTO usage_reservations (id, account_id, meter, period, quantity, expires_at, created_at, nonce)
SELECT ?1, ?2, ?3, ?4, ?5, ?6, ?7, NULL
WHERE COALESCE((SELECT current FROM usage_gauges WHERE account_id = ?2 AND meter = ?3 AND period = ?4), 0)
+ COALESCE((SELECT SUM(quantity) FROM usage_reservations WHERE account_id = ?2 AND meter = ?3
AND expires_at > ?7), 0) + ?5 <= ?8`,
`gauge:${randomId()}`,
accountId,
meter,
period,
quantity,
now + GAUGE_HOLD_TTL_MS,
now,
cap,
);
return result.meta.changes > 0;
}