import { Prisma } from "@prisma/client";
import { NextResponse } from "next/server";
import { getSessionPayload } from "@/lib/auth/session";
import { prisma } from "@/lib/prisma";

function parseString(value: unknown) {
  return typeof value === "string" ? value.trim() : "";
}

function parseEvidence(value: unknown) {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    return null;
  }

  const record = value as Record<string, unknown>;
  const notes = parseString(record.notes).slice(0, 2000);
  const links = Array.isArray(record.links)
    ? record.links
        .map((entry) => parseString(entry))
        .filter((entry) => entry.length > 0)
        .slice(0, 10)
    : [];

  if (!notes && links.length === 0) {
    return null;
  }

  return {
    ...(notes ? { notes } : {}),
    ...(links.length > 0 ? { links } : {}),
  };
}

function isLegacyResponseEvidenceSchemaError(error: unknown) {
  return error instanceof Prisma.PrismaClientKnownRequestError && (error.code === "P2021" || error.code === "P2022");
}

export async function POST(request: Request) {
  const session = await getSessionPayload();

  if (!session) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  try {
    const body = (await request.json()) as Record<string, unknown>;
    const moduleKey = parseString(body.moduleKey);
    const questionId = parseString(body.questionId);
    const answerOptionId = parseString(body.answerOptionId);
    const evidence = parseEvidence(body.evidence);
    const normalizedEvidence = evidence ?? Prisma.DbNull;

    if (!moduleKey || !questionId || !answerOptionId) {
      return NextResponse.json({ error: "Missing response payload fields." }, { status: 400 });
    }

    const question = await prisma.question.findUnique({
      where: { id: questionId },
      select: {
        id: true,
        module: {
          select: {
            key: true,
            id: true,
            questions: {
              select: {
                id: true,
              },
            },
          },
        },
      },
    });

    if (!question || question.module.key !== moduleKey) {
      return NextResponse.json({ error: "Question does not belong to the target module." }, { status: 400 });
    }

    const option = await prisma.answerOption.findFirst({
      where: {
        id: answerOptionId,
        questionId,
      },
      select: {
        id: true,
      },
    });

    if (!option) {
      return NextResponse.json({ error: "Invalid answer option for this question." }, { status: 400 });
    }

    try {
      await prisma.response.upsert({
        where: {
          userId_questionId: {
            userId: session.userId,
            questionId,
          },
        },
        update: {
          answerOptionId,
          evidence: normalizedEvidence,
        },
        create: {
          userId: session.userId,
          questionId,
          answerOptionId,
          evidence: normalizedEvidence,
        },
      });
    } catch (error) {
      if (!isLegacyResponseEvidenceSchemaError(error)) {
        throw error;
      }

      await prisma.response.upsert({
        where: {
          userId_questionId: {
            userId: session.userId,
            questionId,
          },
        },
        update: {
          answerOptionId,
        },
        create: {
          userId: session.userId,
          questionId,
          answerOptionId,
        },
      });
    }

    const moduleQuestionIds = question.module.questions.map((moduleQuestion) => moduleQuestion.id);

    const answeredCount = await prisma.response.count({
      where: {
        userId: session.userId,
        questionId: {
          in: moduleQuestionIds,
        },
      },
    });

    const totalQuestions = moduleQuestionIds.length;
    const completion = totalQuestions > 0 ? Math.round((answeredCount / totalQuestions) * 100) : 0;

    return NextResponse.json({
      ok: true,
      progress: {
        answeredCount,
        totalQuestions,
        completion,
      },
    });
  } catch {
    return NextResponse.json({ error: "Unable to save response." }, { status: 400 });
  }
}
