import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { requireAuth } from "@/lib/auth";
import {
  generateCertificatePdf,
  generateSerialNumber,
  buildVerifyUrl,
} from "@/lib/learn";

export async function POST(request: Request) {
  const jwt = await requireAuth(request);
  if (!jwt) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  try {
    const body = await request.json();
    const { moduleId, completed, score, listened } = body;

    if (!moduleId || typeof completed !== "boolean") {
      return NextResponse.json(
        { error: "moduleId (string) and completed (boolean) are required" },
        { status: 400 }
      );
    }

    // 1. Find the module with its course relation
    const module = await prisma.learnModule.findUnique({
      where: { id: moduleId },
      include: { course: true },
    });

    if (!module) {
      return NextResponse.json({ error: "Module not found" }, { status: 404 });
    }

    if (!module.course.published) {
      return NextResponse.json(
        { error: "Course is not published" },
        { status: 403 }
      );
    }

    // 2. Find the enrollment for this user + course
    let enrollment = await prisma.learnEnrollment.findUnique({
      where: {
        userId_courseId: {
          userId: jwt.userId,
          courseId: module.courseId,
        },
      },
    });

    if (!enrollment) {
      return NextResponse.json(
        { error: "You are not enrolled in this course" },
        { status: 403 }
      );
    }

    // 3. Upsert LearnModuleProgress
    await prisma.learnModuleProgress.upsert({
      where: {
        enrollmentId_moduleId: {
          enrollmentId: enrollment.id,
          moduleId: module.id,
        },
      },
      update: {
        completed,
        ...(score !== undefined ? { score } : {}),
        ...(listened !== undefined ? { listened } : {}),
        ...(completed ? { completedAt: new Date() } : {}),
      },
      create: {
        enrollmentId: enrollment.id,
        moduleId: module.id,
        completed,
        ...(score !== undefined ? { score } : {}),
        ...(listened !== undefined ? { listened } : {}),
        ...(completed ? { completedAt: new Date() } : {}),
      },
    });

    // 4. Count completed modules vs total to compute progress
    const totalModules = await prisma.learnModule.count({
      where: { courseId: module.courseId },
    });

    const completedProgressRecords = await prisma.learnModuleProgress.count({
      where: {
        enrollmentId: enrollment.id,
        completed: true,
      },
    });

    const progress =
      totalModules > 0 ? completedProgressRecords / totalModules : 0;

    // 5. If completing (progress >= 1), handle certificate + enrollment atomically
    let certificate: {
      serialNumber: string;
      verifyUrl: string;
      pdfPath: string;
    } | undefined;

    if (progress >= 1 && !enrollment.completed) {
      const now = new Date();

      // Fetch user info for certificate (before transaction)
      const user = await prisma.user.findUnique({
        where: { id: jwt.userId },
        select: { name: true },
      });

      const course = module.course;
      const serialNumber = generateSerialNumber(
        jwt.userId,
        course.id,
        course.slug,
        now
      );

      const certificateData = {
        serialNumber,
        userName: user?.name ?? "Student",
        courseTitle: course.title,
        courseAuthor: course.author,
        moduleCount: totalModules,
        score: score ?? undefined,
        issuedAt: now,
        verifyUrl: buildVerifyUrl(serialNumber),
      };

      // Generate PDF before transaction — if it fails, transaction never runs
      const pdfPath = await generateCertificatePdf(certificateData);

      // All DB mutations inside a single Prisma transaction for atomicity
      const result = await prisma.$transaction(async (tx) => {
        // Race condition guard: check no existing certificate for this user + course
        const existingCert = await tx.learnCertificate.findFirst({
          where: { userId: jwt.userId, courseId: course.id },
        });

        if (existingCert) {
          // Already issued — still update enrollment but don't create another
          const updatedEnrollment = await tx.learnEnrollment.update({
            where: { id: enrollment.id },
            data: {
              progress,
              completed: true,
              completedAt: now,
            },
          });
          return { enrollment: updatedEnrollment, certificate: null };
        }

        // Update enrollment (progress + completion) and create certificate atomically
        const updatedEnrollment = await tx.learnEnrollment.update({
          where: { id: enrollment.id },
          data: {
            progress,
            completed: true,
            completedAt: now,
          },
        });

        await tx.learnCertificate.create({
          data: {
            serialNumber,
            userId: jwt.userId,
            userName: user?.name ?? "Student",
            courseId: course.id,
            moduleCount: totalModules,
            score: score ?? null,
            pdfPath,
            issuedAt: now,
          },
        });

        return {
          enrollment: updatedEnrollment,
          certificate: {
            serialNumber,
            verifyUrl: certificateData.verifyUrl,
            pdfPath,
          },
        };
      });

      enrollment = result.enrollment;
      if (result.certificate) {
        certificate = result.certificate;
      }
    } else {
      // No completion — just update progress outside transaction
      enrollment = await prisma.learnEnrollment.update({
        where: { id: enrollment.id },
        data: { progress },
      });
    }

    return NextResponse.json({
      progress,
      completed: enrollment.completed,
      ...(certificate ? { certificate } : {}),
    });
  } catch (error) {
    console.error("Learn progress error:", error);
    return NextResponse.json(
      { error: "Failed to update progress" },
      { status: 500 }
    );
  }
}
