import { AUDIT_ACTIONS, writeAudit } from '@/lib/audit'
import { apiRoute } from '@/lib/api/handler'
import { ApiError, ERROR_CODES } from '@/lib/api/errors'
import { learningReviewSchema, type LearningReviewInput } from '@/lib/api/schemas'
import {
  listLearningCandidates,
  refreshLearningCandidates,
  reviewLearningCandidate,
} from '@/lib/services/memory-service'

export const dynamic = 'force-dynamic'

/** The governed learning queue (§33). */
export const GET = apiRoute({ permission: 'learning.read' }, async ({ searchParams }) => {
  const status = searchParams.get('status') ?? undefined
  const limit = Math.min(60, Math.max(1, Number(searchParams.get('limit')) || 30))

  if (searchParams.get('refresh') === '1') await refreshLearningCandidates()

  return { candidates: await listLearningCandidates({ status, limit }) }
})

/**
 * Approve or reject a candidate (§32).
 *
 * Approval records a human decision and stops there. There is no code path from this
 * route to a deployed model, a threshold or a strategy weight — that separation is the
 * whole content of §32, and implementing the "apply" half would remove it.
 */
export const PATCH = apiRoute<Record<string, string>, LearningReviewInput>(
  { permission: 'learning.review', bodySchema: learningReviewSchema, limit: 30 },
  async ({ body, user, request }) => {
    const ok = await reviewLearningCandidate({
      code: body.code,
      decision: body.decision,
      note: body.note,
      userId: user.id,
    })
    if (!ok) throw new ApiError(ERROR_CODES.conflict)

    await writeAudit({
      action: AUDIT_ACTIONS.learningReviewed,
      entity: 'learning_candidate',
      entityId: body.code,
      user,
      request,
      detail: `${body.decision}${body.note ? `: ${body.note}` : ''}`,
    })

    return { ok: true, applied: false }
  },
)
