import type { Channel } from '@zyphon/shared-types';

export interface MessageMetricEvent {
  channel: Channel;
  provider: string | null;
  status: 'delivered' | 'failed' | 'accepted';
  latencyMs: number;
  timestamp: number;
}

export class MetricsCollector {
  private events: MessageMetricEvent[] = [];
  private maxHistory = 10000;

  public record(event: MessageMetricEvent): void {
    this.events.push(event);
    if (this.events.length > this.maxHistory) {
      this.events.shift();
    }
  }

  public getSummary(): {
    total: number;
    delivered: number;
    failed: number;
    accepted: number;
    successRate: number;
    byChannel: Record<string, { total: number; delivered: number; failed: number }>;
    byProvider: Record<string, { total: number; delivered: number; failed: number }>;
    latencyP50: number;
    latencyP95: number;
    latencyP99: number;
  } {
    const total = this.events.length;
    let delivered = 0;
    let failed = 0;
    let accepted = 0;
    const byChannel: Record<string, { total: number; delivered: number; failed: number }> = {};
    const byProvider: Record<string, { total: number; delivered: number; failed: number }> = {};
    const latencies: number[] = [];

    for (const e of this.events) {
      if (e.status === 'delivered') delivered++;
      else if (e.status === 'failed') failed++;
      else if (e.status === 'accepted') accepted++;

      latencies.push(e.latencyMs);

      // byChannel
      if (!byChannel[e.channel]) {
        byChannel[e.channel] = { total: 0, delivered: 0, failed: 0 };
      }
      byChannel[e.channel]!.total++;
      if (e.status === 'delivered') byChannel[e.channel]!.delivered++;
      if (e.status === 'failed') byChannel[e.channel]!.failed++;

      // byProvider
      const prov = e.provider || 'none';
      if (!byProvider[prov]) {
        byProvider[prov] = { total: 0, delivered: 0, failed: 0 };
      }
      byProvider[prov]!.total++;
      if (e.status === 'delivered') byProvider[prov]!.delivered++;
      if (e.status === 'failed') byProvider[prov]!.failed++;
    }

    latencies.sort((a, b) => a - b);
    const p50 = latencies[Math.floor(latencies.length * 0.5)] || 0;
    const p95 = latencies[Math.floor(latencies.length * 0.95)] || 0;
    const p99 = latencies[Math.floor(latencies.length * 0.99)] || 0;

    return {
      total,
      delivered,
      failed,
      accepted,
      successRate: total > 0 ? Math.round((delivered / total) * 100) : 100,
      byChannel,
      byProvider,
      latencyP50: p50,
      latencyP95: p95,
      latencyP99: p99,
    };
  }

  public toPrometheusFormat(): string {
    const summary = this.getSummary();
    const lines: string[] = [
      '# HELP zyphon_messages_total Total messages processed by Zyphon',
      '# TYPE zyphon_messages_total counter',
      `zyphon_messages_total ${summary.total}`,
      '# HELP zyphon_messages_delivered_total Total messages delivered',
      '# TYPE zyphon_messages_delivered_total counter',
      `zyphon_messages_delivered_total ${summary.delivered}`,
      '# HELP zyphon_messages_failed_total Total messages failed',
      '# TYPE zyphon_messages_failed_total counter',
      `zyphon_messages_failed_total ${summary.failed}`,
      '# HELP zyphon_latency_milliseconds Latency percentiles in milliseconds',
      '# TYPE zyphon_latency_milliseconds gauge',
      `zyphon_latency_milliseconds{quantile="0.5"} ${summary.latencyP50}`,
      `zyphon_latency_milliseconds{quantile="0.95"} ${summary.latencyP95}`,
      `zyphon_latency_milliseconds{quantile="0.99"} ${summary.latencyP99}`,
    ];

    for (const [ch, data] of Object.entries(summary.byChannel)) {
      lines.push(`zyphon_channel_messages_total{channel="${ch}"} ${data.total}`);
    }

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

