'use strict';
/*
 * Лёгкие in-memory метрики HTTP-запросов для System Health (Level 2).
 * Сбрасываются при перезапуске процесса. Память ограничена кольцевыми буферами.
 * Группировка по шаблону маршрута (req.route.path), а не по конкретному URL —
 * чтобы /api/biochem/molecules/:id не плодил тысячи ключей.
 */
const MAX_LAT = 3000;       // окно последних латентностей для перцентилей
const WINDOW_MS = 60_000;   // окно для req/min

let total = 0;
let totalServerErrors = 0;
const statusClasses = { '2xx': 0, '3xx': 0, '4xx': 0, '5xx': 0 };
const routeStats = new Map();   // "METHOD /path" -> { count, totalMs, maxMs, errors }
const latencies = [];           // последние латентности (мс)
const recentTs = [];            // метки времени последних запросов (для req/min)
const startedAt = Date.now();

function record(method, route, status, ms) {
  total++;
  const cls = Math.floor(status / 100) + 'xx';
  if (statusClasses[cls] !== undefined) statusClasses[cls]++;
  if (status >= 500) totalServerErrors++;

  const key = method + ' ' + route;
  let r = routeStats.get(key);
  if (!r) { r = { count: 0, totalMs: 0, maxMs: 0, errors: 0 }; routeStats.set(key, r); }
  r.count++; r.totalMs += ms; if (ms > r.maxMs) r.maxMs = ms;
  if (status >= 400) r.errors++;

  latencies.push(ms);
  if (latencies.length > MAX_LAT) latencies.shift();

  const now = Date.now();
  recentTs.push(now);
  const cutoff = now - WINDOW_MS;
  while (recentTs.length && recentTs[0] < cutoff) recentTs.shift();
}

function _pct(sorted, p) {
  if (!sorted.length) return 0;
  return sorted[Math.min(sorted.length - 1, Math.floor((p / 100) * sorted.length))];
}

/* ── Сэмплинг для трендов (Level 3): раз в минуту в кольцевой буфер ─────── */
const HISTORY_CAP = 1440;        // ~24 часа при 1 сэмпле/мин
const samples = [];
let _lastErr5xx = 0, _lastTotal = 0;

function sample() {
  const now = Date.now();
  const cutoff = now - WINDOW_MS;
  let rpm = 0;
  for (let i = recentTs.length - 1; i >= 0 && recentTs[i] >= cutoff; i--) rpm++;
  const sorted = [...latencies].sort((a, b) => a - b);
  const cur5xx = statusClasses['5xx'];
  const errDelta = Math.max(0, cur5xx - _lastErr5xx); _lastErr5xx = cur5xx;
  const reqDelta = Math.max(0, total - _lastTotal); _lastTotal = total;
  const mem = process.memoryUsage();
  samples.push({
    ts: now,
    rss: mem.rss,
    heapUsed: mem.heapUsed,
    reqPerMin: rpm,
    reqDelta,
    err5xx: errDelta,
    p95: _pct(sorted, 95),
  });
  if (samples.length > HISTORY_CAP) samples.shift();
}

const _sampler = setInterval(sample, 60_000);
if (_sampler.unref) _sampler.unref();   // не держим процесс живым
sample();                                // стартовая точка сразу

function history(limit = 180) { return samples.slice(-limit); }

function snapshot() {
  const now = Date.now();
  const cutoff = now - WINDOW_MS;
  let reqPerMin = 0;
  for (let i = recentTs.length - 1; i >= 0 && recentTs[i] >= cutoff; i--) reqPerMin++;
  const sorted = [...latencies].sort((a, b) => a - b);
  const avg = latencies.length ? latencies.reduce((s, x) => s + x, 0) / latencies.length : 0;
  const routes = [...routeStats.entries()].map(([k, r]) => ({
    route: k, count: r.count, avgMs: r.totalMs / r.count, maxMs: r.maxMs, errors: r.errors,
  }));
  return {
    total, totalServerErrors, statusClasses,
    reqPerMin,
    avgMs: avg, p50: _pct(sorted, 50), p95: _pct(sorted, 95), p99: _pct(sorted, 99),
    routeCount: routeStats.size,
    sinceMs: now - startedAt,
    topBusy:   [...routes].sort((a, b) => b.count - a.count).slice(0, 8),
    topSlow:   [...routes].sort((a, b) => b.avgMs - a.avgMs).slice(0, 8),
    topErrors: routes.filter(r => r.errors > 0).sort((a, b) => b.errors - a.errors).slice(0, 8),
    history: history(180),
  };
}

module.exports = { record, snapshot, sample, history };
