import { AUDIT_ACTIONS, writeAudit } from '@/lib/audit'
import { apiRoute } from '@/lib/api/handler'
import { ApiError, ERROR_CODES } from '@/lib/api/errors'
import { contingencySchema, type ContingencyInput } from '@/lib/api/schemas'
import { demoBaseTimestamp } from '@/lib/demo/config'
import { runGridContingency } from '@/lib/services/physics-grid-service'

export const dynamic = 'force-dynamic'

/**
 * N-1, and N-2 on request.
 *
 * A full pair scan is quadratic in the number of assets, so it is never automatic and
 * always bounded — and the response says whether the bound was reached, because a
 * truncated scan that found nothing has not shown the grid is secure.
 *
 * Every case is a sandbox solve. Nothing here writes, and nothing here reaches an asset.
 */
export const POST = apiRoute<Record<string, string>, ContingencyInput>(
  { permission: 'twin.simulate', bodySchema: contingencySchema, limit: 12 },
  async ({ body, user, request }) => {
    const report = await runGridContingency({
      twinCode: body.twinCode,
      ts: body.demoClock ? demoBaseTimestamp() : undefined,
      depth: body.depth ?? 1,
      maxCases: body.maxCases,
      scopeAsset: body.scopeAsset,
    })
    if (!report) throw new ApiError(ERROR_CODES.notFound)

    await writeAudit({
      action: AUDIT_ACTIONS.contingencyRun,
      entity: 'live_grid',
      entityId: report.twinCode,
      user,
      request,
      detail: `N-${report.depth} over ${report.scope.buses} buses: ${report.scanned}/${report.candidates} cases, ${report.violations.length} violations in ${report.durationMs} ms`,
    })

    return report
  },
)
