import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { parseDateUTC, fmt12 } from "@/lib/timeslot-utils";
import { getStudent } from "@/lib/studentAuth";
import { sendMail } from "@/lib/mailer";
import { scheduleChangeRequestedEmailTemplate } from "@/lib/email-templates";

interface CreateRequestBody {
  booking_id?: number;
  requested_date: string;
  requested_start_time: string;
  requested_end_time: string;
  reason?: string;
}

export async function GET() {
  const student = await getStudent();
  if (!student) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

  const requests = await prisma.schedule_change_requests.findMany({
    where: { student_id: student.id as number },
    orderBy: { created_at: "desc" },
  });

  return NextResponse.json({ data: requests });
}

export async function POST(req: NextRequest) {
  const student = await getStudent();
  if (!student) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

  // Explicit casts here in case StudentTokenPayload still carries an index
  // signature ([key: string]: unknown) rather than typed fields.
  const studentId = student.id as number;
  const studentName = String(student.name ?? "A student");
  const studentEmailOrId = String(student.student_id ?? student.email ?? "");

  const body: CreateRequestBody = await req.json();
  const { booking_id, requested_date, requested_start_time, requested_end_time, reason } = body;

  if (!requested_date || !requested_start_time || !requested_end_time) {
    return NextResponse.json({ error: "requested_date, requested_start_time, requested_end_time are required" }, { status: 400 });
  }

  const todayKey = new Date().toISOString().slice(0, 10);
  if (requested_date < todayKey) {
    return NextResponse.json({ error: "You can't request a past date." }, { status: 400 });
  }

  let currentBookingDate: Date | null = null;
  let currentStartTime: string | null = null;
  let currentEndTime: string | null = null;

  if (booking_id) {
    const booking = await prisma.schedule_bookings.findUnique({
      where: { id: booking_id },
      include: {
        student_schedules: true,
        time_slots: { select: { start_time: true, end_time: true } },
      },
    });
    if (!booking || booking.student_schedules.student_id !== studentId) {
      return NextResponse.json({ error: "Booking not found" }, { status: 404 });
    }
    if (booking.status !== "active") {
      return NextResponse.json({ error: "That booking is not active" }, { status: 400 });
    }

    const bookingDateKey = booking.booking_date.toISOString().slice(0, 10);
    if (bookingDateKey < todayKey) {
      return NextResponse.json({ error: "This class date has already passed and can no longer be changed." }, { status: 400 });
    }

    currentBookingDate = booking.booking_date;
    currentStartTime = booking.time_slots.start_time;
    currentEndTime = booking.time_slots.end_time;
  }

  const requestedDate = parseDateUTC(requested_date);

  const targetSlot = await prisma.time_slots.findUnique({
    where: {
      slot_date_start_time_end_time: {
        slot_date: requestedDate,
        start_time: requested_start_time,
        end_time: requested_end_time,
      },
    },
  });
  if (!targetSlot) {
    return NextResponse.json({ error: "No slot exists for that date/time" }, { status: 400 });
  }

  const created = await prisma.schedule_change_requests.create({
    data: {
      student_id: studentId,
      booking_id: booking_id ?? null,
      current_booking_date: currentBookingDate,
      current_start_time: currentStartTime,
      current_end_time: currentEndTime,
      requested_date: requestedDate,
      requested_start_time,
      requested_end_time,
      reason: reason?.trim() || null,
    },
  });

  // Notify all active admins. Never let a mail failure fail the request itself.
  try {
    const admins = await prisma.master_admin_users.findMany({
      where: {
        active_status: true,
        delete_status: false,
        deleted_at: null,
      },
      select: { user_email: true },
    });

    const adminEmails = admins.map((a) => a.user_email).filter(Boolean);

    if (adminEmails.length > 0) {
      const html = scheduleChangeRequestedEmailTemplate(
        studentName,
        studentEmailOrId,
        currentBookingDate && currentStartTime && currentEndTime
          ? {
              dateLabel: currentBookingDate.toISOString().slice(0, 10),
              startTime: fmt12(currentStartTime),
              endTime: fmt12(currentEndTime),
            }
          : null,
        {
          dateLabel: requested_date,
          startTime: fmt12(requested_start_time),
          endTime: fmt12(requested_end_time),
        },
        reason?.trim() || null,
        `${process.env.NEXT_PUBLIC_APP_URL}/admin/schedule-requests`
      );

      // Send individually so one bad address doesn't silently drop the rest,
      // and so sendMail's `to` type (string) is satisfied either way.
      await Promise.allSettled(
        adminEmails.map((email) =>
          sendMail({
            to: email,
            subject: `Schedule change request from ${studentName}`,
            html,
          })
        )
      );
    } else {
      console.warn("No active admin recipients found for schedule change notification.");
    }
  } catch (mailErr) {
    console.error("Schedule Change Request Admin Email Error:", mailErr);
  }

  return NextResponse.json({ data: created }, { status: 201 });
}