/* global React */
// «Сигнал» chart engine — faithful to the repo visual language + an added
// interactive hover layer (crosshair + tooltip) on the main signal chart.
const { useState, useRef } = React;

// ── helpers ──────────────────────────────────────────────
function ruNum(v) {
  if (!Number.isFinite(v)) return v;
  const s = Number.isInteger(v) ? String(v) : v.toFixed(1);
  return s.replace(".", ",");
}
function niceStep(x) {
  if (x <= 0) return 1;
  const exp = Math.pow(10, Math.floor(Math.log10(x)));
  const f = x / exp;
  const nf = f < 1.5 ? 1 : f < 3 ? 2 : f < 7 ? 5 : 10;
  return nf * exp;
}
function axisTicks(max, count = 3) {
  const step = niceStep(max / count);
  const top = Math.ceil(max / step) * step;
  const ticks = [];
  for (let v = 0; v <= top + 1e-9; v += step) ticks.push(Math.round(v * 100) / 100);
  return { top, ticks };
}
// Catmull-Rom → cubic bezier smooth path
function smoothPath(pts) {
  if (pts.length < 2) return pts.length ? `M${pts[0][0]},${pts[0][1]}` : "";
  let d = `M${pts[0][0].toFixed(1)},${pts[0][1].toFixed(1)}`;
  for (let i = 0; i < pts.length - 1; i++) {
    const p0 = pts[i - 1] || pts[i], p1 = pts[i], p2 = pts[i + 1], p3 = pts[i + 2] || p2;
    const c1x = p1[0] + (p2[0] - p0[0]) / 6, c1y = p1[1] + (p2[1] - p0[1]) / 6;
    const c2x = p2[0] - (p3[0] - p1[0]) / 6, c2y = p2[1] - (p3[1] - p1[1]) / 6;
    d += ` C${c1x.toFixed(1)},${c1y.toFixed(1)} ${c2x.toFixed(1)},${c2y.toFixed(1)} ${p2[0].toFixed(1)},${p2[1].toFixed(1)}`;
  }
  return d;
}

/**
 * SignalLineChart — главный интерактивный график.
 * series: текущие значения; bars: фоновые столбцы; target: план/норма;
 * splitIndex: с какого индекса начинается прогноз (amber dashed + конус);
 * forecastLo/Hi: границы конуса; eventIndex: точка-сигнал (пульс);
 * labels: подписи оси X; dates: для тултипа; hints: подсказка «что значит» на точку;
 * unit; betterDown: считать ли снижение хорошим.
 */
function SignalLineChart({
  series, bars = [], target, splitIndex, forecastLo = [], forecastHi = [],
  eventIndex, eventLabel = "AI сигнал", labels = [], dates = [], hints = [],
  unit = "%", betterDown = false, height = 270
}) {
  const W = 600, H = height, padL = 38, padR = 18, padT = 30, padB = 36;
  const x0 = padL, x1 = W - padR, y0 = padT, y1 = H - padB;
  const shellRef = useRef(null);
  const [hi, setHi] = useState(null);

  const all = [...series, ...bars, ...forecastLo, ...forecastHi, target].filter(Number.isFinite);
  const rawMax = Math.max(...all), rawMin = Math.min(...all);
  const max = rawMax + Math.max(6, (rawMax - rawMin) * 0.2);
  const min = Math.min(0, rawMin) === rawMin && rawMin >= 0 ? Math.max(0, rawMin - (rawMax - rawMin) * 0.12) : rawMin - Math.max(4, (rawMax - rawMin) * 0.12);
  const span = max - min || 1;
  const n = series.length;
  const xOf = (i) => x0 + (i / (n - 1)) * (x1 - x0);
  const yOf = (v) => y1 - ((v - min) / span) * (y1 - y0);
  const pts = series.map((v, i) => [xOf(i), yOf(v)]);
  const actual = Number.isFinite(splitIndex) ? pts.slice(0, splitIndex + 1) : pts;
  const fore = Number.isFinite(splitIndex) ? pts.slice(splitIndex) : [];

  const cone = (Number.isFinite(splitIndex) && forecastLo.length && forecastHi.length)
    ? forecastLo.map((lo, i) => {
        const src = pts[splitIndex + i]; if (!src) return null;
        return { x: src[0], loY: yOf(lo), hiY: yOf(forecastHi[i]) };
      }).filter(Boolean) : [];

  const targetY = Number.isFinite(target) ? yOf(target) : null;
  const ev = Number.isFinite(eventIndex) ? pts[Math.max(0, Math.min(n - 1, eventIndex))] : null;
  const stepT = niceStep((max - min) / 3);
  const visibleTicks = [];
  for (let t = Math.ceil(min / stepT) * stepT; t <= max; t += stepT) visibleTicks.push(Math.round(t * 100) / 100);
  const areaD = `${smoothPath(actual)} L${actual[actual.length - 1][0].toFixed(1)},${y1} L${actual[0][0].toFixed(1)},${y1} Z`;

  function onMove(e) {
    const r = shellRef.current.getBoundingClientRect();
    const rx = ((e.clientX - r.left) / r.width) * W;
    let idx = Math.round(((rx - x0) / (x1 - x0)) * (n - 1));
    idx = Math.max(0, Math.min(n - 1, idx));
    setHi(idx);
  }

  const tip = hi != null ? (() => {
    const v = series[hi];
    const delta = Number.isFinite(target) ? v - target : null;
    let cls = "warn", txt = "";
    if (delta != null) {
      const good = betterDown ? delta <= 0 : delta >= 0;
      cls = good ? "up" : "down";
      txt = `${delta > 0 ? "+" : ""}${ruNum(Math.round(delta * 10) / 10)}${unit} к плану`;
    }
    return { idx: hi, v, cls, txt, px: pts[hi][0], py: pts[hi][1] };
  })() : null;

  return (
    <div className="chart-shell" ref={shellRef} onMouseMove={onMove} onMouseLeave={() => setHi(null)}>
      <svg className="viz-chart signal-chart" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="xMidYMid meet">
        <defs>
          <linearGradient id="slc-area" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0" stopColor="var(--cyan)" stopOpacity=".26" />
            <stop offset=".7" stopColor="var(--cyan)" stopOpacity=".05" />
            <stop offset="1" stopColor="var(--cyan)" stopOpacity="0" />
          </linearGradient>
        </defs>

        <g className="chart-grid">
          {visibleTicks.map((t) => (
            <g key={t}>
              <line x1={x0} y1={yOf(t)} x2={x1} y2={yOf(t)} strokeDasharray="2 5" />
              <text x={x0 - 7} y={yOf(t) + 3.5} textAnchor="end">{ruNum(t)}{unit}</text>
            </g>
          ))}
        </g>

        {bars.map((b, i) => {
          const bw = ((x1 - x0) / n) * 0.46, by = yOf(b);
          return <rect key={i} x={xOf(i) - bw / 2} y={by} width={bw} height={y1 - by} rx="3" fill="var(--text-3)" opacity=".16" />;
        })}

        {cone.length > 1 ? (
          <path d={[`M${cone[0].x},${cone[0].hiY}`, ...cone.slice(1).map((p) => `L${p.x},${p.hiY}`), ...cone.slice().reverse().map((p) => `L${p.x},${p.loY}`), "Z"].join(" ")} fill="var(--amber)" opacity=".14" />
        ) : null}

        {targetY !== null ? (
          <g className="chart-target">
            <line x1={x0} y1={targetY} x2={x1} y2={targetY} />
            <text x={x0 + 8} y={targetY - 8}>план {ruNum(target)}{unit}</text>
          </g>
        ) : null}

        <path d={areaD} fill="url(#slc-area)" />
        <path d={smoothPath(actual)} fill="none" stroke="var(--cyan)" strokeWidth="2.8" strokeLinejoin="round" strokeLinecap="round" />
        {fore.length > 1 ? <path d={smoothPath(fore)} fill="none" stroke="var(--amber)" strokeWidth="2.6" strokeDasharray="8 7" strokeLinejoin="round" strokeLinecap="round" /> : null}

        {ev ? (
          <g>
            <line x1={ev[0]} y1={ev[1]} x2={ev[0]} y2={y1} stroke="var(--amber)" strokeOpacity=".45" strokeWidth="1.5" strokeDasharray="3 3" />
            <circle className="signal-pulse-ring" cx={ev[0]} cy={ev[1]} r="6" fill="var(--amber)" />
            <circle className="signal-pulse-ring is-delayed" cx={ev[0]} cy={ev[1]} r="6" fill="var(--amber)" />
            <circle cx={ev[0]} cy={ev[1]} r="4.5" fill="var(--amber)" stroke="var(--bg)" strokeWidth="1.5" />
            <g className="viz-pill">
              <rect x={ev[0] > x1 - 110 ? ev[0] - 96 : ev[0] + 9} y={ev[1] - 27} width="88" height="19" rx="5" />
              <text className="viz-pill-flag" x={ev[0] > x1 - 110 ? ev[0] - 52 : ev[0] + 53} y={ev[1] - 14} textAnchor="middle">⚡ {eventLabel}</text>
            </g>
          </g>
        ) : null}

        {/* x labels */}
        {labels.map((lab, i) => {
          const idx = Math.round((i / Math.max(labels.length - 1, 1)) * (n - 1));
          return <text key={i} className="x-label" x={xOf(idx)} y={H - 8}>{lab}</text>;
        })}

        {/* hover crosshair + dot */}
        {tip ? (
          <g>
            <line className="ix-crosshair" x1={tip.px} y1={y0} x2={tip.px} y2={y1} />
            <circle className="ix-dot-halo" cx={tip.px} cy={tip.py} r="8" />
            <circle className="ix-dot" cx={tip.px} cy={tip.py} r="4.5" />
          </g>
        ) : null}
      </svg>

      {tip ? (
        <div className="chart-tip" style={{ left: `${(tip.px / W) * 100}%`, top: `${(tip.py / H) * 100}%`, marginTop: -12 }}>
          <div className="ct-date">{dates[tip.idx] || labels[Math.round((tip.idx / (n - 1)) * Math.max(labels.length - 1, 0))] || `точка ${tip.idx + 1}`}</div>
          <div className="ct-val">{ruNum(tip.v)}{unit}</div>
          {tip.txt ? <div className="ct-row"><b className={tip.cls}>{tip.txt}</b></div> : null}
          {hints[tip.idx] ? <div className="ct-hint">{hints[tip.idx]}</div> : null}
        </div>
      ) : null}
    </div>
  );
}

// ── Donut (revenue structure) ────────────────────────────
function Donut({ segments, total, center, sub, size = 150 }) {
  const r = 34, sw = 13, c = 2 * Math.PI * r;
  let off = 0;
  const sum = total ?? segments.reduce((s, x) => s + x.value, 0);
  return (
    <div className="donut-wrap" style={{ display: "flex", alignItems: "center", gap: 14 }}>
      <div style={{ position: "relative", width: size, height: size }}>
        <svg viewBox="0 0 100 100" style={{ width: size, height: size }}>
          <circle cx="50" cy="50" r={r} fill="none" stroke="var(--surface-3)" strokeWidth={sw} />
          {segments.map((s) => {
            const dash = (s.value / sum) * c;
            const el = <circle key={s.label} cx="50" cy="50" r={r} fill="none" stroke={s.color} strokeWidth={sw} strokeDasharray={`${dash} ${c - dash}`} strokeDashoffset={-off} transform="rotate(-90 50 50)" />;
            off += dash; return el;
          })}
        </svg>
        <div className="donut-center" style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }}>
          <b>{center}</b><span>{sub}</span>
        </div>
      </div>
    </div>
  );
}

// ── MicroSpark (tiny trend) ──────────────────────────────
function MicroSpark({ data, severity = "amber", width = 96, height = 34 }) {
  const min = Math.min(...data), max = Math.max(...data);
  const pts = data.map((v, i) => [(i / (data.length - 1)) * width, height - ((v - min) / (max - min || 1)) * (height - 6) - 3]);
  const col = severity === "red" ? "var(--red)" : severity === "green" ? "var(--green)" : "var(--amber)";
  return (
    <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`}>
      <path d={`${smoothPath(pts)} L${width},${height} L0,${height} Z`} fill={col} opacity=".13" />
      <path d={smoothPath(pts)} fill="none" stroke={col} strokeWidth="2" strokeLinecap="round" />
      <circle cx={pts[pts.length - 1][0]} cy={pts[pts.length - 1][1]} r="2.6" fill={col} />
    </svg>
  );
}

// ── BeforeAfterBars (cases / industries) — interactive ───
function BeforeAfterBars({ series, split, fromLabel, toLabel, unit = "", labels = [], splitLabel, height = 240 }) {
  const W = 580, H = height, padL = 38, padR = 14, padT = 28, padB = 46;
  const x0 = padL, x1 = W - padR, y0 = padT, y1 = H - padB;
  const shellRef = useRef(null);
  const [hi, setHi] = useState(null);
  const { top, ticks } = axisTicks(Math.max(...series) * 1.08, 3);
  const yOf = (v) => y1 - (v / top) * (y1 - y0);
  const n = series.length, slot = (x1 - x0) / n, barW = slot * 0.58;
  const splitX = x0 + slot * split;
  const af = series.slice(0, split).reduce((a, b) => a + b, 0) / Math.max(split, 1);
  const at = series.slice(split).reduce((a, b) => a + b, 0) / Math.max(n - split, 1);
  function onMove(e) {
    const r = shellRef.current.getBoundingClientRect();
    const rx = ((e.clientX - r.left) / r.width) * W;
    let idx = Math.floor((rx - x0) / slot);
    idx = Math.max(0, Math.min(n - 1, idx));
    setHi(idx);
  }
  const tip = hi != null ? { idx: hi, v: series[hi], after: hi >= split, cx: x0 + slot * hi + slot / 2, cy: yOf(series[hi]) } : null;
  return (
    <div className="chart-shell" ref={shellRef} onMouseMove={onMove} onMouseLeave={() => setHi(null)}>
      <svg className="viz-chart" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="xMidYMid meet">
        {ticks.filter((t) => t <= top).map((t) => (
          <g key={t}>
            <line x1={x0} x2={x1} y1={yOf(t)} y2={yOf(t)} stroke="var(--grid-line)" strokeDasharray={t === 0 ? "0" : "2 5"} />
            <text className="viz-axis" x={x0 - 7} y={yOf(t) + 3.5} textAnchor="end">{ruNum(t)}</text>
          </g>
        ))}
        <rect x={splitX} y={y0} width={x1 - splitX} height={y1 - y0} fill="var(--green)" opacity=".05" />
        {series.map((v, i) => {
          const after = i >= split, bx = x0 + slot * i + (slot - barW) / 2, by = yOf(v);
          const active = hi === i;
          return <rect key={i} x={bx} y={by} width={barW} height={y1 - by} rx="3" fill={after ? "var(--green)" : "var(--text-3)"} opacity={active ? (after ? 1 : .7) : (after ? ".85" : ".4")} />;
        })}
        <line x1={x0 + 3} x2={splitX - 3} y1={yOf(af)} y2={yOf(af)} stroke="var(--text-3)" strokeWidth="2.5" />
        <line x1={splitX + 3} x2={x1 - 3} y1={yOf(at)} y2={yOf(at)} stroke="var(--green)" strokeWidth="2.5" />
        <g className="viz-pill"><rect x={x0 + 6} y={yOf(af) - 26} width="92" height="19" rx="5" /><text className="viz-pill-was" x={x0 + 52} y={yOf(af) - 13} textAnchor="middle">было {fromLabel}</text></g>
        <g className="viz-pill"><rect x={x1 - 98} y={yOf(at) - 26} width="92" height="19" rx="5" /><text className="viz-pill-now" x={x1 - 52} y={yOf(at) - 13} textAnchor="middle">стало {toLabel}</text></g>
        {splitLabel ? <g><line x1={splitX} x2={splitX} y1={y0 - 4} y2={y1} stroke="var(--green)" strokeOpacity=".5" strokeWidth="1.5" strokeDasharray="3 3" /><text className="viz-split-label" x={splitX} y={y0 - 9} textAnchor="middle">↓ {splitLabel}</text></g> : null}
        <line x1={x0} x2={x1} y1={y1} y2={y1} stroke="var(--line)" />
        {labels.map((lab, i) => {
          const tx = labels.length === 1 ? (x0 + x1) / 2 : x0 + (i / (labels.length - 1)) * (x1 - x0);
          return <text key={i} className="viz-xaxis" x={tx} y={y1 + 16} textAnchor={i === 0 ? "start" : i === labels.length - 1 ? "end" : "middle"}>{lab}</text>;
        })}
        <text className="viz-zone-label" x={(x0 + splitX) / 2} y={H - 8} textAnchor="middle">ранний период</text>
        <text className="viz-zone-label is-after" x={(splitX + x1) / 2} y={H - 8} textAnchor="middle">после внедрения</text>
        {tip ? <line className="ix-crosshair" x1={tip.cx} y1={y0} x2={tip.cx} y2={y1} /> : null}
      </svg>
      {tip ? (
        <div className="chart-tip" style={{ left: `${(tip.cx / W) * 100}%`, top: `${(tip.cy / H) * 100}%`, marginTop: -10 }}>
          <div className="ct-date">{tip.after ? "после внедрения" : "ранний период"}</div>
          <div className="ct-val">{ruNum(tip.v)}{unit}</div>
        </div>
      ) : null}
    </div>
  );
}

// ── WaterfallBars — interactive compare/waterfall ────────
function WaterfallBars({ items, unit = "", height = 150 }) {
  const W = 360, H = height, padT = 22, padB = 30;
  const shellRef = useRef(null);
  const [hi, setHi] = useState(null);
  const maxAbs = Math.max(...items.map((it) => Math.abs(it.value))) || 1;
  const n = items.length, slot = W / n, barW = slot * 0.56;
  const yTop = padT, yBot = H - padB;
  const hOf = (v) => (Math.abs(v) / maxAbs) * (yBot - yTop);
  return (
    <div className="chart-shell" ref={shellRef} onMouseLeave={() => setHi(null)}>
      <svg className="viz-chart" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="xMidYMid meet">
        <line x1="0" x2={W} y1={yBot} y2={yBot} stroke="var(--line)" />
        {items.map((it, i) => {
          const bx = slot * i + (slot - barW) / 2, bh = hOf(it.value), by = yBot - bh;
          const active = hi === i;
          return (
            <g key={it.label} onMouseEnter={() => setHi(i)}>
              <rect x={slot * i} y={0} width={slot} height={H} fill="transparent" />
              <rect x={bx} y={by} width={barW} height={bh} rx="4" fill={it.color} opacity={active ? 1 : .9} />
              <text className="viz-pill-now" x={bx + barW / 2} y={by - 7} textAnchor="middle" style={{ fill: it.color, fontWeight: 700, fontSize: 11 }}>{it.value > 0 ? "+" : "−"}{ruNum(Math.abs(it.value))}</text>
              <text className="viz-xaxis" x={bx + barW / 2} y={yBot + 16} textAnchor="middle" style={{ fontSize: 9.5 }}>{it.label}</text>
            </g>
          );
        })}
      </svg>
      {hi != null ? (
        <div className="chart-tip" style={{ left: `${((slot * hi + slot / 2) / W) * 100}%`, top: `${((yBot - hOf(items[hi].value)) / H) * 100}%`, marginTop: -10 }}>
          <div className="ct-date">{items[hi].label}</div>
          <div className="ct-val" style={{ color: items[hi].color }}>{items[hi].value > 0 ? "+" : "−"}{ruNum(Math.abs(items[hi].value))}{unit}</div>
          {items[hi].hint ? <div className="ct-hint">{items[hi].hint}</div> : null}
        </div>
      ) : null}
    </div>
  );
}

Object.assign(window, { SignalLineChart, Donut, MicroSpark, BeforeAfterBars, WaterfallBars, ruNum, smoothPath, axisTicks });
