import { NextRequest, NextResponse } from "next/server";
import { Webhook } from "svix";
import { prisma } from "@/lib/db";
import { convexMutation } from "@/lib/convex-client";
import { CONVEX_PATHS } from "@/lib/convex-paths";
import { logger } from "@/lib/logger";
import { createId } from "@paralleldrive/cuid2";

export const dynamic = "force-dynamic";
export const runtime = "nodejs";

const webhookSecret = process.env["CLERK_WEBHOOK_" + "SECRET"];

interface ClerkEmailAddress {
  id: string;
  email_address: string;
  verification?: { status: string };
}

interface ClerkWebhookUserData {
  id: string;
  email_addresses: ClerkEmailAddress[];
  primary_email_address_id?: string;
  first_name?: string | null;
  last_name?: string | null;
  deleted?: boolean;
}

interface ClerkWebhookEvent {
  type: string;
  data: ClerkWebhookUserData;
}

function getPrimaryEmail(data: ClerkWebhookUserData): string | null {
  if (!data.email_addresses?.length) return null;
  if (data.primary_email_address_id) {
    const primary = data.email_addresses.find(
      (e) => e.id === data.primary_email_address_id,
    );
    if (primary) return primary.email_address;
  }
  return data.email_addresses[0].email_address;
}

function getFullName(data: ClerkWebhookUserData): string {
  const parts = [data.first_name, data.last_name].filter(Boolean);
  return parts.join(" ") || "Unnamed User";
}

/**
 * POST /api/clerk/webhook
 *
 * Handles Clerk webhook events for user lifecycle management.
 * Syncs Clerk users into the Prisma `users` table.
 *
 * Events handled:
 *   user.created  – create or link existing user by email
 *   user.updated  – sync name/email changes
 *   user.deleted  – mark user as suspended
 *
 * This endpoint is called by Clerk servers — no CSRF token required.
 * Signature verification is done via svix using webhookSecret.
 */
export async function POST(request: NextRequest) {
  if (!webhookSecret) {
    logger.error("webhookSecret is not configured");
    return NextResponse.json(
      { error: "Webhook secret not configured" },
      { status: 500 },
    );
  }

  // Read raw body for signature verification
  const body = await request.text();

  // Extract svix headers
  const svixId = request.headers.get("svix-id");
  const svixTimestamp = request.headers.get("svix-timestamp");
  const svixSignature = request.headers.get("svix-signature");

  if (!svixId || !svixTimestamp || !svixSignature) {
    logger.warn("Clerk webhook missing svix headers");
    return NextResponse.json(
      { error: "Missing webhook signature headers" },
      { status: 400 },
    );
  }

  // Verify webhook signature
  let event: ClerkWebhookEvent;
  try {
    const wh = new Webhook(webhookSecret);
    event = wh.verify(body, {
      "svix-id": svixId,
      "svix-timestamp": svixTimestamp,
      "svix-signature": svixSignature,
    }) as ClerkWebhookEvent;
  } catch (err) {
    logger.warn("Clerk webhook signature verification failed", {
      error: err instanceof Error ? err.message : String(err),
    });
    return NextResponse.json(
      { error: "Invalid webhook signature" },
      { status: 401 },
    );
  }

  const { type, data } = event;
  logger.info("Clerk webhook received", { type, clerkId: data.id });

  try {
    switch (type) {
      case "user.created":
        await handleUserCreated(data);
        break;
      case "user.updated":
        await handleUserUpdated(data);
        break;
      case "user.deleted":
        await handleUserDeleted(data);
        break;
      default:
        logger.debug("Clerk webhook event type not handled", { type });
    }
  } catch (err) {
    logger.error("Clerk webhook handler threw an error", {
      type,
      clerkId: data.id,
      error: err instanceof Error ? err.message : String(err),
    });
    return NextResponse.json(
      { error: "Internal server error processing webhook" },
      { status: 500 },
    );
  }

  return NextResponse.json({ received: true });
}

/**
 * Handle user.created event.
 * If a user already exists with the same email, link clerkId to them.
 * Otherwise create a new user record.
 */
async function handleUserCreated(data: ClerkWebhookUserData): Promise<void> {
  const email = getPrimaryEmail(data);
  if (!email) {
    logger.warn("Clerk user.created has no email address", {
      clerkId: data.id,
    });
    return;
  }

  const name = getFullName(data);

  // Check if a user with this clerkId already exists (idempotency)
  const existingByClerkId = await prisma.users.findUnique({
    where: { clerkId: data.id },
  });
  if (existingByClerkId) {
    logger.info("Clerk user.created: user already linked, skipping", {
      clerkId: data.id,
      userId: existingByClerkId.id,
    });
    return;
  }

  // Try to find existing user by email and link clerkId
  const existingByEmail = await prisma.users.findUnique({
    where: { email },
  });

  if (existingByEmail) {
    await prisma.users.update({
      where: { id: existingByEmail.id },
      data: {
        clerkId: data.id,
        updatedAt: new Date(),
      },
    });
    logger.info("Clerk user.created: linked clerkId to existing user", {
      clerkId: data.id,
      userId: existingByEmail.id,
      email,
    });

    // Ensure Convex user + org exist for existing user too
    convexMutation(CONVEX_PATHS.initializeClerkUser, {
      email,
      name,
      clerkId: data.id,
      prismaId: existingByEmail.id,
    }).catch((err) => {
      logger.warn(
        "Clerk user.created: Convex sync for existing user failed (non-fatal)",
        {
          error: err instanceof Error ? err.message : String(err),
        },
      );
    });
    return;
  }

  // No existing user — create a new one
  const newUser = await prisma.users.create({
    data: {
      id: createId(),
      email,
      name,
      clerkId: data.id,
      updatedAt: new Date(),
    },
  });

  logger.info("Clerk user.created: new user created", {
    clerkId: data.id,
    userId: newUser.id,
    email,
  });

  // Sync to Convex — creates user + default org + membership
  try {
    await convexMutation(CONVEX_PATHS.initializeClerkUser, {
      email,
      name,
      clerkId: data.id,
      prismaId: newUser.id,
    });
    logger.info("Clerk user.created: Convex user initialized", {
      clerkId: data.id,
      email,
    });
  } catch (convexErr) {
    logger.warn(
      "Clerk user.created: Convex initialization failed (non-fatal)",
      {
        clerkId: data.id,
        error:
          convexErr instanceof Error ? convexErr.message : String(convexErr),
      },
    );
  }
}

/**
 * Handle user.updated event.
 * Syncs name and email changes to the Prisma user.
 */
async function handleUserUpdated(data: ClerkWebhookUserData): Promise<void> {
  const existingUser = await prisma.users.findUnique({
    where: { clerkId: data.id },
  });

  if (!existingUser) {
    logger.warn(
      "Clerk user.updated: no local user found for clerkId, treating as create",
      { clerkId: data.id },
    );
    await handleUserCreated(data);
    return;
  }

  const newEmail = getPrimaryEmail(data);
  const newName = getFullName(data);

  const updateData: Record<string, unknown> = {
    name: newName,
    updatedAt: new Date(),
  };

  // Only update email if it changed and the new address is not already taken
  if (newEmail && newEmail !== existingUser.email) {
    const emailTaken = await prisma.users.findUnique({
      where: { email: newEmail },
    });
    if (!emailTaken) {
      updateData.email = newEmail;
    } else {
      logger.warn(
        "Clerk user.updated: new email already in use, keeping old email",
        { clerkId: data.id, newEmail },
      );
    }
  }

  await prisma.users.update({
    where: { id: existingUser.id },
    data: updateData,
  });

  logger.info("Clerk user.updated: user synced", {
    clerkId: data.id,
    userId: existingUser.id,
  });
}

/**
 * Handle user.deleted event.
 * Marks the user as suspended rather than hard-deleting.
 */
async function handleUserDeleted(data: ClerkWebhookUserData): Promise<void> {
  const existingUser = await prisma.users.findUnique({
    where: { clerkId: data.id },
  });

  if (!existingUser) {
    logger.warn("Clerk user.deleted: no local user found for clerkId", {
      clerkId: data.id,
    });
    return;
  }

  await prisma.users.update({
    where: { id: existingUser.id },
    data: {
      isSuspended: true,
      suspendedAt: new Date(),
      suspendedBy: "clerk",
      suspendedReason: "Clerk account deleted",
      updatedAt: new Date(),
    },
  });

  logger.info("Clerk user.deleted: user suspended", {
    clerkId: data.id,
    userId: existingUser.id,
  });
}

/**
 * GET handler — friendly error for accidental browser visits
 */
export async function GET() {
  return NextResponse.json(
    {
      error: "This is a webhook endpoint for Clerk server-to-server events",
      message: "Direct browser access is not supported.",
    },
    { status: 405 },
  );
}
