// Pure SVG chart primitives - no library
const fmtBRL = (v) => "R$ " + (Number(v) || 0).toLocaleString("pt-BR", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const fmtBRLShort = (v) => {
  const abs = Math.abs(v);
  if (abs >= 1000) return "R$ " + (v / 1000).toFixed(1).replace(".", ",") + "k";
  return "R$ " + v.toFixed(0);
};
const monthLabel = (idx) => ["jan", "fev", "mar", "abr", "mai", "jun", "jul", "ago", "set", "out", "nov", "dez"][idx];

// Hook: detecta quando um elemento entra na viewport (uma vez só, depois desconecta)
// Permite animar gráficos "se desenhando" no scroll, estilo AfterEffects.
function useInView(threshold = 0.15) {
  const ref = React.useRef(null);
  const [inView, setInView] = React.useState(false);
  React.useEffect(() => {
    if (!ref.current) return;
    const obs = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) { setInView(true); obs.disconnect(); }
    }, { threshold });
    obs.observe(ref.current);
    return () => obs.disconnect();
  }, []);
  return [ref, inView];
}
// Easing comum
const EASE = "cubic-bezier(.2,.8,.2,1)";
// Durações das animações de gráfico foram reduzidas pra ~40% do original
// pra deixar a UI mais "snappy" sem perder a estética de desenho-no-scroll.

// Donut chart with center label
function Donut({ data, size = 180, thickness = 28, accent = "#16a34a", showLegend = true, palette }) {
  const total = data.reduce((s, d) => s + d.value, 0) || 1;
  const r = size / 2 - thickness / 2;
  const cx = size / 2, cy = size / 2;
  const C = 2 * Math.PI * r;
  let offset = 0;
  const colors = palette || [accent, "var(--info)", "#f59e0b", "#ef4444", "#a855f7", "#14b8a6", "#84cc16"];
  const [ref, inView] = useInView();
  const factor = inView ? 1 : 0;

  const svg = (
    <svg width={size} height={size} style={{ transform: "rotate(-90deg)" }}>
      <circle cx={cx} cy={cy} r={r} fill="none" stroke="var(--border)" strokeWidth={thickness} />
      {data.map((d, i) => {
        const fullLen = (d.value / total) * C;
        const len = fullLen * factor;
        const seg = (
          <circle
            key={i}
            cx={cx} cy={cy} r={r}
            fill="none"
            stroke={colors[i % colors.length]}
            strokeWidth={thickness}
            strokeDasharray={`${len} ${C - len}`}
            strokeDashoffset={-offset * factor}
            style={{ transition: `stroke-dasharray 0.45s ${EASE} ${i * 80}ms, stroke-dashoffset 0.45s ${EASE} ${i * 80}ms` }}
          />
        );
        offset += fullLen;
        return seg;
      })}
    </svg>
  );

  if (!showLegend) return <span ref={ref} style={{ display: "inline-block" }}>{svg}</span>;

  return (
    <div ref={ref} style={{ display: "flex", alignItems: "center", gap: 20 }}>
      {svg}
      <div style={{ flex: 1, display: "flex", flexDirection: "column", gap: 8 }}>
        {data.map((d, i) => (
          <div key={i} style={{ display: "flex", alignItems: "center", gap: 10, fontSize: 13 }}>
            <span style={{ width: 10, height: 10, borderRadius: 3, background: colors[i % colors.length], flexShrink: 0 }}></span>
            <span style={{ flex: 1, color: "var(--fg-2)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{d.label}</span>
            <span style={{ color: "var(--fg-1)", fontVariantNumeric: "tabular-nums", fontWeight: 500 }}>
              {((d.value / total) * 100).toFixed(0)}%
            </span>
          </div>
        ))}
      </div>
    </div>
  );
}

// Bars: receita vs despesa por mês
function BarsCompare({ months, accent = "#16a34a" }) {
  const w = 560, h = 220, padX = 40, padY = 24;
  const innerW = w - padX * 2, innerH = h - padY * 2;
  const max = Math.max(1, ...months.flatMap(m => [m.in, m.out]));
  const groupW = innerW / months.length;
  const barW = Math.min(18, groupW * 0.32);
  const ySteps = 4;
  const [ref, inView] = useInView();
  const f = inView ? 1 : 0;
  return (
    <div ref={ref}>
    <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="xMidYMid meet" style={{ width: "100%", height: "auto", maxHeight: 260, display: "block" }}>
      {/* gridlines */}
      {Array.from({ length: ySteps + 1 }).map((_, i) => {
        const y = padY + (innerH * i) / ySteps;
        const val = max - (max * i) / ySteps;
        return (
          <g key={i}>
            <line x1={padX} y1={y} x2={w - padX} y2={y} stroke="var(--border)" strokeDasharray="2 4" />
            <text x={padX - 6} y={y + 3} textAnchor="end" fontSize="10" fill="var(--fg-3)" fontFamily="ui-monospace, monospace">
              {fmtBRLShort(val)}
            </text>
          </g>
        );
      })}
      {months.map((m, i) => {
        const cx = padX + groupW * i + groupW / 2;
        const inH = (m.in / max) * innerH * f;
        const outH = (m.out / max) * innerH * f;
        const delay = `${i * 50}ms`;
        const trs = `y 0.35s ${EASE} ${delay}, height 0.35s ${EASE} ${delay}`;
        return (
          <g key={i}>
            <rect x={cx - barW - 2} y={padY + innerH - inH} width={barW} height={inH} fill={accent} rx="3" opacity="0.95" style={{ transition: trs }}>
              <title>{`${m.label}: receita ${fmtBRL(m.in)}`}</title>
            </rect>
            <rect x={cx + 2} y={padY + innerH - outH} width={barW} height={outH} fill="var(--fg-1)" rx="3" opacity="0.85" style={{ transition: trs }}>
              <title>{`${m.label}: despesa ${fmtBRL(m.out)}`}</title>
            </rect>
            <text x={cx} y={h - 6} textAnchor="middle" fontSize="11" fill="var(--fg-2)">{m.label}</text>
          </g>
        );
      })}
    </svg>
    </div>
  );
}

// Line / area chart for cumulative balance
function AreaTrend({ points, accent = "#16a34a", height = 140 }) {
  const w = 560, h = height, padX = 8, padY = 16;
  const innerW = w - padX * 2, innerH = h - padY * 2;
  const [ref, inView] = useInView();
  if (!points.length) return <svg viewBox={`0 0 ${w} ${h}`} />;
  const max = Math.max(...points.map(p => p.v));
  const min = Math.min(...points.map(p => p.v), 0);
  const range = max - min || 1;
  const x = (i) => padX + (innerW * i) / Math.max(1, points.length - 1);
  const y = (v) => padY + innerH - ((v - min) / range) * innerH;
  const path = points.map((p, i) => `${i === 0 ? "M" : "L"} ${x(i)} ${y(p.v)}`).join(" ");
  const area = path + ` L ${x(points.length - 1)} ${padY + innerH} L ${x(0)} ${padY + innerH} Z`;
  const id = "g-" + Math.random().toString(36).slice(2, 7);
  return (
    <div ref={ref}>
    <svg viewBox={`0 0 ${w} ${h}`} style={{ width: "100%", height: "auto", display: "block" }}>
      <defs>
        <linearGradient id={id} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={accent} stopOpacity="0.25" />
          <stop offset="100%" stopColor={accent} stopOpacity="0" />
        </linearGradient>
      </defs>
      <path d={area} fill={`url(#${id})`} opacity={inView ? 1 : 0} style={{ transition: `opacity 0.4s ${EASE} .4s` }} />
      <path d={path} stroke={accent} strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"
        pathLength="1" strokeDasharray="1" strokeDashoffset={inView ? 0 : 1}
        style={{ transition: `stroke-dashoffset 0.5s ${EASE}` }} />
      {points.map((p, i) => (
        <circle key={i} cx={x(i)} cy={y(p.v)} r={i === points.length - 1 ? 4 : 2.5} fill={accent}
          opacity={inView ? 1 : 0}
          style={{ transition: `opacity .35s ease ${0.2 + i * 0.08}s` }}>
          <title>{`${p.label}: ${fmtBRL(p.v)}`}</title>
        </circle>
      ))}
    </svg>
    </div>
  );
}

// Horizontal bars (top despesas)
function HBars({ items, accent = "#16a34a" }) {
  const max = Math.max(1, ...items.map(i => i.value));
  const [ref, inView] = useInView();
  return (
    <div ref={ref} style={{ display: "flex", flexDirection: "column", gap: 10 }}>
      {items.map((it, i) => (
        <div key={i}>
          <div style={{ display: "flex", justifyContent: "space-between", gap: 8, fontSize: 12.5, marginBottom: 4 }}>
            <span style={{ color: "var(--fg-1)", fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", flex: 1 }}>{it.label}</span>
            <span style={{ color: "var(--fg-2)", fontVariantNumeric: "tabular-nums", flexShrink: 0 }}>{fmtBRL(it.value)}</span>
          </div>
          <div style={{ height: 6, background: "var(--surface-2)", borderRadius: 999, overflow: "hidden" }}>
            <div style={{
              width: inView ? `${(it.value / max) * 100}%` : "0%",
              height: "100%",
              background: accent,
              borderRadius: 999,
              transition: `width 0.35s ${EASE} ${i * 80}ms`,
            }} />
          </div>
        </div>
      ))}
    </div>
  );
}

// Calendar heatmap mini (current month)
function CalendarHeat({ transactions, year, month, accent }) {
  const firstDay = new Date(year, month, 1).getDay();
  const daysInMonth = new Date(year, month + 1, 0).getDate();
  const byDay = {};
  transactions.forEach(t => {
    const d = new Date(t.date);
    if (d.getFullYear() === year && d.getMonth() === month) {
      const day = d.getDate();
      byDay[day] = (byDay[day] || 0) + t.value;
    }
  });
  const max = Math.max(1, ...Object.values(byDay));
  const cells = [];
  for (let i = 0; i < firstDay; i++) cells.push(null);
  for (let d = 1; d <= daysInMonth; d++) cells.push(d);
  const dows = ["D", "S", "T", "Q", "Q", "S", "S"];
  return (
    <div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 4, marginBottom: 6 }}>
        {dows.map((d, i) => (
          <div key={i} style={{ fontSize: 10, color: "var(--fg-3)", textAlign: "center", fontWeight: 500 }}>{d}</div>
        ))}
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 4 }}>
        {cells.map((d, i) => {
          if (d === null) return <div key={i} />;
          const v = byDay[d] || 0;
          const intensity = v ? 0.15 + (v / max) * 0.85 : 0;
          return (
            <div key={i} title={v ? `Dia ${d}: ${fmtBRL(v)}` : `Dia ${d}`}
              style={{
                aspectRatio: "1",
                borderRadius: 5,
                background: v ? `color-mix(in oklab, ${accent} ${intensity * 100}%, var(--surface-1))` : "var(--surface-2)",
                fontSize: 10,
                color: v && intensity > 0.5 ? "white" : "var(--fg-2)",
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
                fontVariantNumeric: "tabular-nums",
                fontWeight: v ? 500 : 400,
                cursor: v ? "pointer" : "default",
              }}>
              {d}
            </div>
          );
        })}
      </div>
    </div>
  );
}

// Combo: receita+despesa bars + saldo acumulado linha
function CashflowCombo({ months, accent = "#16a34a" }) {
  const w = 720, h = 280, padX = 48, padY = 28;
  const innerW = w - padX * 2, innerH = h - padY * 2;
  const max = Math.max(1, ...months.flatMap(m => [m.in, m.out]));
  const groupW = innerW / months.length;
  const barW = Math.min(16, groupW * 0.3);
  const ySteps = 4;
  const [ref, inView] = useInView();
  const f = inView ? 1 : 0;

  // Saldo acumulado
  let acc = 0;
  const cumPoints = months.map(m => { acc += (m.in - m.out); return acc; });
  const cMax = Math.max(0, ...cumPoints);
  const cMin = Math.min(0, ...cumPoints);
  const cRange = cMax - cMin || 1;
  const yCum = (v) => padY + innerH - ((v - cMin) / cRange) * innerH;
  const xMid = (i) => padX + groupW * i + groupW / 2;
  const cumPath = cumPoints.map((v, i) => `${i === 0 ? "M" : "L"} ${xMid(i)} ${yCum(v)}`).join(" ");
  const id = "cf-" + Math.random().toString(36).slice(2, 7);

  return (
    <div ref={ref}>
    <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="xMidYMid meet" style={{ width: "100%", height: "auto", maxHeight: 320, display: "block" }}>
      <defs>
        <linearGradient id={id} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={accent} stopOpacity="0.18" />
          <stop offset="100%" stopColor={accent} stopOpacity="0" />
        </linearGradient>
      </defs>
      {Array.from({ length: ySteps + 1 }).map((_, i) => {
        const y = padY + (innerH * i) / ySteps;
        const val = max - (max * i) / ySteps;
        return (
          <g key={i}>
            <line x1={padX} y1={y} x2={w - padX} y2={y} stroke="var(--border)" strokeDasharray="2 4" />
            <text x={padX - 6} y={y + 3} textAnchor="end" fontSize="10" fill="var(--fg-3)" fontFamily="ui-monospace, monospace">{fmtBRLShort(val)}</text>
          </g>
        );
      })}
      {months.map((m, i) => {
        const cx = xMid(i);
        const inH = (m.in / max) * innerH * f;
        const outH = (m.out / max) * innerH * f;
        const delay = `${i * 45}ms`;
        const trs = `y 0.35s ${EASE} ${delay}, height 0.35s ${EASE} ${delay}`;
        return (
          <g key={i}>
            <rect x={cx - barW - 1} y={padY + innerH - inH} width={barW} height={inH} fill={accent} rx="2.5" opacity="0.95" style={{ transition: trs }}>
              <title>{`${m.label}: receita ${fmtBRL(m.in)}`}</title>
            </rect>
            <rect x={cx + 1} y={padY + innerH - outH} width={barW} height={outH} fill="var(--fg-1)" rx="2.5" opacity="0.85" style={{ transition: trs }}>
              <title>{`${m.label}: despesa ${fmtBRL(m.out)}`}</title>
            </rect>
            <text x={cx} y={h - 6} textAnchor="middle" fontSize="11" fill="var(--fg-2)">{m.label}</text>
          </g>
        );
      })}
      {/* Saldo acumulado overlay — linha desenhando */}
      <path d={cumPath} stroke="var(--info)" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round" opacity="0.85"
        pathLength="1" strokeDasharray="1" strokeDashoffset={inView ? 0 : 1}
        style={{ transition: `stroke-dashoffset 0.55s ${EASE} .3s` }} />
      {cumPoints.map((v, i) => (
        <circle key={i} cx={xMid(i)} cy={yCum(v)} r="3" fill="var(--info)"
          opacity={inView ? 1 : 0}
          style={{ transition: `opacity .3s ease ${0.5 + i * 0.06}s` }}>
          <title>{`Saldo acumulado em ${months[i].label}: ${fmtBRL(v)}`}</title>
        </circle>
      ))}
      {/* Legend */}
      <g transform={`translate(${padX}, 8)`}>
        <rect x="0" y="0" width="10" height="10" fill={accent} rx="2" />
        <text x="14" y="9" fontSize="10.5" fill="var(--fg-2)">Receita</text>
        <rect x="70" y="0" width="10" height="10" fill="var(--fg-1)" opacity="0.85" rx="2" />
        <text x="84" y="9" fontSize="10.5" fill="var(--fg-2)">Despesa</text>
        <line x1="140" y1="5" x2="155" y2="5" stroke="var(--info)" strokeWidth="2" />
        <circle cx="147" cy="5" r="2.5" fill="var(--info)" />
        <text x="160" y="9" fontSize="10.5" fill="var(--fg-2)">Saldo acumulado</text>
      </g>
    </svg>
    </div>
  );
}

// Stacked bars: fixas vs variaveis
function StackedBars({ months, accent = "#16a34a" }) {
  const w = 560, h = 200, padX = 40, padY = 20;
  const innerW = w - padX * 2, innerH = h - padY * 2;
  const max = Math.max(1, ...months.map(m => m.fixed + m.variable));
  const groupW = innerW / months.length;
  const barW = Math.min(28, groupW * 0.62);
  const [ref, inView] = useInView();
  const f = inView ? 1 : 0;
  return (
    <div ref={ref}>
    <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="xMidYMid meet" style={{ width: "100%", height: "auto", maxHeight: 240, display: "block" }}>
      {[0, 1, 2, 3, 4].map(i => {
        const y = padY + (innerH * i) / 4;
        const val = max - (max * i) / 4;
        return (
          <g key={i}>
            <line x1={padX} y1={y} x2={w - padX} y2={y} stroke="var(--border)" strokeDasharray="2 4" />
            <text x={padX - 6} y={y + 3} textAnchor="end" fontSize="10" fill="var(--fg-3)" fontFamily="ui-monospace, monospace">{fmtBRLShort(val)}</text>
          </g>
        );
      })}
      {months.map((m, i) => {
        const cx = padX + groupW * i + groupW / 2;
        const fH = (m.fixed / max) * innerH * f;
        const vH = (m.variable / max) * innerH * f;
        const delay = `${i * 50}ms`;
        const trs = `y 0.35s ${EASE} ${delay}, height 0.35s ${EASE} ${delay}`;
        return (
          <g key={i}>
            <rect x={cx - barW / 2} y={padY + innerH - fH} width={barW} height={fH} fill="var(--fg-1)" opacity="0.85" rx="3" style={{ transition: trs }}>
              <title>{`${m.label}: fixas ${fmtBRL(m.fixed)}`}</title>
            </rect>
            <rect x={cx - barW / 2} y={padY + innerH - fH - vH} width={barW} height={vH} fill={accent} opacity="0.6" rx="3" style={{ transition: trs }}>
              <title>{`${m.label}: variáveis ${fmtBRL(m.variable)}`}</title>
            </rect>
            <text x={cx} y={h - 4} textAnchor="middle" fontSize="11" fill="var(--fg-2)">{m.label}</text>
          </g>
        );
      })}
    </svg>
    </div>
  );
}

// Sparkline
function Sparkline({ values, accent = "#16a34a", width = 80, height = 24 }) {
  const [ref, inView] = useInView();
  if (!values.length) return null;
  const max = Math.max(...values, 1);
  const min = Math.min(...values, 0);
  const range = max - min || 1;
  const x = (i) => (width * i) / Math.max(1, values.length - 1);
  const y = (v) => height - ((v - min) / range) * height;
  const path = values.map((v, i) => `${i === 0 ? "M" : "L"} ${x(i)} ${y(v)}`).join(" ");
  return (
    <span ref={ref} style={{ display: "inline-block" }}>
      <svg width={width} height={height} style={{ display: "block" }}>
        <path d={path} stroke={accent} strokeWidth="1.5" fill="none" strokeLinecap="round" strokeLinejoin="round"
          pathLength="1" strokeDasharray="1" strokeDashoffset={inView ? 0 : 1}
          style={{ transition: `stroke-dashoffset 0.4s ${EASE}` }} />
      </svg>
    </span>
  );
}

Object.assign(window, { Donut, BarsCompare, CashflowCombo, StackedBars, Sparkline, AreaTrend, HBars, CalendarHeat, fmtBRL, fmtBRLShort, monthLabel });
