/**
 * N8N Booking Webhook Callback
 *
 * Creates appointments from AI conversations (WhatsApp/Voice).
 * Stores data in bookings table for dashboard display.
 */

import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/db";
import { logger } from "@/lib/logger";
import { systemSettingsService } from "@/lib/services/system-settings.service";
import { z } from "zod";
import { createId } from "@paralleldrive/cuid2";
import { applyRateLimit, rateLimiters, getClientIdentifier } from "@/lib/rate-limiter";

// Request validation schema
const bookingPayloadSchema = z.object({
  auth_token: z.string().min(1),
  user_id: z.string().min(1),
  conversation_id: z.string().optional(),
  source: z.enum(["whatsapp", "voice", "web"]),
  customer: z.object({
    name: z.string().min(1),
    phone: z.string().min(1),
    email: z.string().email().optional(),
  }),
  booking: z.object({
    service: z.string().min(1),
    date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be YYYY-MM-DD"),
    time: z.string().regex(/^\d{2}:\d{2}$/, "Time must be HH:MM"),
    duration: z.number().min(15).max(480).default(30),
    staff_member: z.string().optional(),
    notes: z.string().optional(),
    total_amount: z.number().min(0).optional(),
    deposit_amount: z.number().min(0).optional(),
  }),
});

interface BookingCallbackResponse {
  success: boolean;
  booking_id?: string;
  error?: string;
}

export async function POST(request: NextRequest) {
  // Rate limit webhook requests (100/min per IP)
  const rateLimitResponse = await applyRateLimit(request, rateLimiters.webhookGeneral);
  if (rateLimitResponse) {
    logger.warn("N8N booking webhook rate limit exceeded", {
      ip: getClientIdentifier(request),
    });
    return rateLimitResponse;
  }

  try {
    // Parse and validate request body
    const body = await request.json();
    const validationResult = bookingPayloadSchema.safeParse(body);

    if (!validationResult.success) {
      logger.warn("N8N booking callback - invalid payload", {
        errors: validationResult.error.errors,
      });
      return NextResponse.json<BookingCallbackResponse>(
        {
          success: false,
          error: "Invalid payload: " + validationResult.error.errors[0].message,
        },
        { status: 400 },
      );
    }

    const payload = validationResult.data;

    // Validate auth token (from database or env fallback)
    const isValidToken = await systemSettingsService.validateN8NToken(
      payload.auth_token,
    );
    if (!isValidToken) {
      logger.warn("N8N booking callback - invalid auth token", {
        user_id: payload.user_id,
      });
      return NextResponse.json<BookingCallbackResponse>(
        { success: false, error: "Invalid authentication token" },
        { status: 401 },
      );
    }

    // Verify user exists and has active subscription
    const user = await prisma.users.findUnique({
      where: { id: payload.user_id },
      include: {
        subscriptions: {
          where: { status: { in: ["active", "trialing"] } },
          take: 1,
        },
      },
    });

    if (!user || !user.isActive) {
      logger.warn("N8N booking callback - user not found or inactive", {
        user_id: payload.user_id,
      });
      return NextResponse.json<BookingCallbackResponse>(
        { success: false, error: "User not found or inactive" },
        { status: 404 },
      );
    }

    if (user.subscriptions.length === 0) {
      logger.warn("N8N booking callback - no active subscription", {
        user_id: payload.user_id,
      });
      return NextResponse.json<BookingCallbackResponse>(
        { success: false, error: "No active subscription" },
        { status: 403 },
      );
    }

    // Parse date and time
    const [year, month, day] = payload.booking.date.split("-").map(Number);
    const appointmentDate = new Date(year, month - 1, day);

    // Create the booking
    const bookingId = createId();

    logger.info("N8N booking callback - created booking", {
      booking_id: booking.id,
      user_id: payload.user_id,
      source: payload.source,
      service: payload.booking.service,
    });

    // Update conversation with booking reference if provided
    if (payload.conversation_id) {
      try {
        await prisma.conversationLog.update({
          where: { id: payload.conversation_id },
          data: {
            metadata: {
              booking_created: true,
              booking_id: booking.id,
            },
          },
        });
      } catch {
        logger.warn("Failed to update conversation with booking ref", {
          conversation_id: payload.conversation_id,
          booking_id: booking.id,
        });
      }
    }

    // Update or create customer record
    await upsertCustomer(
      payload.user_id,
      payload.customer.name,
      payload.customer.phone,
      payload.customer.email,
    );

    // Update workflow analytics
    await updateBookingAnalytics(payload.user_id, payload.source);

    // Create notification for user
    await createBookingNotification(
      payload.user_id,
      booking.id,
      payload.customer.name,
      payload.booking.service,
      payload.booking.date,
      payload.booking.time,
    );

    return NextResponse.json<BookingCallbackResponse>({
      success: true,
      booking_id: booking.id,
    });
  } catch (error) {
    logger.error("N8N booking callback error", { error });
    return NextResponse.json<BookingCallbackResponse>(
      { success: false, error: "Internal server error" },
      { status: 500 },
    );
  }
}

/**
 * Upsert customer record
 */
async function upsertCustomer(
  userId: string,
  name: string,
  phone: string,
  email?: string,
) {
  try {
    await prisma.customers.upsert({
      where: {
        userId_phone: {
          userId,
          phone,
        },
      },
      create: {
        userId,
        name,
        phone,
        email,
      },
      update: {
        name,
        email: email || undefined,
      },
    });
  } catch (error) {
    logger.error("Failed to upsert customer", {
      error,
      userId,
      phone,
    });
  }
}

/**
 * Update workflow analytics for bookings
 */
async function updateBookingAnalytics(userId: string, source: string) {
  try {
    const today = new Date();
    today.setHours(0, 0, 0, 0);

    const workflowType =
      source === "voice"
        ? "voice_agent"
        : source === "whatsapp"
          ? "whatsapp"
          : "ai_agent";

    await prisma.workflowAnalytics.upsert({
      where: {
        userId_workflowType_date: {
          userId,
          workflowType,
          date: today,
        },
      },
      create: {
        userId,
        workflowType,
        date: today,
        appointmentsBooked: 1,
      },
      update: {
        appointmentsBooked: { increment: 1 },
      },
    });
  } catch (error) {
    logger.error("Failed to update booking analytics", {
      error,
      userId,
      source,
    });
  }
}

/**
 * Create notification for new booking
 */
async function createBookingNotification(
  userId: string,
  bookingId: string,
  customerName: string,
  service: string,
  date: string,
  time: string,
) {
  try {
    await prisma.notifications.create({
      data: {
        userId,
        type: "new_booking",
        title: "New Appointment Booked",
        message: `${customerName} booked ${service} for ${date} at ${time}`,
        actionUrl: `/dashboard?tab=appointments&booking=${bookingId}`,
        metadata: {
          booking_id: bookingId,
          customer_name: customerName,
          service,
          date,
          time,
        },
      },
    });
  } catch (error) {
    logger.error("Failed to create booking notification", {
      error,
      userId,
      bookingId,
    });
  }
}

// Only allow POST method
export async function GET() {
  return NextResponse.json({ error: "Method not allowed" }, { status: 405 });
}
