import { NextResponse } from 'next/server'
import { db } from '@/lib/db'
import { checkAgentAuth } from '@/lib/agent-auth'
import { CONSTRUCTS_BY_CODE } from '@/lib/constructs'
import { buildStateSnapshot } from '@/lib/generator'
import { buildGenerationPrompt, buildGradingPrompt, buildSpiritualPrompt } from '@/lib/prompts'
import { GRADER_PROMPT_VERSION, rubricHash, rubricVersionFor } from '@/lib/rubrics'

export const runtime = 'nodejs'

/**
 * GET /api/agent/work/<sessionId>
 *
 * Everything the Mac agent needs for this session, with the prompts already
 * composed here. The agent never writes its own instructions — the exact text
 * that produced a score has to be reproducible from this repo plus the version
 * stamped on the grading.
 *
 * For GRADE work the payload is BLIND (Rule 4): it carries the item, the
 * criteria and the response, and no name, sibling, prior score, round number or
 * baseline/re-test marker. There is no field here through which one could
 * arrive.
 */
export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
  if (!checkAgentAuth(req)) return new NextResponse('Unauthorized', { status: 401 })

  const { id } = await params
  const session = await db.agentSession.findUnique({ where: { id } })
  if (!session) return new NextResponse('Session not found', { status: 404 })

  if (session.kind === 'GENERATE') {
    if (!session.studentId) return new NextResponse('Session has no student', { status: 400 })
    const snapshot = await buildStateSnapshot(session.studentId, session.track)
    return NextResponse.json({
      kind: 'GENERATE',
      studentId: session.studentId,
      track: session.track,
      // The two tracks are written under different instructions: the spiritual
      // one carries the copyright line and, more importantly, the rules about
      // what must never be asked of a child.
      prompt:
        session.track === 'SPIRITUAL'
          ? buildSpiritualPrompt(snapshot)
          : buildGenerationPrompt(snapshot),
      stateSnapshot: snapshot,
    })
  }

  if (session.kind === 'ANSWER') {
    // The agent fetches the questions themselves from /api/agent/questions;
    // this only tells it which mode it is in.
    return NextResponse.json({ kind: 'ANSWER' })
  }

  // GRADE
  const responses = await db.response.findMany({
    where: {
      gradings: { none: {} },
      // Conceded items are not graded — he saw the answer, so a score here
      // would measure the explanation rather than him.
      conceded: false,
      ...(session.responseId ? { id: session.responseId } : {}),
      ...(session.setId ? { setItem: { setId: session.setId } } : {}),
    },
    include: { setItem: { include: { item: { include: { construct: true } } } } },
    orderBy: { submittedAt: 'asc' },
  })

  const jobs = responses.flatMap((r) => {
    const item = r.setItem.item
    const construct = CONSTRUCTS_BY_CODE[item.constructCode]
    if (!construct) return []
    const images = Array.isArray(r.imagePaths) ? (r.imagePaths as string[]) : []

    return [
      {
        responseId: r.id,
        // So the agent knows which set to email results for when it finishes.
        setId: r.setItem.setId,
        // Local filenames the agent will save downloads to. The prompt refers to
        // these, so it can only see what we hand it.
        imageUrls: images.map((p) => `/api/agent/image/${p}`),
        imageFiles: images.map((_, i) => `response-${i}.img`),
        promptTemplate: buildGradingPrompt({
          construct,
          format: item.format,
          itemPrompt: item.prompt,
          itemStimulus: item.stimulus,
          rubricCriteria: item.rubricCriteria,
          typedText: r.typedText,
          imageFiles: images.map((_, i) => `response-${i}.img`),
        }),
        // Echoed back on POST and verified server-side (Rule 6).
        rubricVersion: rubricVersionFor(construct.code),
        rubricHash: rubricHash(construct),
        promptVersion: GRADER_PROMPT_VERSION,
        // Reasoning is not scored on an answers-only item, so it is not
        // offered back either — the agent maps only what it is given.
        dimensions: construct.dimensions
          .filter((d) => !(item.format === 'COMPUTATION_CONTROL' && d.code === 'reasoning_quality'))
          .map((d) => ({ code: d.code, maxValue: d.maxValue })),
      },
    ]
  })

  return NextResponse.json({ kind: 'GRADE', jobs })
}
