import { Hono } from 'hono';

/**
 * Prometheus-style metrics for the Shade Prekey Server.
 *
 * Exports counters for each route + a histogram of request durations.
 * Format: text/plain Prometheus exposition format.
 */

interface CounterMap {
  [labels: string]: number;
}

class Metrics {
  // Counters
  private requests: CounterMap = {};
  private errors: CounterMap = {};

  // Histogram buckets (milliseconds)
  private readonly latencyBuckets = [1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000];
  private latencyCounts: Record<string, number[]> = {};
  private latencySum: CounterMap = {};
  private latencyCount: CounterMap = {};

  recordRequest(route: string, status: number, durationMs: number): void {
    const key = `${route}|${status}`;
    this.requests[key] = (this.requests[key] ?? 0) + 1;

    if (status >= 400) {
      this.errors[key] = (this.errors[key] ?? 0) + 1;
    }

    if (!this.latencyCounts[route]) {
      this.latencyCounts[route] = new Array(this.latencyBuckets.length).fill(0);
      this.latencySum[route] = 0;
      this.latencyCount[route] = 0;
    }

    for (let i = 0; i < this.latencyBuckets.length; i++) {
      if (durationMs <= this.latencyBuckets[i]!) {
        this.latencyCounts[route]![i]!++;
      }
    }
    this.latencySum[route] = (this.latencySum[route] ?? 0) + durationMs;
    this.latencyCount[route] = (this.latencyCount[route] ?? 0) + 1;
  }

  /** Render in Prometheus exposition format */
  render(): string {
    const lines: string[] = [];

    lines.push('# HELP shade_requests_total Total HTTP requests');
    lines.push('# TYPE shade_requests_total counter');
    for (const [key, value] of Object.entries(this.requests)) {
      const [route, status] = key.split('|');
      lines.push(`shade_requests_total{route="${route}",status="${status}"} ${value}`);
    }

    lines.push('# HELP shade_errors_total Total HTTP errors (4xx/5xx)');
    lines.push('# TYPE shade_errors_total counter');
    for (const [key, value] of Object.entries(this.errors)) {
      const [route, status] = key.split('|');
      lines.push(`shade_errors_total{route="${route}",status="${status}"} ${value}`);
    }

    lines.push('# HELP shade_request_duration_ms Request duration in milliseconds');
    lines.push('# TYPE shade_request_duration_ms histogram');
    for (const route of Object.keys(this.latencyCounts)) {
      const counts = this.latencyCounts[route]!;
      for (let i = 0; i < this.latencyBuckets.length; i++) {
        lines.push(`shade_request_duration_ms_bucket{route="${route}",le="${this.latencyBuckets[i]}"} ${counts[i]}`);
      }
      lines.push(`shade_request_duration_ms_bucket{route="${route}",le="+Inf"} ${this.latencyCount[route]}`);
      lines.push(`shade_request_duration_ms_sum{route="${route}"} ${this.latencySum[route]}`);
      lines.push(`shade_request_duration_ms_count{route="${route}"} ${this.latencyCount[route]}`);
    }

    return lines.join('\n') + '\n';
  }
}

export const metrics = new Metrics();

/** Hono middleware that records request metrics */
export function metricsMiddleware() {
  return async (c: any, next: any) => {
    const start = Date.now();
    await next();
    const duration = Date.now() - start;
    const route = c.req.routePath ?? c.req.path;
    metrics.recordRequest(route, c.res.status, duration);
  };
}

/** Mount the /metrics endpoint */
export function createMetricsRoutes(): Hono {
  const app = new Hono();
  app.get('/metrics', (c) => {
    c.header('Content-Type', 'text/plain; version=0.0.4');
    return c.body(metrics.render());
  });
  return app;
}
