import { Hono } from 'hono';
import { streamSSE } from 'hono/streaming';
import type { ShadeEventEmitter, ShadeEvent } from '@shade/core';
import { errorToHttpStatus, ShadeError } from '@shade/core';
import type { PrekeyServerEvents, PrekeyServerEvent } from '@shade/server';
import { StateAggregator } from './state.js';
import { createAuthMiddleware } from './auth.js';

export interface ObserverOptions {
  token: string;
  clientEvents?: ShadeEventEmitter;
  serverEvents?: PrekeyServerEvents;
}

export function createObserverRoutes(options: ObserverOptions): Hono {
  const app = new Hono();
  const aggregator = new StateAggregator(options.clientEvents, options.serverEvents);
  const auth = createAuthMiddleware(options.token);

  // Global error handler
  app.onError((err, c) => {
    if (err instanceof ShadeError) {
      return c.json(err.toJSON(), errorToHttpStatus(err) as any);
    }
    console.error('[Shade Observer] Unhandled error:', err);
    return c.json({ error: 'Internal server error' }, 500);
  });

  // ─── Snapshot ──────────────────────────────────────────────
  app.get('/api/state', auth, (c) => {
    return c.json(aggregator.toJSON());
  });

  // ─── Live event stream ─────────────────────────────────────
  app.get('/api/events', auth, async (c) => {
    const sinceParam = c.req.query('since');
    const since = sinceParam ? parseInt(sinceParam, 10) : 0;

    return streamSSE(c, async (stream) => {
      // Send buffered events from `since` onwards
      if (options.clientEvents) {
        for (const e of options.clientEvents.getBufferedSince(since)) {
          await stream.writeSSE({
            event: 'shade',
            id: String(e.seq),
            data: JSON.stringify({ source: 'client', ...e }),
          });
        }
      }
      if (options.serverEvents) {
        for (const e of options.serverEvents.getBufferedSince(since)) {
          await stream.writeSSE({
            event: 'shade',
            id: String(e.seq),
            data: JSON.stringify({ source: 'server', ...e }),
          });
        }
      }

      // Subscribe to live events
      let closed = false;
      const queue: Array<{ source: 'client' | 'server'; event: ShadeEvent | PrekeyServerEvent }> = [];

      const unsubClient = options.clientEvents?.on((e) => {
        if (closed) return;
        queue.push({ source: 'client', event: e });
      });
      const unsubServer = options.serverEvents?.on((e) => {
        if (closed) return;
        queue.push({ source: 'server', event: e });
      });

      // Drain queue periodically (or on demand)
      try {
        while (!closed) {
          if (queue.length > 0) {
            const { source, event } = queue.shift()!;
            await stream.writeSSE({
              event: 'shade',
              id: String(event.seq),
              data: JSON.stringify({ source, ...event }),
            });
          } else {
            // Heartbeat every 15s to keep connection alive
            await stream.writeSSE({ event: 'heartbeat', data: 'ping' });
            await stream.sleep(15000);
          }
        }
      } catch {
        // Stream closed
      } finally {
        closed = true;
        unsubClient?.();
        unsubServer?.();
      }
    });
  });

  // ─── Health (no auth) ──────────────────────────────────────
  app.get('/health', (c) => {
    return c.json({ status: 'ok', service: 'shade-observer' });
  });

  return app;
}
