/**
 * Notification REST API — mounted at /api/notifications
 *
 * All endpoints require Bearer auth (same pattern as spaces.ts).
 */

import { Hono } from "hono";
import {
  verifyEncryptIDToken,
  extractToken,
} from "@encryptid/sdk/server";
import {
  getUserNotifications,
  getUnreadCount,
  markNotificationRead,
  markAllNotificationsRead,
  dismissNotification,
  getNotificationPreferences,
  upsertNotificationPreferences,
  savePushSubscription,
  deletePushSubscriptionByEndpoint,
} from "../src/encryptid/db";

export const notificationRouter = new Hono();

// ── Auth helper ──
async function requireAuth(req: Request) {
  const token = extractToken(req.headers);
  if (!token) return null;
  try {
    return await verifyEncryptIDToken(token);
  } catch {
    return null;
  }
}

// ── GET / — Paginated notification list ──
notificationRouter.get("/", async (c) => {
  try {
    const claims = await requireAuth(c.req.raw);
    if (!claims) return c.json({ error: "Authentication required" }, 401);

    const url = new URL(c.req.url);
    const unreadOnly = url.searchParams.get("unread") === "true";
    const category = url.searchParams.get("category") || undefined;
    const limit = Math.min(Number(url.searchParams.get("limit")) || 50, 100);
    const offset = Number(url.searchParams.get("offset")) || 0;

    const notifications = await getUserNotifications(claims.sub, {
      unreadOnly,
      category,
      limit,
      offset,
    });

    return c.json({ notifications });
  } catch (err) {
    console.error("[notifications] GET / failed:", err instanceof Error ? err.message : err);
    return c.json({ notifications: [] });
  }
});

// ── GET /count — Lightweight unread count (polling fallback) ──
notificationRouter.get("/count", async (c) => {
  try {
    const claims = await requireAuth(c.req.raw);
    if (!claims) return c.json({ error: "Authentication required" }, 401);

    const count = await getUnreadCount(claims.sub);
    return c.json({ unreadCount: count });
  } catch (err) {
    console.error("[notifications] GET /count failed:", err instanceof Error ? err.message : err);
    return c.json({ unreadCount: 0 });
  }
});

// ── PATCH /:id/read — Mark one notification as read ──
notificationRouter.patch("/:id/read", async (c) => {
  const claims = await requireAuth(c.req.raw);
  if (!claims) return c.json({ error: "Authentication required" }, 401);

  const id = c.req.param("id");
  const ok = await markNotificationRead(id, claims.sub);
  if (!ok) return c.json({ error: "Notification not found" }, 404);
  return c.json({ ok: true });
});

// ── POST /read-all — Mark all read (optional scope) ──
notificationRouter.post("/read-all", async (c) => {
  const claims = await requireAuth(c.req.raw);
  if (!claims) return c.json({ error: "Authentication required" }, 401);

  let spaceSlug: string | undefined;
  let category: string | undefined;
  try {
    const body = await c.req.json();
    spaceSlug = body.spaceSlug;
    category = body.category;
  } catch {
    // No body is fine — mark everything read
  }

  const count = await markAllNotificationsRead(claims.sub, { spaceSlug, category });
  return c.json({ ok: true, markedRead: count });
});

// ── DELETE /:id — Dismiss/archive a notification ──
notificationRouter.delete("/:id", async (c) => {
  const claims = await requireAuth(c.req.raw);
  if (!claims) return c.json({ error: "Authentication required" }, 401);

  const id = c.req.param("id");
  const ok = await dismissNotification(id, claims.sub);
  if (!ok) return c.json({ error: "Notification not found" }, 404);
  return c.json({ ok: true });
});

// ── GET /preferences — Get notification preferences ──
notificationRouter.get("/preferences", async (c) => {
  const claims = await requireAuth(c.req.raw);
  if (!claims) return c.json({ error: "Authentication required" }, 401);

  const prefs = await getNotificationPreferences(claims.sub);
  return c.json({ preferences: prefs || {
    emailEnabled: true,
    pushEnabled: true,
    quietHoursStart: null,
    quietHoursEnd: null,
    mutedSpaces: [],
    mutedCategories: [],
    digestFrequency: 'none',
  }});
});

// ── PATCH /preferences — Update notification preferences ──
notificationRouter.patch("/preferences", async (c) => {
  const claims = await requireAuth(c.req.raw);
  if (!claims) return c.json({ error: "Authentication required" }, 401);

  const body = await c.req.json();
  const prefs = await upsertNotificationPreferences(claims.sub, body);
  return c.json({ preferences: prefs });
});

// ============================================================================
// WEB PUSH ENDPOINTS
// ============================================================================

// ── GET /push/vapid-public-key — Return public VAPID key (no auth) ──
notificationRouter.get("/push/vapid-public-key", (c) => {
  const key = process.env.VAPID_PUBLIC_KEY;
  if (!key) return c.json({ error: "Push not configured" }, 503);
  return c.json({ publicKey: key });
});

// ── POST /push/subscribe — Save push subscription (auth required) ──
notificationRouter.post("/push/subscribe", async (c) => {
  const claims = await requireAuth(c.req.raw);
  if (!claims) return c.json({ error: "Authentication required" }, 401);

  const body = await c.req.json();
  const { endpoint, keys } = body;
  if (!endpoint || !keys?.p256dh || !keys?.auth) {
    return c.json({ error: "Invalid subscription object" }, 400);
  }

  await savePushSubscription({
    id: crypto.randomUUID(),
    userDid: claims.sub,
    endpoint,
    keyP256dh: keys.p256dh,
    keyAuth: keys.auth,
    userAgent: c.req.header("user-agent"),
  });

  return c.json({ ok: true });
});

// ── POST /push/unsubscribe — Remove push subscription (auth required) ──
notificationRouter.post("/push/unsubscribe", async (c) => {
  const claims = await requireAuth(c.req.raw);
  if (!claims) return c.json({ error: "Authentication required" }, 401);

  const body = await c.req.json();
  if (!body.endpoint) return c.json({ error: "endpoint required" }, 400);

  await deletePushSubscriptionByEndpoint(body.endpoint);
  return c.json({ ok: true });
});
