/**
 * Fleet Prometheus exposition (§4) — pure renderer unit tests.
 */

import { describe, it, expect, afterEach } from 'vitest';
import { renderFleetMetricsProm, scrapeTokenMatches, type FleetPromInput } from './prometheus.js';
import type { FleetMetrics } from './coordinator.js';
import type { ReaperStats } from './reaper.js';

function metrics(over: Partial<FleetMetrics> = {}): FleetMetrics {
  return {
    productId: 'lysnrai',
    generatedAt: '2026-06-01T00:00:00.000Z',
    jobs: {
      total: 3,
      byStage: { queued: 2, shipped: 1 },
      queueDepth: 2,
      blocked: 0,
      active: 0,
      oldestQueuedAgeMs: 5000,
    },
    factories: {
      total: 2,
      live: 1,
      stale: 1,
      byHealth: { ok: 1, degraded: 0, down: 0 },
      seatsUsed: 1,
      seatsTotal: 4,
      utilizationPct: 25,
    },
    budget: null,
    alerts: [],
    ...over,
  } as FleetMetrics;
}

const reaper: ReaperStats = {
  running: true,
  startedAt: null,
  lastReclaimAt: null,
  lastSweepAt: null,
  totals: {
    expiredReclaimed: 5,
    staleReclaimed: 2,
    leasesDeleted: 10,
    factoriesDeleted: 0,
    tokensDeleted: 0,
    jobsDeleted: 3,
    runsDeleted: 0,
    eventsDeleted: 0,
    artifactsDeleted: 0,
  },
};

const base: FleetPromInput = {
  products: [{ productId: 'lysnrai', metrics: metrics() }],
  reaper,
  breakers: [],
};

describe('renderFleetMetricsProm', () => {
  it('emits HELP/TYPE headers and product-labelled gauges', () => {
    const out = renderFleetMetricsProm(base);
    expect(out).toContain('# TYPE fleet_queue_depth gauge');
    expect(out).toContain('fleet_queue_depth{product="lysnrai"} 2');
    expect(out).toContain('fleet_utilization_pct{product="lysnrai"} 25');
    expect(out).toContain('fleet_jobs_by_stage{product="lysnrai",stage="queued"} 2');
  });

  it('emits global reaper counters and a running gauge', () => {
    const out = renderFleetMetricsProm(base);
    expect(out).toContain('# TYPE fleet_reaper_expired_reclaimed_total counter');
    expect(out).toContain('fleet_reaper_expired_reclaimed_total 5');
    expect(out).toContain('fleet_reaper_jobs_deleted_total 3');
    expect(out).toContain('fleet_reaper_running 1');
  });

  it('emits one alert series per active alert, labelled by code + level', () => {
    const out = renderFleetMetricsProm({
      ...base,
      products: [
        {
          productId: 'lysnrai',
          metrics: metrics({
            alerts: [{ level: 'warning', code: 'dead_letter', message: 'x' }],
          }),
        },
      ],
    });
    expect(out).toContain(
      'fleet_alert_active{product="lysnrai",code="dead_letter",level="warning"} 1'
    );
  });

  it('emits budget gauges only when a budget is configured', () => {
    const withBudget = renderFleetMetricsProm({
      ...base,
      products: [
        {
          productId: 'lysnrai',
          metrics: metrics({
            budget: {
              ceilingUsd: 100,
              spentUsd: 40,
              status: 'active',
              window: 'monthly',
              projectedUsd: 300,
              engines: [],
            },
          }),
        },
      ],
    });
    expect(withBudget).toContain('fleet_budget_spent_usd{product="lysnrai"} 40');
    expect(withBudget).toContain('fleet_budget_projected_usd{product="lysnrai"} 300');
    // no budget ⇒ no budget series
    expect(renderFleetMetricsProm(base)).not.toContain('fleet_budget_spent_usd');
  });

  it('counts and labels only tripped breakers', () => {
    const out = renderFleetMetricsProm({
      ...base,
      breakers: [
        {
          factoryId: 'fac_1',
          engine: 'codex',
          state: 'OPEN',
          failureCount: 3,
          lastFailureAt: null,
        },
        {
          factoryId: 'fac_1',
          engine: 'devin',
          state: 'CLOSED',
          failureCount: 0,
          lastFailureAt: null,
        },
      ],
    });
    expect(out).toContain(
      'fleet_engine_breaker_open{factory="fac_1",engine="codex",state="OPEN"} 1'
    );
    expect(out).not.toContain('engine="devin"');
    expect(out).toContain('fleet_engine_breaker_open_count 1');
  });

  it('emits autoscale series only when a recommendation is attached', () => {
    const out = renderFleetMetricsProm({
      ...base,
      products: [
        {
          productId: 'lysnrai',
          metrics: metrics(),
          recommendation: {
            productId: 'lysnrai',
            action: 'scale_out',
            recommendedSeats: 6,
            delta: 2,
            reason: 'x',
            urgency: 'warning',
            signals: {
              queueDepth: 2,
              blocked: 0,
              active: 0,
              seatsUsed: 4,
              seatsTotal: 4,
              utilizationPct: 100,
              pressure: 2,
              alertCodes: ['saturated'],
            },
            generatedAt: '2026-06-01T00:00:00.000Z',
          },
        },
      ],
    });
    expect(out).toContain('fleet_autoscale_recommended_seats{product="lysnrai"} 6');
    expect(out).toContain('fleet_autoscale_delta{product="lysnrai"} 2');
    expect(out).toContain('fleet_autoscale_pressure{product="lysnrai"} 2');
    expect(out).toContain('fleet_autoscale_action{product="lysnrai",action="scale_out"} 1');
    expect(out).toContain('fleet_autoscale_action{product="lysnrai",action="scale_in"} 0');
    // no recommendation attached ⇒ no autoscale series
    expect(renderFleetMetricsProm(base)).not.toContain('fleet_autoscale_');
  });

  it('escapes special characters in label values', () => {
    const out = renderFleetMetricsProm({
      ...base,
      breakers: [
        {
          factoryId: 'fac"x',
          engine: 'a\\b',
          state: 'HALF_OPEN',
          failureCount: 1,
          lastFailureAt: null,
        },
      ],
    });
    expect(out).toContain('factory="fac\\"x",engine="a\\\\b"');
  });
});

describe('scrapeTokenMatches', () => {
  afterEach(() => {
    delete process.env.FLEET_METRICS_TOKEN;
  });

  it('is false when no token is configured (forces admin-auth fallback)', () => {
    expect(scrapeTokenMatches('Bearer anything')).toBe(false);
  });

  it('matches a correct bearer token and rejects a wrong / missing one', () => {
    process.env.FLEET_METRICS_TOKEN = 'secret';
    expect(scrapeTokenMatches('Bearer secret')).toBe(true);
    expect(scrapeTokenMatches('Bearer nope')).toBe(false);
    expect(scrapeTokenMatches(undefined)).toBe(false);
  });
});
