/**
 * AI usage API — cost-control Phase 0.
 *
 *   GET /api/admin/ai-usage — spend rollup from the AI usage ledger.
 *
 * Owner-only (fail closed outside local open mode, matching audit-api).
 * Read-only: the ledger is populated by the inference hooks; nothing here
 * mutates it.
 *
 * Query params:
 *   since, until      ISO timestamp or epoch ms
 *   feature           filter to one feature ('chat' | 'title' | 'caption' | …)
 *   group_by          CSV of feature|model|day (default "feature,model,day");
 *                     unknown tokens → 400
 *   include_events=1  append recent raw rows (paginated via limit/offset)
 *   limit, offset     raw-event pagination (limit clamped 1–500)
 *
 * Response: { totals, groups, events? } — token sums per class, costUsd, and
 * cacheHitRate (= cacheRead / (input + cacheRead)) per group and overall.
 */
import { isWorkspaceOwnerOrOpenMode } from '../../services/access-control.js';
function requireOwner(req, authService) {
    // FAIL CLOSED outside local open mode (matches audit-api / secrets-api).
    return isWorkspaceOwnerOrOpenMode(req.user, authService);
}
function parseTs(v) {
    if (!v)
        return undefined;
    const n = Number(v);
    if (Number.isFinite(n) && n > 0)
        return n;
    const d = Date.parse(v);
    return Number.isFinite(d) ? d : undefined;
}
const VALID_GROUP_BY = new Set(['feature', 'model', 'day']);
export async function registerAiUsageApiRoutes(fastify, authService, ledger) {
    fastify.get('/api/admin/ai-usage', async (req, reply) => {
        if (!requireOwner(req, authService)) {
            return reply.status(403).send({ error: 'Workspace owner access required' });
        }
        const q = req.query ?? {};
        const groupByRaw = (q.group_by ?? 'feature,model,day')
            .split(',')
            .map((s) => s.trim())
            .filter(Boolean);
        const invalid = groupByRaw.filter((g) => !VALID_GROUP_BY.has(g));
        if (invalid.length) {
            return reply.status(400).send({ error: `Invalid group_by value(s): ${invalid.join(', ')}. Valid: feature, model, day.` });
        }
        const groupBy = groupByRaw;
        const filter = {
            since: parseTs(q.since),
            until: parseTs(q.until),
            feature: q.feature || undefined,
        };
        const totals = ledger.queryTotals(filter);
        const groups = ledger.queryRollup({ ...filter, groupBy });
        const body = { totals, groups };
        if (q.include_events === '1' || q.include_events === 'true') {
            const limit = Math.min(Math.max(parseInt(q.limit ?? '50', 10) || 50, 1), 500);
            const offset = Math.max(parseInt(q.offset ?? '0', 10) || 0, 0);
            const { total, items } = ledger.queryRecent({ ...filter, limit, offset });
            body.events = { total, limit, offset, items };
        }
        return reply.send(body);
    });
}
//# sourceMappingURL=ai-usage-api.js.map