import { UnauthorizedError, ConfigurationError } from '@shade/core';
import type { Context, Next } from 'hono';

/**
 * Bearer token middleware for the observer.
 *
 * Reads token from `Authorization: Bearer <token>` header.
 * For SSE endpoints (where browsers can't set headers), also accepts
 * `?token=<token>` query parameter.
 *
 * Throws ConfigurationError if SHADE_OBSERVER_TOKEN is empty (refuses to start).
 */
export function createAuthMiddleware(token: string) {
  if (!token || token.length < 16) {
    throw new ConfigurationError(
      'SHADE_OBSERVER_TOKEN must be set and at least 16 characters. Refusing to start.',
    );
  }

  return async (c: Context, next: Next) => {
    const header = c.req.header('Authorization');
    let provided: string | null = null;

    if (header && header.startsWith('Bearer ')) {
      provided = header.slice(7);
    } else {
      // Allow query string for SSE (EventSource can't set headers)
      provided = c.req.query('token') ?? null;
    }

    if (!provided || !constantTimeStringEqual(provided, token)) {
      throw new UnauthorizedError('Invalid or missing observer token');
    }

    await next();
  };
}

/** Constant-time string comparison (avoids timing attacks on token check) */
function constantTimeStringEqual(a: string, b: string): boolean {
  if (a.length !== b.length) return false;
  let diff = 0;
  for (let i = 0; i < a.length; i++) {
    diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
  }
  return diff === 0;
}
