/* ============================================================
   RC-PAGE-005 — Canonical metric value formatter.
   Single source of truth for how metric values render across
   scorecards, chart hovers, tooltips, and tables.
   ============================================================ */

// Metric registry — maps metric id → type + precision rules.
// In production this would come from /api/metrics/definitions.
const METRIC_REGISTRY = {
  // ===== Currency =====
  spend:           { type: 'currency', precision: 0, short: true },
  adSales:         { type: 'currency', precision: 0, short: true },
  ops:             { type: 'currency', precision: 0, short: true },   // Ordered Product Sales (canonical)
  cpc:             { type: 'currency', precision: 2, short: false },
  cpa:             { type: 'currency', precision: 2, short: false },
  aov:             { type: 'currency', precision: 2, short: false },
  aovByOrdItem:    { type: 'currency', precision: 2, short: false },
  asp:             { type: 'currency', precision: 2, short: false },  // Avg. Selling Price

  // ===== Percent (0-100 scale, formatter adds %) =====
  acos:            { type: 'percent',  precision: 1 },
  tacos:           { type: 'percent',  precision: 1 },
  ctr:             { type: 'percent',  precision: 2 },
  convR:           { type: 'percent',  precision: 2 },
  adConvR:         { type: 'percent',  precision: 2 },
  adSalesPct:      { type: 'percent',  precision: 1 },
  unitSessPct:     { type: 'percent',  precision: 2 },                // Ordered Item Session %
  buyBoxPct:       { type: 'percent',  precision: 1 },                // Buy Box %

  // ===== Ratio (no unit) =====
  roas:            { type: 'ratio',    precision: 2 },
  unitsPerOrdItem: { type: 'ratio',    precision: 2 },
  avgOfrCount:     { type: 'ratio',    precision: 1 },

  // ===== Counts =====
  orders:          { type: 'count',    precision: 0, short: true },
  adOrders:        { type: 'count',    precision: 0, short: true },
  totOrdItems:     { type: 'count',    precision: 0, short: true },
  unitsOrdered:    { type: 'count',    precision: 0, short: true },
  units:           { type: 'count',    precision: 0, short: true },
  impressions:     { type: 'count',    precision: 0, short: true },
  clicks:          { type: 'count',    precision: 0, short: true },
  pageViews:       { type: 'count',    precision: 0, short: true },
  sessions:        { type: 'count',    precision: 0, short: true },
  asins:           { type: 'count',    precision: 0, short: false },
  campaigns:       { type: 'count',    precision: 0, short: false },
};

// Legacy aliases — any legacy code still referencing these resolves to
// the canonical entry above.  Keeps us robust while the codebase converges.
METRIC_REGISTRY.netSales    = METRIC_REGISTRY.ops;
METRIC_REGISTRY.orderedProd = METRIC_REGISTRY.ops;
METRIC_REGISTRY.adCost      = METRIC_REGISTRY.spend;
METRIC_REGISTRY.cvr         = METRIC_REGISTRY.convR;
METRIC_REGISTRY.bsrShare    = METRIC_REGISTRY.buyBoxPct;

function isPercentMetric(id) {
  return METRIC_REGISTRY[id]?.type === 'percent';
}

// Compact count: 12,345 → "12.3K", 1,234,567 → "1.2M"
function formatCompact(n, precision = 1) {
  const abs = Math.abs(n);
  if (abs >= 1e9)  return (n / 1e9).toFixed(precision) + 'B';
  if (abs >= 1e6)  return (n / 1e6).toFixed(precision) + 'M';
  if (abs >= 1e3)  return (n / 1e3).toFixed(precision) + 'K';
  return n.toFixed(0);
}

function formatNumber(n, precision = 0) {
  return n.toLocaleString('en-US', {
    minimumFractionDigits: precision,
    maximumFractionDigits: precision,
  });
}

/** Format a metric value. */
function formatValue(metricId, value, opts = {}) {
  const spec = METRIC_REGISTRY[metricId];
  const { currency = 'USD', compact } = opts;
  if (value == null || Number.isNaN(value)) return '—';
  if (!spec) return formatNumber(value, 0);

  const useCompact = compact ?? spec.short;

  switch (spec.type) {
    case 'currency': {
      const symbol = currency === 'USD' ? '$' : currency === 'EUR' ? '€' : currency === 'GBP' ? '£' : '$';
      if (useCompact && Math.abs(value) >= 1000) {
        return symbol + formatCompact(value, 1);
      }
      return symbol + formatNumber(value, spec.precision);
    }
    case 'percent':
      return formatNumber(value, spec.precision) + '%';
    case 'ratio':
      return formatNumber(value, spec.precision);
    case 'count':
      if (useCompact && Math.abs(value) >= 1000) return formatCompact(value, 1);
      return formatNumber(value, spec.precision);
  }
  return String(value);
}

/**
 * Format a change value per RC-RPT-007 policy.
 *
 * mode: 'net' | 'pct'
 *   - net: show absolute difference, formatted using the metric's type
 *   - pct: show percentage delta; for percentage-native metrics, render
 *          with "pts" semantics (percentage-points) rather than %%.
 */
function formatChange(metricId, current, prior, mode = 'pct') {
  if (current == null || prior == null) return '—';
  const delta = current - prior;

  if (mode === 'net') {
    const sign = delta > 0 ? '+' : delta < 0 ? '−' : '';
    return sign + formatValue(metricId, Math.abs(delta), { compact: true });
  }

  // pct mode
  if (isPercentMetric(metricId)) {
    // pts semantics — percentage point difference
    const sign = delta > 0 ? '+' : delta < 0 ? '−' : '';
    return sign + Math.abs(delta).toFixed(1) + ' pts';
  }

  if (prior === 0) return delta === 0 ? '0%' : '—';
  const pct = (delta / Math.abs(prior)) * 100;
  const sign = pct > 0 ? '+' : pct < 0 ? '−' : '';
  return sign + Math.abs(pct).toFixed(1) + '%';
}

/** Is a change direction "good" for this metric?
 *  Some metrics invert (ACoS, CPC, CPA — lower is better). */
const INVERTED_METRICS = new Set(['acos', 'tacos', 'cpc', 'cpa', 'adCost', 'avgOfrCount']);

function changeDirection(metricId, delta) {
  if (delta === 0) return 'flat';
  const positive = delta > 0;
  const inverted = INVERTED_METRICS.has(metricId);
  if (inverted) return positive ? 'bad' : 'good';
  return positive ? 'good' : 'bad';
}

Object.assign(window, {
  METRIC_REGISTRY,
  formatValue,
  formatChange,
  changeDirection,
  isPercentMetric,
  INVERTED_METRICS,
});
