'use strict';

/**
 * Plugin diagnostics surface (Item F).
 *
 * In-memory ring buffer of plugin load-time + runtime diagnostics.
 * All 4 registries (skill-loader, provider, channel, adapter) feed
 * diagnostics here; the API endpoint (api-walle.js GET
 * /api/wall-e/plugins/diagnostics) reads them.
 *
 * Inspired by OpenClaw's `PluginDiagnostic { level, message, pluginId, source }`
 * pattern (manifest-types.ts), with a Wall-E-specific `component` field
 * (skill-loader, provider-registry, channel-registry, adapter-registry,
 * activation-planner) to scope filtering.
 *
 * Cross-cutting recovery-logger (from error-recovery deep-dive) handles
 * runtime errors. This is for *plugin* lifecycle: load failures, validation
 * misses, version skew. Different audience (admin debugging plugin layout)
 * vs runtime ops.
 */

const MAX_DIAGNOSTICS = 256;

const _diagnostics = []; // ring buffer; push to end, drop from front

/**
 * Record a single diagnostic. Idempotent on (component, message, pluginId).
 *
 * @param {object} diag - { level, message, pluginId?, source?, fieldPath?, component? }
 * @param {object} [opts]
 * @param {string} [opts.component] - Override component (default from diag)
 * @returns {object} the stored diagnostic with timestamp added
 */
function addDiagnostic(diag, opts = {}) {
  if (!diag || typeof diag !== 'object') return null;

  const entry = {
    level: diag.level === 'error' ? 'error' : 'warn',
    message: String(diag.message || ''),
    pluginId: diag.pluginId,
    source: diag.source,
    fieldPath: diag.fieldPath,
    component: opts.component || diag.component || 'plugin',
    timestamp: new Date().toISOString(),
  };

  // De-dupe: if the same (component, level, message, pluginId) exists in the
  // last 32 entries, skip — prevents log spam on repeated reload of the same
  // bad manifest.
  const recent = _diagnostics.slice(-32);
  for (const d of recent) {
    if (
      d.component === entry.component &&
      d.level === entry.level &&
      d.message === entry.message &&
      d.pluginId === entry.pluginId
    ) {
      return d; // already known
    }
  }

  _diagnostics.push(entry);
  while (_diagnostics.length > MAX_DIAGNOSTICS) _diagnostics.shift();

  // Mirror to recovery-logger so plugin issues show up in the unified log.
  // Cheap call (atomic appendFileSync, ~10us). Best-effort only.
  try {
    const { logEvent } = require('../lib/recovery-logger');
    logEvent({
      level: entry.level,
      component: entry.component,
      event: 'plugin_diagnostic',
      message: entry.message,
      details: {
        pluginId: entry.pluginId,
        source: entry.source,
        fieldPath: entry.fieldPath,
      },
    });
  } catch {}

  return entry;
}

/** Bulk-record diagnostics. */
function addDiagnostics(diags, opts = {}) {
  if (!Array.isArray(diags)) return [];
  const out = [];
  for (const d of diags) {
    const stored = addDiagnostic(d, opts);
    if (stored) out.push(stored);
  }
  return out;
}

/**
 * Read diagnostics, optionally filtered by component / level / pluginId.
 * Most-recent last (push order).
 */
function listDiagnostics(filter = {}) {
  let out = _diagnostics.slice();
  if (filter.component) out = out.filter((d) => d.component === filter.component);
  if (filter.level) out = out.filter((d) => d.level === filter.level);
  if (filter.pluginId) out = out.filter((d) => d.pluginId === filter.pluginId);
  if (filter.limit) out = out.slice(-filter.limit);
  return out;
}

/** Clear (used in tests). */
function clearDiagnostics() {
  _diagnostics.length = 0;
}

/** Stats — count by level, count by component. */
function getStats() {
  const total = _diagnostics.length;
  const byLevel = { warn: 0, error: 0 };
  const byComponent = {};
  for (const d of _diagnostics) {
    byLevel[d.level] = (byLevel[d.level] || 0) + 1;
    byComponent[d.component] = (byComponent[d.component] || 0) + 1;
  }
  return {
    total,
    capacity: MAX_DIAGNOSTICS,
    byLevel,
    byComponent,
  };
}

module.exports = {
  MAX_DIAGNOSTICS,
  addDiagnostic,
  addDiagnostics,
  listDiagnostics,
  clearDiagnostics,
  getStats,
};
