import {
  recordAudit
} from "./chunk-JXGS7P5R.js";
import {
  COMMUNITY_LIMITS,
  hasFeature,
  requireFeature
} from "./chunk-2N2KL4KM.js";
import {
  assertResourceTenant,
  requireAdmin,
  resolveCreateTenant,
  tenantFilter
} from "./chunk-3F6VNSRK.js";
import {
  jsonOk,
  routeWrap
} from "./chunk-KKJ5D3II.js";
import {
  createApiKey,
  getApiKeyById,
  hydratePermissions,
  listApiKeys,
  revokeApiKey,
  updateApiKeyPermissions
} from "./chunk-UW7HVMAP.js";
import {
  listSessionsByApiKey
} from "./chunk-SEGKFEG5.js";
import {
  getDb,
  init_client
} from "./chunk-TLEKKGH7.js";
import {
  badRequest,
  forbidden,
  notFound
} from "./chunk-EZYKRG4W.js";

// src/handlers/api_keys.ts
import { z } from "zod";
init_client();
var ScopeSchema = z.object({
  agents: z.array(z.string()),
  environments: z.array(z.string()),
  vaults: z.array(z.string())
});
var PermissionsSchema = z.object({
  admin: z.boolean(),
  scope: ScopeSchema.nullable()
});
var CreateBody = z.object({
  name: z.string().min(1).max(200),
  permissions: PermissionsSchema.optional(),
  tenant_id: z.string().optional()
});
var PatchBody = z.object({
  permissions: PermissionsSchema
});
function toView(row) {
  return row;
}
function loadKeyForCaller(auth, id) {
  const row = getApiKeyById(id);
  if (!row || row.revoked_at != null) throw notFound(`api key ${id} not found`);
  assertResourceTenant(auth, row.tenant_id, `api key ${id} not found`);
  return row;
}
function validateKeyCreation(auth, permissions, tenantId) {
  if (permissions.admin && !tenantId) {
    throw badRequest("admin keys must be scoped to a tenant");
  }
  if (!auth.isGlobalAdmin && permissions.admin && tenantId !== auth.tenantId) {
    throw forbidden("cannot create admin keys outside your tenant");
  }
}
function handleCreateApiKey(request) {
  return routeWrap(request, async ({ auth, request: req }) => {
    requireAdmin(auth);
    if (!hasFeature("unlimited_keys")) {
      const existing = listApiKeys({ tenantFilter: tenantFilter(auth) });
      if (existing.length >= COMMUNITY_LIMITS.maxKeys) {
        requireFeature("unlimited_keys", `more than ${COMMUNITY_LIMITS.maxKeys} API keys`);
      }
    }
    const body = await req.json().catch(() => null);
    const parsed = CreateBody.safeParse(body);
    if (!parsed.success) {
      throw badRequest(parsed.error.errors.map((e) => e.message).join("; "));
    }
    const permissions = parsed.data.permissions ?? {
      admin: false,
      scope: null
    };
    const tenantId = resolveCreateTenant(auth, parsed.data.tenant_id);
    validateKeyCreation(auth, permissions, tenantId);
    const { key, id } = createApiKey({
      name: parsed.data.name,
      permissions,
      tenantId
    });
    recordAudit({
      auth,
      action: "api_keys.create",
      resource_type: "api_key",
      resource_id: id,
      tenant_id: tenantId,
      metadata: { name: parsed.data.name, admin: permissions.admin }
    });
    return jsonOk({
      id,
      name: parsed.data.name,
      key,
      permissions,
      tenant_id: tenantId
    }, 201);
  });
}
function handleListApiKeys(request) {
  return routeWrap(request, async ({ auth }) => {
    requireAdmin(auth);
    return jsonOk({ data: listApiKeys({ tenantFilter: tenantFilter(auth) }).map(toView) });
  });
}
function handleGetApiKey(request, id) {
  return routeWrap(request, async ({ auth }) => {
    requireAdmin(auth);
    const row = loadKeyForCaller(auth, id);
    return jsonOk({
      id: row.id,
      name: row.name,
      prefix: row.prefix,
      permissions: hydratePermissions(row.permissions_json),
      tenant_id: row.tenant_id,
      created_at: row.created_at
    });
  });
}
function handlePatchApiKey(request, id) {
  return routeWrap(request, async ({ auth, request: req }) => {
    requireAdmin(auth);
    loadKeyForCaller(auth, id);
    const body = await req.json().catch(() => null);
    const parsed = PatchBody.safeParse(body);
    if (!parsed.success) {
      throw badRequest(parsed.error.errors.map((e) => e.message).join("; "));
    }
    const ok = updateApiKeyPermissions(id, parsed.data.permissions);
    if (!ok) throw notFound(`api key ${id} not found`);
    const row = getApiKeyById(id);
    if (!row) throw notFound(`api key ${id} not found`);
    recordAudit({
      auth,
      action: "api_keys.update",
      resource_type: "api_key",
      resource_id: id,
      tenant_id: row.tenant_id,
      metadata: { admin: parsed.data.permissions.admin }
    });
    return jsonOk({
      id: row.id,
      name: row.name,
      prefix: row.prefix,
      permissions: hydratePermissions(row.permissions_json),
      tenant_id: row.tenant_id,
      created_at: row.created_at
    });
  });
}
function handleRevokeApiKey(request, id) {
  return routeWrap(request, async ({ auth }) => {
    requireAdmin(auth);
    const targetRow = loadKeyForCaller(auth, id);
    if (auth.keyId === id) {
      throw badRequest("cannot revoke the key used for this request");
    }
    const ok = revokeApiKey(id);
    if (!ok) throw notFound(`api key ${id} not found`);
    recordAudit({
      auth,
      action: "api_keys.revoke",
      resource_type: "api_key",
      resource_id: id,
      tenant_id: targetRow.tenant_id
    });
    return jsonOk({ ok: true, id });
  });
}
function handleGetApiKeyActivity(request, id) {
  return routeWrap(request, async ({ auth }) => {
    requireFeature("per_key_analytics", "per-key cost attribution");
    requireAdmin(auth);
    const row = loadKeyForCaller(auth, id);
    const sessions = listSessionsByApiKey(id, { limit: 50 });
    const db = getDb();
    const tenantId = tenantFilter(auth);
    const totals = tenantId != null ? db.prepare(
      `SELECT COUNT(*)                         AS session_count,
                  COALESCE(SUM(usage_cost_usd), 0) AS cost_usd,
                  COALESCE(SUM(turn_count), 0)     AS turn_count
             FROM sessions
            WHERE api_key_id = ? AND tenant_id = ?`
    ).get(id, tenantId) : db.prepare(
      `SELECT COUNT(*)                         AS session_count,
                  COALESCE(SUM(usage_cost_usd), 0) AS cost_usd,
                  COALESCE(SUM(turn_count), 0)     AS turn_count
             FROM sessions
            WHERE api_key_id = ?`
    ).get(id);
    const errorRow = tenantId != null ? db.prepare(
      `SELECT COUNT(*) AS error_count
             FROM events e
             JOIN sessions s ON s.id = e.session_id
            WHERE s.api_key_id = ? AND s.tenant_id = ? AND e.type = 'session.error'`
    ).get(id, tenantId) : db.prepare(
      `SELECT COUNT(*) AS error_count
             FROM events e
             JOIN sessions s ON s.id = e.session_id
            WHERE s.api_key_id = ? AND e.type = 'session.error'`
    ).get(id);
    return jsonOk({
      id,
      name: row.name,
      sessions,
      totals: { ...totals, error_count: errorRow.error_count }
    });
  });
}

export {
  handleCreateApiKey,
  handleListApiKeys,
  handleGetApiKey,
  handlePatchApiKey,
  handleRevokeApiKey,
  handleGetApiKeyActivity
};
