import { randomUUID } from "crypto";
import type { NextApiRequest, NextApiResponse } from "next";
import type { Readable } from "node:stream";

import { createNextApiContext } from "@kan/api/trpc";
import * as subscriptionRepo from "@kan/db/repository/subscription.repo";
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
import { createLogger } from "@kan/logger";
import { createStripeClient } from "@kan/stripe";

const log = createLogger("api");

async function buffer(readable: Readable) {
  const chunks = [];
  for await (const chunk of readable) {
    chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
  }
  return Buffer.concat(chunks);
}

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  const stripe = createStripeClient();
  const start = Date.now();
  const requestId = randomUUID();
  const procedure = req.url?.split("?")[0] ?? "/api/stripe/webhook";

  if (req.method !== "POST") {
    return res.status(405).json({ message: "Method not allowed" });
  }

  const sig = req.headers["stripe-signature"];

  if (!sig) {
    return res.status(400).json({ message: "No signature found" });
  }

  try {
    const buf = await buffer(req);
    const rawBody = buf.toString("utf8");

    const event = stripe.webhooks.constructEvent(
      rawBody,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET_LEGACY!,
    );

    const { db } = await createNextApiContext(req);

    switch (event.type) {
      case "checkout.session.completed": {
        const checkoutSession = event.data.object;
        const meta = checkoutSession.metadata;

        if (!meta?.workspacePublicId) break;

        const plan = meta.plan === "team" ? "team" : ("pro" as const);

        if (
          meta.isNewWorkspace === "true" &&
          meta.workspaceName &&
          meta.userId &&
          meta.userEmail
        ) {
          const existing = await workspaceRepo.getByPublicId(
            db,
            meta.workspacePublicId,
          );

          if (!existing) {
            const slug = meta.workspaceSlug ?? meta.workspacePublicId;

            await workspaceRepo.create(db, {
              publicId: meta.workspacePublicId,
              name: meta.workspaceName,
              slug,
              plan,
              createdBy: meta.userId,
              createdByEmail: meta.userEmail,
              ...(meta.workspaceDescription && {
                description: meta.workspaceDescription,
              }),
            });

            await subscriptionRepo.create(db, {
              plan,
              referenceId: meta.workspacePublicId,
              userId: meta.userId,
              stripeCustomerId: checkoutSession.customer as string,
              status: "active",
            });
          }
        } else {
          await workspaceRepo.update(db, meta.workspacePublicId, {
            plan,
            ...(plan === "pro" &&
              meta.workspaceSlug && { slug: meta.workspaceSlug }),
          });
        }

        break;
      }
      default:
        log.warn({ eventType: event.type }, "Unhandled Stripe event type");
    }

    log.info(
      {
        requestId,
        procedure,
        transport: "rest",
        duration: Date.now() - start,
        status: 200,
        input: { eventType: event.type, eventId: event.id },
      },
      "API OK",
    );

    return res.status(200).json({ received: true });
  } catch (err) {
    log.error(
      {
        requestId,
        procedure,
        transport: "rest",
        duration: Date.now() - start,
        status: 400,
        err,
      },
      "API error",
    );
    return res.status(400).json({ message: "Webhook handler failed" });
  }
}

export const config = {
  api: {
    bodyParser: false,
  },
};
