import { httpRouter } from "convex/server";
import { httpAction } from "./_generated/server";
import { internal } from "./_generated/api";
import type { WebhookEvent } from "@clerk/backend";
import { Webhook } from "svix";

const http = httpRouter();

http.route({
  path: "/clerk-users-webhook",
  method: "POST",
  handler: httpAction(async (ctx, request) => {
    const event = await validateRequest(request);
    if (!event) return new Response("Error", { status: 400 });

    switch (event.type) {
      case "user.created":
      case "user.updated":
        await ctx.runMutation(internal.users.upsertFromClerk, {
          externalId: event.data.id,
          name:
            `${event.data.first_name ?? ""} ${event.data.last_name ?? ""}`.trim(),
          email: event.data.email_addresses[0]?.email_address ?? "",
          avatarUrl: event.data.image_url ?? undefined,
        });
        break;
      case "user.deleted":
        if (event.data.id) {
          await ctx.runMutation(internal.users.deleteFromClerk, {
            externalId: event.data.id,
          });
        }
        break;
      default:
        console.log("Ignored webhook event:", event.type);
    }

    return new Response(null, { status: 200 });
  }),
});

async function validateRequest(
  req: Request,
): Promise<WebhookEvent | null> {
  const payload = await req.text();
  const headers = {
    "svix-id": req.headers.get("svix-id")!,
    "svix-timestamp": req.headers.get("svix-timestamp")!,
    "svix-signature": req.headers.get("svix-signature")!,
  };
  try {
    return new Webhook(process.env.CLERK_STOREFRONT_WEBHOOK_SECRET!).verify(
      payload,
      headers,
    ) as WebhookEvent;
  } catch {
    return null;
  }
}

http.route({
  path: "/clerk-admin-webhook",
  method: "POST",
  handler: httpAction(async (ctx, request) => {
    const event = await validateAdminRequest(request);
    if (!event) return new Response("Error", { status: 400 });

    switch (event.type) {
      case "user.created":
      case "user.updated": {
        const role = (event.data.public_metadata as Record<string, unknown>)
          ?.role;
        await ctx.runMutation(internal.users.upsertFromClerk, {
          externalId: event.data.id,
          name:
            `${event.data.first_name ?? ""} ${event.data.last_name ?? ""}`.trim(),
          email: event.data.email_addresses[0]?.email_address ?? "",
          avatarUrl: event.data.image_url ?? undefined,
          role:
            role === "admin" || role === "super_admin" ? role : undefined,
        });
        break;
      }
      case "user.deleted":
        if (event.data.id) {
          await ctx.runMutation(internal.users.deleteFromClerk, {
            externalId: event.data.id,
          });
        }
        break;
      default:
        console.log("Ignored admin webhook event:", event.type);
    }

    return new Response(null, { status: 200 });
  }),
});

async function validateAdminRequest(
  req: Request,
): Promise<WebhookEvent | null> {
  const payload = await req.text();
  const headers = {
    "svix-id": req.headers.get("svix-id")!,
    "svix-timestamp": req.headers.get("svix-timestamp")!,
    "svix-signature": req.headers.get("svix-signature")!,
  };
  try {
    return new Webhook(process.env.CLERK_ADMIN_WEBHOOK_SECRET!).verify(
      payload,
      headers,
    ) as WebhookEvent;
  } catch {
    return null;
  }
}

http.route({
  path: "/stripe/webhook",
  method: "POST",
  handler: httpAction(async (ctx, request) => {
    const body = await request.text();
    const signature = request.headers.get("stripe-signature");
    if (!signature) {
      return new Response("Missing stripe-signature header", { status: 400 });
    }

    const result = await ctx.runAction(internal.stripeActions.handleWebhook, {
      payload: body,
      signature,
    });

    return new Response(null, { status: result.success ? 200 : 400 });
  }),
});

http.route({
  path: "/shippo/webhook",
  method: "POST",
  handler: httpAction(async (ctx, request) => {
    const body = await request.text();
    // Always respond 200 first — Shippo retries on non-2xx, so we must not
    // let internal errors cause retry storms. Errors are logged in the action.
    await ctx.runAction(internal.shippoWebhook.handleTrackUpdated, { body });
    return new Response(null, { status: 200 });
  }),
});

export default http;