// Componentes de visualização: Cartograma do Brasil, AreaChart, Sparkline, Funnel etc.
const { useState, useEffect, useRef, useMemo } = React;

// ── Cartograma de estados ─────────────────────────────────────
function BrazilCartogram({ data, metric, selected, onSelect }) {
  const max = Math.max(0, ...data.map((d) => d[metric] || 0));
  const hasData = max > 0;

  const fmtVal = (s) => {
    if (metric === "ruptureRate") return Math.round(s[metric] * 100) + "%";
    if (metric === "patients") {
      const v = s.patients;
      return v >= 1000 ? (v / 1000).toFixed(v >= 10000 ? 0 : 1).replace(".", ",") + "k" : v;
    }
    const v = s.dispensed;
    if (v >= 1_000_000) return (v / 1_000_000).toFixed(1).replace(".", ",") + "M";
    return (v / 1000).toFixed(0) + "k";
  };

  const stepBgs = [0.0, 0.25, 0.5, 0.75, 1.0].map((intensity) => {
    return `oklch(${94 - intensity * 38}% ${0.018 + intensity * 0.07} var(--accent-h, 165))`;
  });

  const cells = [];
  for (let r = 0; r < 10; r++) {
    for (let c = 0; c < 11; c++) {
      const s = data.find((d) => d.col === c && d.row === r);
      if (!s) {
        cells.push(<div key={`${r}-${c}`} className="cartogram__cell cartogram__cell--empty" />);
        continue;
      }
      const v = s[metric] || 0;
      const intensity = hasData ? Math.pow(v / max, 0.55) : 0;
      const bg = hasData
        ? `oklch(${94 - intensity * 38}% ${0.018 + intensity * 0.075} 165)`
        : "oklch(91% 0.005 240)";
      const fg = intensity > 0.45 ? "oklch(99% 0 0)" : "oklch(28% 0.02 240)";
      const isWarn = s.ruptureRate > 0.18;
      const showVal = metric !== "ruptureRate" ? intensity > 0.18 : true;
      cells.push(
        <button
          key={`${r}-${c}`}
          className={`cartogram__cell ${selected === s.uf ? "is-selected" : ""}`}
          style={{ "--cell-bg": bg, "--cell-fg": fg }}
          onClick={() => onSelect(s.uf)}
          onMouseEnter={(e) => showTip(e, s, metric)}
          onMouseLeave={hideTip}
          onMouseMove={moveTip}>
          <span className="cartogram__cell-bg" />
          <span className="cartogram__cell-uf">{s.uf}</span>
          {showVal && <span className="cartogram__cell-val">{fmtVal(s)}</span>}
          {isWarn && <span className="cartogram__cell-warn" />}
        </button>
      );
    }
  }

  const metricLabel =
    metric === "dispensed" ? "Cápsulas dispensadas" :
    metric === "patients" ? "Pacientes ativos" :
    "Taxa de ruptura";

  return (
    <div className="cartogram-wrap">
      <div className="cartogram">{cells}</div>
      <div className="legend">
        <span>{metricLabel}</span>
        <div className="legend__steps">
          <span style={{ color: "var(--ink-4)" }}>menos</span>
          {stepBgs.map((bg, i) =>
            <span key={i} className="legend__step" style={{ background: bg }} />
          )}
          <span style={{ color: "var(--ink-4)" }}>mais</span>
        </div>
        <span className="legend__chip">
          <span className="d" /> ruptura ≥ 18%
        </span>
      </div>
    </div>
  );
}

const tipEl = (() => {
  if (typeof document === "undefined") return null;
  const el = document.createElement("div");
  el.className = "tooltip";
  document.body.appendChild(el);
  return el;
})();
function showTip(e, s, metric) {
  if (!tipEl) return;
  const num = (n) => n.toLocaleString("pt-BR");
  tipEl.innerHTML = `
    <div class="tooltip__title">${s.uf} · ${s.name}</div>
    <div class="tooltip__row"><span>Pacientes ativos</span><span class="v">${num(s.patients)}</span></div>
    <div class="tooltip__row"><span>Cápsulas / mês</span><span class="v">${num(s.dispensed)}</span></div>
    <div class="tooltip__row"><span>Caixas / mês</span><span class="v">${num(s.caixas)}</span></div>
    <div class="tooltip__row"><span>Ruptura</span><span class="v">${(s.ruptureRate * 100).toFixed(0)}% (${s.ruptureEstabs}/${s.estabs})</span></div>`;
  tipEl.classList.add("is-on");
  moveTip(e);
}
function moveTip(e) {
  if (!tipEl) return;
  tipEl.style.left = e.clientX + "px";
  tipEl.style.top = e.clientY + "px";
}
function hideTip() { if (tipEl) tipEl.classList.remove("is-on"); }

// ── Sparkline ─────────────────────────────────────────────────
function Sparkline({ data, width = 110, height = 36, stroke = "var(--accent)", fill = true, dot = true }) {
  if (!data || data.length === 0) return null;
  const max = Math.max(...data);
  const min = Math.min(...data);
  const range = max - min || 1;
  const step = width / (data.length - 1);
  const pts = data.map((v, i) => [i * step, height - (v - min) / range * (height - 4) - 2]);
  const d = pts.map((p, i) => i === 0 ? `M${p[0]},${p[1]}` : `L${p[0]},${p[1]}`).join(" ");
  const dFill = d + ` L${width},${height} L0,${height} Z`;
  return (
    <svg width={width} height={height} style={{ display: "block" }}>
      {fill && <path d={dFill} fill={stroke} opacity="0.1" />}
      <path d={d} fill="none" stroke={stroke} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
      {dot && <circle cx={pts[pts.length - 1][0]} cy={pts[pts.length - 1][1]} r="2.5" fill={stroke} />}
    </svg>
  );
}

// ── Dual-axis chart: dispensação × ruptura ────────────────────
function DispensacaoRupturaChart({ months, height = 240 }) {
  const ref = useRef(null);
  const [w, setW] = useState(800);
  const [hover, setHover] = useState(null);
  useEffect(() => {
    const obs = new ResizeObserver(([e]) => setW(e.contentRect.width));
    if (ref.current) obs.observe(ref.current);
    return () => obs.disconnect();
  }, []);

  const startIdx = months.findIndex((m) => m.caixas >= 200);
  const data = startIdx >= 0 ? months.slice(Math.max(0, startIdx - 1)) : months;

  const pad = { l: 48, r: 56, t: 18, b: 30 };
  const innerW = Math.max(100, w - pad.l - pad.r);
  const innerH = height - pad.t - pad.b;
  const maxC = Math.max(...data.map((m) => m.caixas)) * 1.08;
  const maxR = 0.35;
  const xStep = data.length > 1 ? innerW / (data.length - 1) : innerW;

  const yC = (v) => pad.t + innerH - v / maxC * innerH;
  const yR = (v) => pad.t + innerH - v / maxR * innerH;

  const ptsC = data.map((m, i) => [pad.l + i * xStep, yC(m.caixas)]);
  const ptsR = data.map((m, i) => [pad.l + i * xStep, yR(m.ruptura)]);

  const dC = ptsC.map((p, i) => i === 0 ? `M${p[0]},${p[1]}` : `L${p[0]},${p[1]}`).join(" ");
  const dCArea = dC + ` L${ptsC[ptsC.length - 1][0]},${pad.t + innerH} L${ptsC[0][0]},${pad.t + innerH} Z`;
  const dR = ptsR.map((p, i) => i === 0 ? `M${p[0]},${p[1]}` : `L${p[0]},${p[1]}`).join(" ");

  const yTicks = 4;
  const yArr = Array.from({ length: yTicks + 1 }, (_, i) => maxC / yTicks * i);
  const yRArr = Array.from({ length: yTicks + 1 }, (_, i) => maxR / yTicks * i);

  return (
    <div ref={ref} className="chart-wrap">
      <svg width={w} height={height}
        onMouseLeave={() => setHover(null)}
        onMouseMove={(e) => {
          const rect = e.currentTarget.getBoundingClientRect();
          const x = e.clientX - rect.left - pad.l;
          const i = Math.round(x / xStep);
          if (i >= 0 && i < data.length) setHover(i);
        }}>
        <defs>
          <linearGradient id="capsArea" x1="0" x2="0" y1="0" y2="1">
            <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.18" />
            <stop offset="100%" stopColor="var(--accent)" stopOpacity="0" />
          </linearGradient>
        </defs>
        {yArr.map((t, i) =>
          <g key={i}>
            <line x1={pad.l} x2={w - pad.r} y1={yC(t)} y2={yC(t)} className="chart-grid-line" />
            <text x={pad.l - 8} y={yC(t) + 4} textAnchor="end" className="chart-axis-label">
              {t >= 1000 ? (t / 1000).toFixed(t >= 10000 ? 0 : 1).replace(".", ",") + "k" : Math.round(t)}
            </text>
          </g>
        )}
        {yRArr.map((t, i) =>
          <text key={i} x={w - pad.r + 8} y={yR(t) + 4} textAnchor="start" className="chart-axis-label" style={{ fill: "var(--warn)" }}>
            {Math.round(t * 100)}%
          </text>
        )}
        <path d={dCArea} fill="url(#capsArea)" />
        <path d={dC} fill="none" stroke="var(--accent)" strokeWidth="2" strokeLinejoin="round" strokeLinecap="round" />
        <path d={dR} fill="none" stroke="var(--warn)" strokeWidth="1.8" strokeLinejoin="round" strokeLinecap="round" />
        {ptsC.map(([px, py], i) =>
          <circle key={"c" + i} cx={px} cy={py} r={hover === i ? 3.5 : 0} fill="var(--accent)" />
        )}
        {ptsR.map(([px, py], i) =>
          <circle key={"r" + i} cx={px} cy={py} r={hover === i ? 3.5 : 1.6} fill="var(--warn)" />
        )}
        {data.map((m, i) => i % 2 === 0 || i === data.length - 1 ?
          <text key={m.c} x={pad.l + i * xStep} y={height - 8} textAnchor="middle" className="chart-axis-label">
            {fmtMonth(m.c, true)}
          </text> : null
        )}
        {hover !== null &&
          <line x1={pad.l + hover * xStep} x2={pad.l + hover * xStep} y1={pad.t} y2={pad.t + innerH} stroke="var(--ink)" opacity="0.3" />
        }
        <text x={pad.l - 8} y={pad.t - 6} textAnchor="end" className="chart-axis-label" style={{ fill: "var(--accent-ink)" }}>caixas</text>
        <text x={w - pad.r + 8} y={pad.t - 6} textAnchor="start" className="chart-axis-label" style={{ fill: "var(--warn)" }}>ruptura</text>
      </svg>
      {hover !== null &&
        <div style={{
          position: "absolute",
          left: Math.min(pad.l + hover * xStep + 12, w - 200),
          top: 8,
          background: "var(--ink)", color: "var(--bg)",
          padding: "10px 12px", borderRadius: 8, fontSize: 12,
          pointerEvents: "none", boxShadow: "var(--shadow-3)", minWidth: 180
        }}>
          <div style={{ fontFamily: "var(--mono)", fontSize: 10, letterSpacing: ".08em", textTransform: "uppercase", opacity: .65 }}>
            {fmtMonth(data[hover].c)}
          </div>
          <div style={{ display: "flex", justifyContent: "space-between", marginTop: 6 }}>
            <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
              <span style={{ width: 7, height: 7, borderRadius: 999, background: "var(--accent-2)" }} />Caixas
            </span>
            <span className="num">{data[hover].caixas.toLocaleString("pt-BR")}</span>
          </div>
          <div style={{ display: "flex", justifyContent: "space-between" }}>
            <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
              <span style={{ width: 7, height: 7, borderRadius: 999, background: "oklch(75% 0.12 45)" }} />Ruptura
            </span>
            <span className="num">{(data[hover].ruptura * 100).toFixed(1).replace(".", ",")}% · {data[hover].estabsRuptura} estab.</span>
          </div>
        </div>
      }
    </div>
  );
}

function PatientsChart({ months, height = 280 }) {
  const ref = useRef(null);
  const [w, setW] = useState(800);
  useEffect(() => {
    const obs = new ResizeObserver(([e]) => setW(e.contentRect.width));
    if (ref.current) obs.observe(ref.current);
    return () => obs.disconnect();
  }, []);

  const pad = { l: 44, r: 18, t: 14, b: 28 };
  const innerW = Math.max(100, w - pad.l - pad.r);
  const innerH = height - pad.t - pad.b;
  const max = Math.max(...months.map((m) => m.ativos)) * 1.08;
  const min = 0;
  const xStep = innerW / (months.length - 1);
  const yScale = (v) => pad.t + innerH - (v - min) / (max - min) * innerH;

  const points = months.map((m, i) => [pad.l + i * xStep, yScale(m.ativos)]);
  const dLine = points.map((p, i) => i === 0 ? `M${p[0]},${p[1]}` : `L${p[0]},${p[1]}`).join(" ");
  const dArea = dLine + ` L${pad.l + (months.length - 1) * xStep},${pad.t + innerH} L${pad.l},${pad.t + innerH} Z`;

  const ticks = 4;
  const yTicks = Array.from({ length: ticks + 1 }, (_, i) => max / ticks * i);
  const ann = months.findIndex((m) => m.c === "202507");
  const [hover, setHover] = useState(null);

  return (
    <div ref={ref} className="chart-wrap">
      <svg width={w} height={height}
        onMouseLeave={() => setHover(null)}
        onMouseMove={(e) => {
          const rect = e.currentTarget.getBoundingClientRect();
          const x = e.clientX - rect.left - pad.l;
          const i = Math.round(x / xStep);
          if (i >= 0 && i < months.length) setHover(i);
        }}>
        {yTicks.map((t, i) =>
          <g key={i}>
            <line x1={pad.l} x2={w - pad.r} y1={yScale(t)} y2={yScale(t)} className="chart-grid-line" />
            <text x={pad.l - 8} y={yScale(t) + 4} textAnchor="end" className="chart-axis-label">
              {t >= 1000 ? (t / 1000).toFixed(t >= 10000 ? 0 : 1) + "k" : Math.round(t)}
            </text>
          </g>
        )}
        {ann >= 0 &&
          <g>
            <line x1={pad.l + ann * xStep} x2={pad.l + ann * xStep} y1={pad.t} y2={pad.t + innerH} stroke="var(--ink-4)" strokeDasharray="2 3" />
            <text x={pad.l + ann * xStep + 6} y={pad.t + 12} className="chart-axis-label" style={{ fill: "var(--ink-3)" }}>jul/25 — entrada SUS</text>
          </g>
        }
        <path d={dArea} fill="var(--accent)" opacity="0.10" />
        <path d={dLine} fill="none" stroke="var(--accent)" strokeWidth="2" strokeLinejoin="round" strokeLinecap="round" />
        {points.map(([px, py], i) =>
          <circle key={i} cx={px} cy={py} r={hover === i ? 4 : 2} fill={hover === i ? "var(--ink)" : "var(--accent)"} />
        )}
        {months.map((m, i) => i % 3 === 0 || i === months.length - 1 ?
          <text key={m.c} x={pad.l + i * xStep} y={height - 8} textAnchor="middle" className="chart-axis-label">
            {fmtMonth(m.c, true)}
          </text> : null
        )}
        {hover !== null &&
          <line x1={pad.l + hover * xStep} x2={pad.l + hover * xStep} y1={pad.t} y2={pad.t + innerH} stroke="var(--ink)" opacity="0.3" />
        }
      </svg>
      {hover !== null &&
        <div style={{
          position: "absolute",
          left: Math.min(pad.l + hover * xStep + 12, w - 200),
          top: 12,
          background: "var(--ink)", color: "var(--bg)",
          padding: "10px 12px", borderRadius: 8, fontSize: 12,
          pointerEvents: "none", boxShadow: "var(--shadow-3)", minWidth: 180
        }}>
          <div style={{ fontFamily: "var(--mono)", fontSize: 10, letterSpacing: ".08em", textTransform: "uppercase", opacity: .65 }}>
            {fmtMonth(months[hover].c)}
          </div>
          <div style={{ display: "flex", justifyContent: "space-between", marginTop: 6 }}>
            <span>Ativos</span><span className="num">{months[hover].ativos.toLocaleString("pt-BR")}</span>
          </div>
          <div style={{ display: "flex", justifyContent: "space-between", color: "var(--accent-2)" }}>
            <span>Novos</span><span className="num">+{months[hover].novos.toLocaleString("pt-BR")}</span>
          </div>
          <div style={{ display: "flex", justifyContent: "space-between", color: "oklch(75% 0.1 30)" }}>
            <span>Desistência</span><span className="num">−{months[hover].dropout}</span>
          </div>
        </div>
      }
    </div>
  );
}

function fmtMonth(c, short = false) {
  const y = c.slice(0, 4), m = parseInt(c.slice(4), 10);
  const names = ["jan", "fev", "mar", "abr", "mai", "jun", "jul", "ago", "set", "out", "nov", "dez"];
  return short ? `${names[m - 1]}/${y.slice(2)}` : `${names[m - 1]} / ${y}`;
}

function Funnel({ rows }) {
  const max = Math.max(...rows.map((r) => r.value));
  return (
    <div className="funnel">
      {rows.map((r, i) =>
        <div key={i} className="funnel__row">
          <div className="funnel__label">
            <span>{r.label}</span>
            <span className="funnel__num">{r.value.toLocaleString("pt-BR")}{r.suffix || ""}</span>
          </div>
          <div className="funnel__bar">
            <div className={`funnel__bar-fill ${r.tone || ""}`} style={{ width: `${r.value / max * 100}%` }} />
          </div>
        </div>
      )}
    </div>
  );
}

function Cohort({ data }) {
  return (
    <div>
      <div className="cohort">
        {data.map((c, i) =>
          <div key={c.m} className="cohort__col">
            <div className="cohort__bar" style={{ height: `${c.pct * 1.4}px`, opacity: 0.5 + c.pct / 200, margin: "5px 0px 0px" }}>
              <span>{Math.round(c.pct)}</span>
            </div>
            <span className="cohort__label">{c.m}</span>
          </div>
        )}
      </div>
    </div>
  );
}

function ReasonsList({ rows }) {
  return (
    <div className="reasons">
      {rows.map((r, i) =>
        <div key={i} className="reason">
          <div>
            <div className="reason__name">{r.label}</div>
            <div className="reason__bar">
              <div className="reason__fill" style={{ width: `${r.value * 100}%` }} />
            </div>
          </div>
          <div className="reason__pct">{Math.round(r.value * 100)}%</div>
        </div>
      )}
    </div>
  );
}

function IndicatorBar({ indicator }) {
  const v = Math.max(0, Math.min(2, indicator));
  const pct = v / 2 * 100;
  const mid = 50;
  let tone = "ok";
  if (indicator < 0.6) tone = "danger";
  else if (indicator < 0.85) tone = "warn";
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
      <div className="hosp-row__bar">
        <div className="hosp-row__bar-fill"
          style={{
            width: `${pct}%`,
            background: tone === "danger" ? "var(--danger)" : tone === "warn" ? "var(--warn)" : "var(--accent)"
          }} />
        <div className="hosp-row__bar-mid" style={{ left: `${mid}%` }} />
      </div>
      <span className="num" style={{ fontSize: 11, color: "var(--ink-3)" }}>
        {indicator === 0 ? "0,00× ruptura" : `${indicator.toFixed(2).replace(".", ",")}× média`}
      </span>
    </div>
  );
}

Object.assign(window, {
  BrazilCartogram, Sparkline, PatientsChart, DispensacaoRupturaChart,
  Funnel, Cohort, ReasonsList, IndicatorBar, fmtMonth
});
