import { env } from "cloudflare:workers";
import { accountDb, repoDb, type ScopedStatement } from "../scoped-db";
import { claimScratchUsage, eventStatements, periodEnd, periodOf, resolveAccount } from "../metering/record";
import { billingSettings, hardCap, siteCapKey } from "../metering/quota";
import { scratchSiteAccount } from "../metering/attribution";
import type { MeterId } from "~/lib/meters";
import { overFreeTier, recordUsage, usageDay } from "./usage";
import type { SiteMeta } from "./config";
/**
* The only place Sites talks to metering. Per-request counters stay in `site_usage` (Durable Object counters,
* flushed to D1) so the hot path never writes D1; `rollupScopeSiteUsage` folds each finished UTC day into the shared
* meters, billed to the owner or to `scratch:site:<id>` (redirected to whoever claimed it).
*/
type Metered = Pick<SiteMeta, "id" | "ownerId" | "scratch" | "repoId">;
const ROLLUP_PAGE = 500;
const ROLLUP_MAX_PAGES = 40;
const CAP_FLAG_CACHE_SECONDS = 60;
/** Every hosting meter whose hard cap stops an owner's Sites from serving. */
export const HOSTING_METERS: MeterId[] = ["compute.site_requests", "compute.site_cpu", "network.sites"];
export const ROLLUP_DAYS = 3;
/** One request and its response bytes, after the response finished streaming. */
export function meterSiteRequest(ctx: ExecutionContext, site: Metered, input: { bytes: number; rayId: string }): void {
ctx.waitUntil(recordUsage(site, { requests: 1, bytesOut: input.bytes }));
}
/** CPU time of a dynamic site's isolate, from its tail events. */
export async function meterSiteCpu(site: Metered, cpuMs: number): Promise<void> {
await recordUsage(site, { cpuMs });
}
/** Before serving a scratch site: false once its free tier is used up (the dispatcher answers 429). */
export async function scratchWithinQuota(site: Metered): Promise<boolean> {
return !site.scratch || !(await overFreeTier(site.id));
}
/** Set by the admin "suspend hosting" action; unlike the cap flag, plan changes never clear it. */
export function hostingSuspendedKey(accountId: string): string {