import { Router, type IRouter } from "express";
import { z } from "zod";
import { requireAuth } from "../middlewares/auth";
import {
  getVapid,
  upsertSubscription,
  removeSubscription,
} from "../lib/push";

const router: IRouter = Router();

const SubscribeBody = z.object({
  endpoint: z.string().url().max(2048),
  keys: z.object({
    p256dh: z.string().min(1).max(512),
    auth: z.string().min(1).max(512),
  }),
});

const UnsubscribeBody = z.object({
  endpoint: z.string().url().max(2048),
});

router.get("/push/vapid-public-key", async (_req, res): Promise<void> => {
  const vapid = await getVapid();
  if (!vapid) {
    res.status(503).json({ error: "Web Push not configured" });
    return;
  }
  res.json({ publicKey: vapid.publicKey });
});

router.post("/push/subscribe", requireAuth, async (req, res): Promise<void> => {
  const parsed = SubscribeBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const userId = req.session.userId!;
  const ua = (req.headers["user-agent"] as string | undefined) ?? null;
  await upsertSubscription(userId, {
    endpoint: parsed.data.endpoint,
    p256dh: parsed.data.keys.p256dh,
    auth: parsed.data.keys.auth,
    userAgent: ua,
  });
  res.json({ success: true });
});

async function handleUnsubscribe(
  req: Parameters<typeof requireAuth>[0] extends never ? never : Parameters<Parameters<typeof router.post>[2]>[0],
  res: Parameters<Parameters<typeof router.post>[2]>[1],
): Promise<void> {
  // Accept the endpoint from either the JSON body (POST) or the query
  // string (DELETE). Browsers don't always send a body with DELETE, so
  // the query-string form keeps the spec-friendly verb usable.
  const candidate = {
    endpoint:
      (req.body && typeof req.body.endpoint === "string"
        ? req.body.endpoint
        : undefined) ?? (req.query.endpoint as string | undefined),
  };
  const parsed = UnsubscribeBody.safeParse(candidate);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const userId = req.session.userId!;
  await removeSubscription(userId, parsed.data.endpoint);
  res.json({ success: true });
}

router.post("/push/unsubscribe", requireAuth, handleUnsubscribe);
// Spec-aligned alias: DELETE /api/push/subscribe?endpoint=... removes
// the caller's subscription. The POST form above stays for backward
// compat with clients that already shipped.
router.delete("/push/subscribe", requireAuth, handleUnsubscribe);

export default router;
