import { getSoulseekFavorites, addSoulseekFavorite, removeSoulseekFavorite } from 'officerdb';

// Officer-owned Soulseek routes — everything slskd itself has no concept of. These are served HERE, by
// the sidecar, not forwarded upstream: the platform API stays a pure auth-and-forward proxy forever, and
// slskd stays unforked. They live under the `/_officer/` prefix, mirroring `/_health`, so they can never
// collide with slskd's own surface (all of which is under `/api/v0/`).
//
// ─────────────────────────────────────────────────────────────────────────────────────────────────
// CONTRACT (reached from the browser as /api/slskd/_officer/…)
//   GET    /_officer/favorites                 → string[]  favourited peer usernames, alphabetical
//   POST   /_officer/favorites { username }    → { ok: true }  add (idempotent)
//   DELETE /_officer/favorites?username=<u>    → { ok: true }  remove (no-op if absent)
// ─────────────────────────────────────────────────────────────────────────────────────────────────

// The authenticated user id arrives in X-Officer-User, injected by the platform proxy after auth. We
// bind loopback only, so it's trusted — same arrangement as the music sidecar's per-user state routes.
const userIdOf = (req: Request): number | null => {
  const n = Number(req.headers.get('x-officer-user'));
  return Number.isInteger(n) && n > 0 ? n : null;
};

// Soulseek usernames are opaque to us and may contain spaces, so the only shapes we reject are ones
// no nick can have: empty, absurdly long, or containing control characters.
const USERNAME_MAX = 255;
const CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
const cleanUsername = (v: unknown): string | null => {
  if (typeof v !== 'string') return null;
  const u = v.trim();
  if (!u || u.length > USERNAME_MAX) return null;
  return CONTROL_CHARS.test(u) ? null : u;
};

/** Handles a `/_officer/*` request, or returns null if the path isn't one of ours. */
export async function handleOfficerRoute(req: Request, url: URL): Promise<Response | null> {
  const path = url.pathname;
  if (path !== '/_officer/favorites') return null;

  const userId = userIdOf(req);
  if (userId === null) return Response.json({ error: 'missing X-Officer-User' }, { status: 401 });

  if (req.method === 'GET') return Response.json(await getSoulseekFavorites(userId));

  if (req.method === 'POST') {
    const body = (await req.json().catch(() => null)) as { username?: unknown } | null;
    const username = cleanUsername(body?.username);
    if (!username) return Response.json({ error: 'username required' }, { status: 400 });
    await addSoulseekFavorite(userId, username);
    return Response.json({ ok: true });
  }

  if (req.method === 'DELETE') {
    const username = cleanUsername(url.searchParams.get('username'));
    if (!username) return Response.json({ error: 'username required' }, { status: 400 });
    await removeSoulseekFavorite(userId, username);
    return Response.json({ ok: true });
  }

  return new Response('method not allowed', { status: 405 });
}
