// Sparkline + change pill — small primitives shared across the page

// ── Period constants ──────────────────────────────────────────────────────────
// Approximate trading-day counts per display period (used to slice spark arrays).
// 252 td/year, 21 td/month, 5 td/week — standard market conventions.
const PERIOD_TD = { "1W": 5, "1M": 21, "3M": 63, "6M": 126, "1Y": 252, "3Y": Infinity };

// Approximate calendar-day spans, used to compute the effective start date for
// the x-axis start label when the data has no explicit per-point date array.
const PERIOD_CD = { "1W": 7, "1M": 31, "3M": 92, "6M": 183, "1Y": 365, "3Y": 1095 };

// ── Axis label formatters ─────────────────────────────────────────────────────

function _axisStartLabel(isoDate, period) {
  if (!isoDate) return "";
  const d = new Date(isoDate);
  if (period === "3Y") {
    return String(d.getUTCFullYear());              // "2023"
  }
  const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
  const dd = String(d.getUTCDate()).padStart(2, "0");
  return `${mm}/${dd}`;                             // "04/29"
}

function _axisEndLabel(isoDate, period) {
  if (!isoDate) return "";
  const d = new Date(isoDate);
  if (period === "3Y") {
    return String(d.getUTCFullYear());              // "2026"
  }
  const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
  const dd = String(d.getUTCDate()).padStart(2, "0");
  return `${mm}/${dd}`;                             // "04/29"
}

// ── Time-grid markers (vertical lines + axis labels) ──────────────────────────
// Returns [{pct, label}] where pct is 0–100 along the x-axis.
function _timeGridMarkers(isoStart, period) {
  if (!isoStart) return [];
  const startMs = new Date(isoStart + "T00:00:00").getTime();
  const now     = new Date();
  const endMs   = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
  const spanMs  = endMs - startMs;
  if (spanMs <= 0) return [];

  const markers = [];

  if (period === "3Y") {
    const startYear = new Date(isoStart).getUTCFullYear();
    const endYear = now.getUTCFullYear();
    for (let yr = startYear + 1; yr <= endYear; yr++) {
      const ms  = Date.UTC(yr, 0, 1);
      const pct = (ms - startMs) / spanMs * 100;
      if (pct > 4 && pct < 96) {
        markers.push({ pct, label: String(yr) });       // "2025"
      }
    }
  } else {
    const s = new Date(isoStart);
    const walk = new Date(Date.UTC(s.getUTCFullYear(), s.getUTCMonth() + 1, 1));
    while (walk.getTime() < endMs) {
      const pct = (walk.getTime() - startMs) / spanMs * 100;
      if (pct > 10 && pct < 88) {
        const mm = String(walk.getUTCMonth() + 1).padStart(2, "0");
        const dd = String(walk.getUTCDate()).padStart(2, "0");
        markers.push({ pct, label: `${mm}/${dd}` });     // "05/01"
      }
      walk.setUTCMonth(walk.getUTCMonth() + 1);
    }
  }

  // Thin out markers for short periods so axis doesn't crowd
  const maxMarkers = { "1W": 1, "1M": 2, "3M": 3, "6M": 4, "1Y": 4 }[period] ?? 4;
  if (markers.length > maxMarkers) {
    const step = Math.ceil(markers.length / maxMarkers);
    return markers.filter((_, i) => i % step === 0).slice(0, maxMarkers);
  }
  return markers;
}

// ── Nice horizontal levels for SVG grid ───────────────────────────────────────
function _niceHorizontalLevels(min, max, targetCount) {
  targetCount = targetCount || 3;
  const range = max - min;
  if (range <= 0 || targetCount < 1) return [];
  const roughStep = range / (targetCount + 1);
  const mag = Math.pow(10, Math.floor(Math.log10(roughStep)));
  const r = roughStep / mag;
  let niceStep;
  if (r <= 1.5) niceStep = mag;
  else if (r <= 3.5) niceStep = 2 * mag;
  else if (r <= 7.5) niceStep = 5 * mag;
  else niceStep = 10 * mag;

  const niceMin = Math.ceil(min / niceStep) * niceStep;
  const levels = [];
  for (let v = niceMin; v <= max - niceStep * 0.001; v += niceStep) {
    if (v > min && v < max) levels.push(v);
  }
  if (levels.length > targetCount) {
    const step = Math.ceil(levels.length / targetCount);
    return levels.filter((_, i) => i % step === 0).slice(0, targetCount);
  }
  return levels;
}

// ── Sparkline ─────────────────────────────────────────────────────────────────
// Props:
//   data       – full spark array (up to 3 years of daily closes)
//   dir        – "up" | "down" (fallback when period data unchanged)
//   h          – rendered height in px
//   startDate  – ISO date string of data[0] (used when period="3Y")
//   endDate    – ISO date string of latest data point (replaces "today")
//   period     – "1W"|"1M"|"3M"|"6M"|"1Y"|"3Y"  (default "1Y")
//   inverted   – true for spreads (rising value = red), false for equities
//   valueType  – "pct" for equities (% change), "bps" for spreads
function Sparkline({ data, dir, w = 200, h = 56, strokeWidth = 1.5, startDate, endDate, period = "1Y", inverted = false, valueType = "pct" }) {
  // ── Slice data to the selected period ──────────────────────────────────────
  const slicedData = React.useMemo(() => {
    if (!data || data.length === 0) return [];
    const td = PERIOD_TD[period] ?? Infinity;
    return td === Infinity ? data : data.slice(-Math.min(td, data.length));
  }, [data, period]);

  // ── Effective start date for axis labels ───────────────────────────────────
  const effectiveStart = React.useMemo(() => {
    if (period === "3Y" && startDate) return startDate;
    const cd  = PERIOD_CD[period] ?? 1095;
    const ms  = Date.now() - cd * 24 * 60 * 60 * 1000;
    return new Date(ms).toISOString().slice(0, 10);
  }, [period, startDate]);

  // ── Time markers for vertical grid & axis labels ───────────────────────────
  const markers = React.useMemo(
    () => _timeGridMarkers(effectiveStart, period),
    [effectiveStart, period]
  );

  // ── Period direction (from sliced data, not daily dir) ─────────────────────
  const periodDir = React.useMemo(() => {
    if (slicedData.length < 2) return dir || "up";
    const first = slicedData[0];
    const last  = slicedData[slicedData.length - 1];
    const gained = last > first;
    return (gained !== inverted) ? "up" : "down";
  }, [slicedData, inverted, dir]);

  // ── Period change label (shown below axis) ─────────────────────────────────
  const periodLabel = React.useMemo(() => {
    if (slicedData.length < 2) return null;
    const first = slicedData[0];
    const last  = slicedData[slicedData.length - 1];
    if (valueType === "bps") {
      const deltaBps = (last - first) * 100;
      const sign = deltaBps >= 0 ? "+" : "";
      return `${sign}${deltaBps.toFixed(1)} bps`;
    }
    const pct = ((last - first) / first) * 100;
    const sign = pct >= 0 ? "+" : "";
    return `${sign}${pct.toFixed(2)}%`;
  }, [slicedData, valueType]);

  if (slicedData.length < 2) return <div className="spark-wrap" style={{ height: h + 18 }} />;

  // ── SVG path ───────────────────────────────────────────────────────────────
  const max   = Math.max(...slicedData);
  const min   = Math.min(...slicedData);
  const range = max - min || 1;
  const stepX = w / (slicedData.length - 1);

  const pts = slicedData.map((v, i) => {
    const x = i * stepX;
    const y = h - ((v - min) / range) * (h - 4) - 2;
    return [x, y];
  });

  const linePath = pts.map(([x, y], i) =>
    `${i === 0 ? "M" : "L"}${x.toFixed(1)},${y.toFixed(1)}`
  ).join(" ");
  const fillPath = `${linePath} L${w},${h} L0,${h} Z`;
  const color    = periodDir === "up" ? "var(--up)" : "var(--down)";

  const hLevels = React.useMemo(() => _niceHorizontalLevels(min, max, 3), [min, max]);

  return (
    <div className="spark-wrap">
      <svg
        viewBox={`0 0 ${w} ${h}`}
        preserveAspectRatio="none"
        className="spark"
        style={{ width: "100%", height: h, display: "block" }}
      >
        {/* Horizontal grid lines — adaptive price levels */}
        {hLevels.map((lv) => {
          const y = h - ((lv - min) / range) * (h - 4) - 2;
          return (
            <line key={lv}
              x1={0} x2={w}
              y1={y} y2={y}
              stroke="var(--fg-dim)"
              strokeWidth="0.7"
              strokeOpacity="0.7"
              strokeDasharray="1 2"
            />
          );
        })}
        {/* Vertical grid lines — time boundaries */}
        {markers.map((m) => (
          <line
            key={m.label}
            x1={m.pct * w / 100} x2={m.pct * w / 100}
            y1={0}               y2={h}
            stroke="var(--fg-dim)"
            strokeWidth="0.7"
            strokeOpacity="0.7"
            strokeDasharray="1 2"
          />
        ))}
        <path d={fillPath} fill={color} fillOpacity="0.14" />
        <path
          d={linePath}
          fill="none"
          stroke={color}
          strokeWidth={strokeWidth}
          strokeLinecap="round"
          strokeLinejoin="round"
        />
      </svg>

      {/* Axis label row */}
      <div className="spark-axis">
        <span className="spark-tick spark-tick--start">
          {_axisStartLabel(effectiveStart, period)}
        </span>
        {markers.map((m) => (
          <span
            key={m.label}
            className="spark-tick spark-tick--mid"
            style={{ left: `${m.pct}%` }}
          >
            {m.label}
          </span>
        ))}
        <span className="spark-tick spark-tick--end">{endDate ? _axisEndLabel(endDate, period) : "today"}</span>
      </div>

      {/* Period change label */}
      {periodLabel && (
        <div className={`spark-period-label ${periodDir}`}>
          {period}&ensp;{periodLabel}
        </div>
      )}
    </div>
  );
}

// ── ChangeChip ────────────────────────────────────────────────────────────────
function ChangeChip({ pct, dir, text }) {
  const arrow = dir === "up" ? "↗" : "↘";
  if (text !== undefined) {
    return <span className={`chip ${dir}`}>{arrow} {text}</span>;
  }
  const sign = pct >= 0 ? "+" : "";
  return (
    <span className={`chip ${dir}`}>
      {arrow} {sign}{pct.toFixed(2)}%
    </span>
  );
}

// ── PriceBlock ────────────────────────────────────────────────────────────────
function PriceBlock({ price, abs, unit, dir }) {
  const sign = abs >= 0 ? "+" : "";
  return (
    <>
      <div className="card-price">
        {price}{unit ? <span className="card-unit"> {unit}</span> : null}
      </div>
      <div className={`card-abs ${dir}`}>
        {sign}{typeof abs === "number" ? abs.toFixed(2) : abs} today
      </div>
    </>
  );
}

window.Sparkline  = Sparkline;
window.ChangeChip = ChangeChip;
window.PriceBlock = PriceBlock;
