import { IncomingMessage, ServerResponse } from 'http';

export interface RequestContext {
  method: string;
  url: URL;
  pathname: string;
  headers: IncomingMessage['headers'];
  remoteAddress: string;
  bearerToken: string | null;
}

const MAX_BODY_BYTES = 1 * 1024 * 1024; // 1 MiB

export function getBearerToken(headers: IncomingMessage['headers']): string | null {
  const raw = headers.authorization;

  if (typeof raw !== 'string') {
    return null;
  }

  const trimmed = raw.trim();

  if (!/^bearer\s+/iu.test(trimmed)) {
    return null;
  }

  const token = trimmed.replace(/^bearer\s+/iu, '').trim();

  return token.length > 0 ? token : null;
}

export async function readJsonBody<T>(req: IncomingMessage): Promise<T> {
  const length = Number(req.headers['content-length'] ?? 0);

  if (length > MAX_BODY_BYTES) {
    throw new HttpError(413, 'Request body too large', 'BODY_TOO_LARGE');
  }

  const chunks: Buffer[] = [];

  let received = 0;

  for await (const chunk of req) {
    const buffer = chunk instanceof Buffer ? chunk : Buffer.from(chunk as string);

    received += buffer.length;

    if (received > MAX_BODY_BYTES) {
      throw new HttpError(413, 'Request body too large', 'BODY_TOO_LARGE');
    }

    chunks.push(buffer);
  }

  if (chunks.length === 0) {
    return {} as T;
  }

  const raw = Buffer.concat(chunks).toString('utf8');

  try {
    return JSON.parse(raw) as T;
  } catch {
    throw new HttpError(400, 'Invalid JSON body', 'INVALID_JSON');
  }
}

export function sendJson(res: ServerResponse, status: number, payload: unknown): void {
  if (!res.headersSent) {
    res.statusCode = status;
    res.setHeader('Content-Type', 'application/json; charset=utf-8');
    res.setHeader('Cache-Control', 'no-store');
  }

  res.end(JSON.stringify(payload));
}

export function sendText(res: ServerResponse, status: number, text: string, contentType = 'text/plain; charset=utf-8'): void {
  if (!res.headersSent) {
    res.statusCode = status;
    res.setHeader('Content-Type', contentType);
    res.setHeader('Cache-Control', 'no-store');
  }

  res.end(text);
}

export class HttpError extends Error {
  readonly status: number;
  readonly code: string;

  constructor(status: number, message: string, code: string) {
    super(message);
    this.status = status;
    this.code = code;
  }
}

export function sendError(res: ServerResponse, error: unknown): void {
  if (error instanceof HttpError) {
    sendJson(res, error.status, { error: error.message, errorCode: error.code });
    return;
  }

  const message = error instanceof Error ? error.message : 'Internal server error';

  sendJson(res, 500, { error: message, errorCode: 'INTERNAL_ERROR' });
}
