/**
 * Reschedule Booking API
 * PATCH /api/bookings/[id]/reschedule
 *
 * Allows admins and staff to modify appointment dates and times
 */

import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/db";
import { getAuthenticatedEmail } from "@/lib/utils/auth-helpers.util";
import { getEffectiveOwnerId } from "@/lib/utils/organization-data";
import { logger } from "@/lib/logger";
import { withCSRF } from "@/lib/csrf";
import type { Id } from "@/convex/_generated/dataModel";
import { convexMutation } from "@/lib/convex-client";

export const dynamic = "force-dynamic";

// Status values that cannot be rescheduled
const NON_RESCHEDULABLE_STATUSES = ["completed", "cancelled", "no_show"];

interface RescheduleBody {
  appointmentDate: string; // YYYY-MM-DD
  appointmentTime: string; // HH:MM (24-hour)
  duration?: number;
  reason?: string;
  notifyCustomer?: boolean;
  staffMember?: string;
}

/**
 * Validate date is in YYYY-MM-DD format and is today or future
 */
function validateDate(dateStr: string): { valid: boolean; error?: string } {
  const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
  if (!dateRegex.test(dateStr)) {
    return { valid: false, error: "Invalid date format. Use YYYY-MM-DD." };
  }

  const inputDate = new Date(dateStr);
  if (isNaN(inputDate.getTime())) {
    return { valid: false, error: "Invalid date value." };
  }

  const today = new Date();
  today.setHours(0, 0, 0, 0);
  inputDate.setHours(0, 0, 0, 0);

  if (inputDate < today) {
    return {
      valid: false,
      error: "Appointment date must be today or in the future.",
    };
  }

  return { valid: true };
}

/**
 * Validate time is in HH:MM format (24-hour)
 */
function validateTime(timeStr: string): { valid: boolean; error?: string } {
  const timeRegex = /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/;
  if (!timeRegex.test(timeStr)) {
    return { valid: false, error: "Invalid time format. Use HH:MM (24-hour)." };
  }
  return { valid: true };
}

/**
 * Validate duration is within acceptable range
 */
function validateDuration(duration: number): {
  valid: boolean;
  error?: string;
} {
  if (duration < 15 || duration > 480) {
    return {
      valid: false,
      error: "Duration must be between 15 and 480 minutes.",
    };
  }
  return { valid: true };
}

/** Extract booking ID from /api/bookings/[id]/reschedule URL */
function extractBookingId(request: NextRequest): string {
  const segments = request.nextUrl.pathname.split("/");
  // /api/bookings/[id]/reschedule  => segments = ["", "api", "bookings", "<id>", "reschedule"]
  return segments[3] ?? "";
}

export const PATCH = withCSRF(async (request: NextRequest) => {
  try {
    const bookingId = extractBookingId(request);

    // Authenticate user
    const userEmail = await getAuthenticatedEmail(request, auth);

    if (!userEmail) {
      logger.warn("Unauthorized reschedule attempt");
      return NextResponse.json(
        { success: false, error: "Unauthorized" },
        { status: 401 },
      );
    }

    // Get user
    const user = await prisma.users.findUnique({
      where: { email: userEmail },
      select: { id: true },
    });

    if (!user) {
      logger.warn("User not found for reschedule", { userEmail });
      return NextResponse.json(
        { success: false, error: "User not found" },
        { status: 404 },
      );
    }

    // Get effective owner ID (for org members, this returns the org owner's ID)
    const effectiveUserId = await getEffectiveOwnerId(user.id);

    // Verify booking belongs to user/organization
    const booking = await prisma.bookings.findFirst({
      where: {
        id: bookingId,
        userId: effectiveUserId,
      },
    });

    if (!booking) {
      logger.warn("Booking not found for user", {
        bookingId,
        effectiveUserId,
      });
      return NextResponse.json(
        { success: false, error: "Booking not found" },
        { status: 404 },
      );
    }

    // Check if booking status allows rescheduling
    if (NON_RESCHEDULABLE_STATUSES.includes(booking.status)) {
      return NextResponse.json(
        {
          success: false,
          error: `Cannot reschedule a booking with status "${booking.status}". Only pending or confirmed appointments can be rescheduled.`,
        },
        { status: 400 },
      );
    }

    // Parse request body
    const body: RescheduleBody = await request.json();
    const { appointmentDate, appointmentTime, duration } = body;

    // Validate required fields
    if (!appointmentDate || !appointmentTime) {
      return NextResponse.json(
        {
          success: false,
          error: "appointmentDate and appointmentTime are required.",
        },
        { status: 400 },
      );
    }

    // Validate date
    const dateValidation = validateDate(appointmentDate);
    if (!dateValidation.valid) {
      return NextResponse.json(
        { success: false, error: dateValidation.error },
        { status: 400 },
      );
    }

    // Validate time
    const timeValidation = validateTime(appointmentTime);
    if (!timeValidation.valid) {
      return NextResponse.json(
        { success: false, error: timeValidation.error },
        { status: 400 },
      );
    }

    // Validate duration if provided
    if (duration !== undefined) {
      const durationValidation = validateDuration(duration);
      if (!durationValidation.valid) {
        return NextResponse.json(
          { success: false, error: durationValidation.error },
          { status: 400 },
        );
      }
    }

    // Store old values for audit note
    const oldDate = new Date(booking.appointmentDate);
    const oldTime = booking.appointmentTime;
    const oldDuration = booking.duration;

    // Parse new date
    const newDate = new Date(appointmentDate);

    // --- Convex data-source routing (strangler fig) ---
    if (getDataSource("bookings") === "convex") {
      const { convexMutation } = await import("@/lib/convex-client");
      const staffMember = (body as RescheduleBody & { staffMember?: string })
        .staffMember;
      const updated = await convexMutation("bookings/mutations:reschedule", {
        id: bookingId as Id<"bookings">,
        appointmentDate: new Date(body.appointmentDate).getTime(),
        appointmentTime: body.appointmentTime,
        ...(staffMember && { staffMember }),
      });
      return NextResponse.json({ booking: updated });
    }
    // --- End Convex routing ---

    // Update booking
    const updatedBooking = await prisma.bookings.update({
      where: { id: bookingId },
      data: {
        appointmentDate: newDate,
        appointmentTime: appointmentTime,
        ...(duration !== undefined && { duration }),
        updatedAt: new Date(),
      },
    });

    logger.info("Booking rescheduled", {
      bookingId,
      previousDate: oldDate.toISOString().split("T")[0],
      previousTime: oldTime,
      newDate: appointmentDate,
      newTime: appointmentTime,
      userEmail,
    });

    // Create audit note
    const noteContent = [
      `Appointment rescheduled`,
      `From: ${formatDateForDisplay(oldDate)} at ${oldTime}`,
      `To: ${formatDateForDisplay(newDate)} at ${appointmentTime}`,
      oldDuration !== (duration ?? oldDuration)
        ? `Duration changed: ${oldDuration} → ${duration} minutes`
        : null,
      reason ? `Reason: ${reason}` : null,
      `By: ${userEmail}`,
    ]
      .filter(Boolean)
      .join("\n");

    await bookingNotesAdapter.create({
      data: {
        id: `reschedule-${bookingId}-${Date.now()}`,
        bookingId,
        authorId: user.id,
        authorName: userEmail,
        content: noteContent,
        updatedAt: new Date(),
      },
    });

    // Update Google Calendar event if connected
    let calendarUpdated = false;
    const googleEventId = await getGoogleEventIdFromBooking(bookingId);
    if (googleEventId) {
      try {
        const accessToken = await getValidGoogleCalendarToken(effectiveUserId);
        if (accessToken) {
          // Get user's primary calendar
          const integration = await integrationsAdapter.findUnique({
            where: {
              userId_type: {
                userId: effectiveUserId,
                type: "google_calendar",
              },
            },
          });

          if (integration) {
            // Parse new date/time for calendar
            const [hours, minutes] = appointmentTime.split(":").map(Number);
            const startDateTime = new Date(newDate);
            startDateTime.setHours(hours, minutes, 0, 0);

            const endDateTime = new Date(startDateTime);
            endDateTime.setMinutes(
              endDateTime.getMinutes() + (duration ?? booking.duration),
            );

            const updated = await updateCalendarEvent(
              effectiveUserId,
              "primary",
              googleEventId,
              {
                start: {
                  dateTime: formatDateTimeForCalendar(startDateTime),
                  timeZone: "Asia/Qatar",
                },
                end: {
                  dateTime: formatDateTimeForCalendar(endDateTime),
                  timeZone: "Asia/Qatar",
                },
              },
            );

            if (updated) {
              calendarUpdated = true;
              logger.info("Google Calendar event updated", {
                bookingId,
                eventId: googleEventId,
              });
            }
          }
        }
      } catch (calendarError) {
        logger.error("Failed to update Google Calendar event", {
          bookingId,
          googleEventId,
          error: getErrorMessage(calendarError),
        });
      }
    }

    // Send WhatsApp notification if requested
    let notificationSent = false;
    if (notifyCustomer && booking.customerPhone) {
      try {
        const message = [
          `مرحباً ${booking.customerName}،`,
          ``,
          `تم تغيير موعدك.`,
          `الموعد الجديد: ${formatDateForDisplay(newDate, "ar")} الساعة ${appointmentTime}`,
          ``,
          `Hello ${booking.customerName},`,
          ``,
          `Your appointment has been rescheduled.`,
          `New appointment: ${formatDateForDisplay(newDate)} at ${appointmentTime}`,
          ``,
          `Thank you! شكراً`,
        ].join("\n");

        const result = await whatsappService.sendMessage(
          effectiveUserId,
          booking.customerPhone,
          message,
        );

        if (result.success) {
          notificationSent = true;
          logger.info("Reschedule notification sent via WhatsApp", {
            bookingId,
            phone: booking.customerPhone,
          });
        }
      } catch (notifyError) {
        logger.error("Failed to send reschedule notification", {
          bookingId,
          error: getErrorMessage(notifyError),
        });
      }
    }

    return NextResponse.json({
      success: true,
      message: "Appointment rescheduled successfully",
      booking: updatedBooking,
      calendarUpdated,
      notificationSent,
    });
    return NextResponse.json({ booking: updated });
  } catch (error) {
    logger.error("Failed to reschedule booking", {
      error: error instanceof Error ? error.message : "Unknown error",
    });

    return NextResponse.json(
      { success: false, error: "Failed to reschedule booking" },
      { status: 500 },
    );
  }
});
