// Path in project: app/api/admin/schedules/bookings/[id]/route.ts
import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { getAdminUser } from "@/lib/auth";
import { fmt12 } from "@/lib/timeslot-utils";
import { sendMail } from "@/lib/mailer";
import { bookingCancelledEmailTemplate, slotChangedEmailTemplate } from "@/lib/email-templates";

// DELETE /api/admin/schedules/bookings/:id — cancels a single date's booking only.
// Used for one-off "specific date" assignments, or to free up a single
// occurrence within an otherwise-active recurring schedule.
// DELETE /api/admin/schedules/bookings/:id — cancels a single date's booking only.
export async function DELETE(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
  const admin = await getAdminUser();
  if (!admin) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

  const { id } = await params;

  const booking = await prisma.schedule_bookings.findUnique({
    where: { id: Number(id) },
    include: { time_slots: true },
  });
  if (!booking) return NextResponse.json({ error: "Booking not found" }, { status: 404 });
  if (booking.status !== "active") return NextResponse.json({ error: "Booking already cancelled" }, { status: 400 });

  await prisma.$transaction(async (tx) => {
    await tx.time_slots.update({
      where: { id: booking.time_slot_id },
      data: { booked_count: { decrement: 1 } },
    });
    await tx.schedule_bookings.update({
      where: { id: Number(id) },
      data: { status: "cancelled" },
    });
  });

  // Notify the student. Never let a mail failure fail the cancellation itself.
  try {
    const schedule = await prisma.student_schedules.findUnique({
      where: { id: booking.schedule_id },
      include: { students: true },
    });

    if (schedule?.students) {
      const bookingDateKey = booking.booking_date.toISOString().slice(0, 10);
      await sendMail({
        to: schedule.students.email,
        subject: "Your RKDA class slot has been cancelled",
        html: bookingCancelledEmailTemplate(
          schedule.students.name,
          bookingDateKey,
          { startTime: fmt12(booking.time_slots.start_time), endTime: fmt12(booking.time_slots.end_time) },
          `${process.env.NEXT_PUBLIC_APP_URL}/student/schedule`
        ),
      });
    }
  } catch (mailErr) {
    console.error("Booking Cancel Email Error:", mailErr);
  }

  return NextResponse.json({ message: "Booking cancelled" });
}

// PUT /api/admin/schedules/bookings/:id — changes which slot a specific date's
// booking points to (same date, different time). Frees the old slot, books the
// new one, and safely reuses/reactivates any stray cancelled booking row on the
// target slot+date instead of hitting the (time_slot_id, booking_date) unique
// constraint.
export async function PUT(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
  const admin = await getAdminUser();
  if (!admin) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

  const { id } = await params;
  const body = await req.json();
  const { time_slot_id } = body as { time_slot_id: number };

  if (!time_slot_id) {
    return NextResponse.json({ error: "time_slot_id is required" }, { status: 400 });
  }

  const booking = await prisma.schedule_bookings.findUnique({ where: { id: Number(id) } });
  if (!booking) return NextResponse.json({ error: "Booking not found" }, { status: 404 });
  if (booking.status !== "active") return NextResponse.json({ error: "Booking is not active" }, { status: 400 });

  const newSlot = await prisma.time_slots.findUnique({ where: { id: time_slot_id } });
  if (!newSlot) return NextResponse.json({ error: "Target slot not found" }, { status: 404 });

  const newSlotDateKey = newSlot.slot_date.toISOString().slice(0, 10);
  const bookingDateKey = booking.booking_date.toISOString().slice(0, 10);
  if (newSlotDateKey !== bookingDateKey) {
    return NextResponse.json({ error: "New slot must be on the same date" }, { status: 400 });
  }
  if (newSlot.id === booking.time_slot_id) {
    return NextResponse.json({ error: "That is already the assigned slot" }, { status: 400 });
  }

  // Capture the old slot's times before it gets swapped, purely for the "old vs
  // new" comparison in the change-notification email.
  const oldSlot = await prisma.time_slots.findUnique({ where: { id: booking.time_slot_id } });

  try {
    await prisma.$transaction(async (tx) => {
      const freshNewSlot = await tx.time_slots.findUnique({ where: { id: newSlot.id } });
      if (!freshNewSlot || freshNewSlot.booked_count >= freshNewSlot.capacity) {
        throw new Error("Selected slot is full");
      }

      // Free the old slot
      await tx.time_slots.update({
        where: { id: booking.time_slot_id },
        data: { booked_count: { decrement: 1 } },
      });

      // Does THIS student's schedule already have a (possibly cancelled) row on
      // the target slot+date? Other students may already be booked into that
      // slot too — that's fine, capacity governs that, not this check.
      const existingForThisSchedule = await tx.schedule_bookings.findUnique({
        where: {
          time_slot_id_booking_date_schedule_id: {
            time_slot_id: newSlot.id,
            booking_date: booking.booking_date,
            schedule_id: booking.schedule_id,
          },
        },
      });

      if (existingForThisSchedule) {
        if (existingForThisSchedule.status === "active") {
          throw new Error("This student is already booked into that slot for this date");
        }
        await tx.schedule_bookings.update({
          where: { id: existingForThisSchedule.id },
          data: { status: "active" },
        });
        await tx.schedule_bookings.update({
          where: { id: booking.id },
          data: { status: "cancelled" },
        });
      } else {
        // No conflicting row — simplest path, just repoint this booking.
        await tx.schedule_bookings.update({
          where: { id: booking.id },
          data: { time_slot_id: newSlot.id },
        });
      }

      await tx.time_slots.update({
        where: { id: newSlot.id },
        data: { booked_count: { increment: 1 } },
      });
    });

    // Notify the student of the change. Look up the student via the schedule
    // this booking belongs to. Failure to send must never fail the request.
    try {
      const schedule = await prisma.student_schedules.findUnique({
        where: { id: booking.schedule_id },
        include: { students: true },
      });

      if (schedule?.students && oldSlot) {
        await sendMail({
          to: schedule.students.email,
          subject: "Your RKDA class slot has been changed",
          html: slotChangedEmailTemplate(
            schedule.students.name,
            bookingDateKey,
            { startTime: fmt12(oldSlot.start_time), endTime: fmt12(oldSlot.end_time) },
            { startTime: fmt12(newSlot.start_time), endTime: fmt12(newSlot.end_time) },
            `${process.env.NEXT_PUBLIC_APP_URL}/student/schedule`
          ),
        });
      }
    } catch (mailErr) {
      console.error("Slot Change Email Error:", mailErr);
    }

    return NextResponse.json({ message: "Slot changed" });
  } catch (err) {
    const message = err instanceof Error ? err.message : "Failed to change slot";
    return NextResponse.json({ error: message }, { status: 409 });
  }
}