import { createRouter } from '../../create-router';
import { getMusicServerUrl } from './sidecar-server';

// Thin reverse-proxy for /api/music/*. The platform's ONLY job here is AUTH + FORWARDING. userMiddleware
// (upstream — this router mounts under the protected /api tree, so the media `?token=` path also works)
// authenticates; we forward the subpath + query + body + Range to the officer-music sidecar, which OWNS
// the entire /api/music/* contract: streaming, indexing, AND per-user state (favorites, now-playing,
// playlists) backed by Postgres. We inject the authenticated user id as `X-Officer-User` so the sidecar
// can serve that per-user state — the sidecar is loopback-only, so it trusts the header.
//
// This is a catch-all with no routes of its own: the full /api/music/* HTTP contract (paths, methods,
// SSE/response shapes) is documented at the top of the sidecar's fetch handler — src/servers/sidecar/music/index.ts.

export const musicRouter = createRouter();

const PREFIX = '/api/music';

musicRouter.all('/*', async (ctx) => {
  const baseUrl = getMusicServerUrl();
  if (!baseUrl) return ctx.text('Music sidecar not available', 503);

  const url = new URL(ctx.req.url);
  const subpath = url.pathname.slice(PREFIX.length) || '/';
  const target = `${baseUrl}${subpath}${url.search}`;

  // A from-scratch reindex holds this proxied connection open for minutes with no bytes flowing, which
  // the main server's 60s idle timeout would drop. Extend it to 30 min for the build/progress endpoints
  // (Bun passes the server as Hono's env). Matches the sidecar's own per-request extension.
  if (subpath === '/reindex' || subpath === '/reindex/stream') {
    const server = ctx.env as { timeout?: (req: Request, seconds: number) => void } | undefined;
    try {
      server?.timeout?.(ctx.req.raw, 1800);
    } catch {
      /* older Bun / no per-request timeout — the build still completes in the background */
    }
  }

  const method = ctx.req.method;
  const headers: Record<string, string> = {};
  const range = ctx.req.header('range');
  if (range) headers['Range'] = range;
  const contentType = ctx.req.header('content-type');
  if (contentType) headers['Content-Type'] = contentType;
  // Forward conditional-request headers so the sidecar's ETag(=v) revalidation works: a `no-cache`
  // artifact (cover/meta/…) gets a cheap 304 when unchanged, a fresh 200 when its version changed.
  const inm = ctx.req.header('if-none-match');
  if (inm) headers['If-None-Match'] = inm;
  // Forward the authenticated user id so the sidecar can serve its per-user state routes (favorites /
  // now-playing / playlists). The sidecar binds loopback only, so this header is trusted.
  headers['X-Officer-User'] = String(ctx.get('user').id);

  // Forward the request body for mutating methods (favorites/now-playing/playlist writes). Streaming +
  // reindex are GET/bodyless POST, so this is a no-op there.
  const hasBody = method !== 'GET' && method !== 'HEAD';

  let upstream: Response;
  try {
    upstream = await fetch(target, {
      method,
      headers,
      body: hasBody ? await ctx.req.arrayBuffer() : undefined,
    });
  } catch (err) {
    console.error('[music] proxy fetch failed', { target, error: String(err) });
    return ctx.text('Music sidecar unreachable', 502);
  }

  // Pass status + headers through and stream the body (206/Content-Range/X-Audio-Duration included).
  return new Response(upstream.body, { status: upstream.status, headers: new Headers(upstream.headers) });
});
