// Chart primitives — all SVG, all animated. Donut, area, bar, sankey, calendar heatmap, stacked progress.

const useContainerWidth = (fallback) => {
  const ref = React.useRef(null);
  const [width, setWidth] = React.useState(fallback);
  React.useEffect(() => {
    if (!ref.current) return;
    setWidth(ref.current.getBoundingClientRect().width || fallback);
    const ro = new ResizeObserver(([e]) => setWidth(e.contentRect.width));
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);
  return [ref, width];
};

// ---------- Animated Donut ----------
const Donut = ({ data, size = 220, thickness = 28, centerLabel, centerValue, centerSub }) => {
  const total = data.reduce((a, d) => a + d.amount, 0);
  const radius = (size - thickness) / 2;
  const cx = size / 2, cy = size / 2;
  const C = 2 * Math.PI * radius;

  const [hovered, setHovered] = React.useState(null);
  const [drawn, setDrawn] = React.useState(false);
  React.useEffect(() => {
    const t = setTimeout(() => setDrawn(true), 60);
    return () => clearTimeout(t);
  }, []);

  let offset = 0;
  const arcs = data.map((d, i) => {
    const frac = total ? d.amount / total : 0;
    const len = C * frac;
    const arc = {
      d, frac, offset,
      strokeDasharray: drawn ? `${len} ${C - len}` : `0 ${C}`,
      strokeDashoffset: -offset,
    };
    offset += len;
    return arc;
  });

  const displayValue = hovered != null ? data[hovered].amount : (centerValue ?? total);
  const displayLabel = hovered != null ? data[hovered].label : centerLabel;
  const animatedValue = useCountUp(displayValue);

  return (
    <div style={{display:'flex', alignItems:'center', justifyContent:'center', position:'relative'}}>
      <svg width={size} height={size} style={{overflow:'visible'}}>
        {/* Track */}
        <circle cx={cx} cy={cy} r={radius} fill="none" stroke="var(--line-2)" strokeWidth={thickness} />
        {arcs.map((a, i) => (
          <circle
            key={i}
            cx={cx} cy={cy} r={radius}
            fill="none"
            stroke={`var(${a.d.cssVar})`}
            strokeWidth={hovered === i ? thickness + 4 : thickness}
            strokeDasharray={a.strokeDasharray}
            strokeDashoffset={a.strokeDashoffset}
            transform={`rotate(-90 ${cx} ${cy})`}
            style={{
              transition: 'stroke-dasharray 0.9s cubic-bezier(0.2,0.8,0.2,1), stroke-width 0.2s',
              cursor: 'pointer',
            }}
            onMouseEnter={() => setHovered(i)}
            onMouseLeave={() => setHovered(null)}
          />
        ))}
      </svg>
      <div style={{
        position: 'absolute',
        textAlign: 'center',
        pointerEvents: 'none',
      }}>
        <div style={{fontSize:11, color:'var(--ink-3)', textTransform:'uppercase', letterSpacing:'0.06em', fontWeight:500}}>{displayLabel}</div>
        <div className="serif" style={{fontSize: 28, marginTop:2, lineHeight:1, fontVariantNumeric:'tabular-nums'}}>
          {fmtZARShort(animatedValue)}
        </div>
        {centerSub && hovered == null && (
          <div style={{fontSize:11, color:'var(--ink-3)', marginTop:4}}>{centerSub}</div>
        )}
      </div>
    </div>
  );
};

// ---------- Animated Area / Line ----------
const AreaChart = ({ months, height = 200 }) => {
  const [containerRef, width] = useContainerWidth(600);
  const pad = { l: 36, r: 16, t: 12, b: 24 };
  const w = width - pad.l - pad.r;
  const h = height - pad.t - pad.b;

  const allVals = months.flatMap(m => [m.income, m.expense]);
  const maxV = Math.max(...allVals, 1) * 1.1;

  const [hover, setHover] = React.useState(null);
  const [drawn, setDrawn] = React.useState(false);
  React.useEffect(() => {
    const t = setTimeout(() => setDrawn(true), 100);
    return () => clearTimeout(t);
  }, []);

  const x = (i) => pad.l + (months.length === 1 ? w/2 : (w * i) / (months.length - 1));
  const y = (v) => pad.t + h - (v / maxV) * h;

  const linePath = (key) => months.map((m, i) => `${i === 0 ? 'M' : 'L'} ${x(i)} ${y(m[key])}`).join(' ');
  const areaPath = (key) => `${linePath(key)} L ${x(months.length-1)} ${pad.t + h} L ${x(0)} ${pad.t + h} Z`;

  // y-axis ticks
  const ticks = [0, 0.5, 1].map(t => ({ v: t * maxV, y: y(t * maxV) }));

  const lineLen = w * 1.5;

  return (
    <div ref={containerRef} style={{width:'100%'}}>
    <svg width={width} height={height} style={{display:'block'}}
      onMouseLeave={() => setHover(null)}
    >
      <defs>
        <linearGradient id="incomeGrad" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="var(--mint)" stopOpacity="0.35"/>
          <stop offset="100%" stopColor="var(--mint)" stopOpacity="0"/>
        </linearGradient>
        <linearGradient id="expenseGrad" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="var(--coral)" stopOpacity="0.25"/>
          <stop offset="100%" stopColor="var(--coral)" stopOpacity="0"/>
        </linearGradient>
      </defs>

      {/* Grid */}
      {ticks.map((t, i) => (
        <g key={i}>
          <line x1={pad.l} x2={pad.l + w} y1={t.y} y2={t.y} stroke="var(--line-2)" strokeDasharray="2 4" />
          <text x={pad.l - 8} y={t.y + 3} fontSize="10" fill="var(--ink-3)" textAnchor="end" className="mono">
            {fmtZARShort(t.v)}
          </text>
        </g>
      ))}

      {/* Areas */}
      <path d={areaPath('income')} fill="url(#incomeGrad)" style={{opacity: drawn ? 1 : 0, transition: 'opacity 0.6s ease 0.4s'}}/>
      <path d={areaPath('expense')} fill="url(#expenseGrad)" style={{opacity: drawn ? 1 : 0, transition: 'opacity 0.6s ease 0.5s'}}/>

      {/* Lines */}
      <path
        d={linePath('income')}
        fill="none" stroke="var(--mint-deep)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"
        className="draw-line" style={{'--draw-len': lineLen}}
      />
      <path
        d={linePath('expense')}
        fill="none" stroke="var(--coral)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"
        className="draw-line" style={{'--draw-len': lineLen, animationDelay: '0.15s'}}
      />

      {/* X-axis labels */}
      {months.map((m, i) => (
        <text key={i} x={x(i)} y={height - 6} fontSize="11" fill="var(--ink-3)" textAnchor="middle" className="mono">{m.label}</text>
      ))}

      {/* Hover hit areas */}
      {months.map((m, i) => {
        const xc = x(i);
        const wHit = w / months.length;
        return (
          <g key={`h-${i}`}>
            <rect
              x={xc - wHit/2} y={pad.t} width={wHit} height={h}
              fill="transparent"
              onMouseEnter={() => setHover(i)}
            />
          </g>
        );
      })}

      {/* Hover indicator */}
      {hover != null && (
        <g className="chart-hover-line">
          <line x1={x(hover)} x2={x(hover)} y1={pad.t} y2={pad.t + h} stroke="var(--ink-2)" strokeDasharray="2 3" strokeWidth="1"/>
          <circle cx={x(hover)} cy={y(months[hover].income)} r="4" fill="var(--mint-deep)" stroke="white" strokeWidth="2"/>
          <circle cx={x(hover)} cy={y(months[hover].expense)} r="4" fill="var(--coral)" stroke="white" strokeWidth="2"/>
        </g>
      )}

      {/* Animated dots */}
      {drawn && months.map((m, i) => (
        <g key={`d-${i}`} style={{opacity: hover === i ? 0 : 1, transition:'opacity 0.15s'}}>
          <circle cx={x(i)} cy={y(m.income)} r="2.5" fill="var(--mint-deep)"/>
          <circle cx={x(i)} cy={y(m.expense)} r="2.5" fill="var(--coral)"/>
        </g>
      ))}

      {/* Tooltip */}
      {hover != null && (
        <foreignObject x={Math.min(x(hover) + 10, width - 150)} y={pad.t + 4} width="150" height="80">
          <div style={{background:'var(--ink)', color:'white', padding:'8px 10px', borderRadius:8, fontSize:11.5, boxShadow:'var(--shadow)'}}>
            <div style={{opacity:0.7, marginBottom:4}}>{months[hover].label}</div>
            <div style={{display:'flex', alignItems:'center', gap:6}}>
              <span style={{width:6, height:6, borderRadius:'50%', background:'var(--mint)'}}></span>
              In: <span className="mono" style={{marginLeft:'auto'}}>{fmtZARShort(months[hover].income)}</span>
            </div>
            <div style={{display:'flex', alignItems:'center', gap:6, marginTop:2}}>
              <span style={{width:6, height:6, borderRadius:'50%', background:'var(--coral)'}}></span>
              Out: <span className="mono" style={{marginLeft:'auto'}}>{fmtZARShort(months[hover].expense)}</span>
            </div>
          </div>
        </foreignObject>
      )}
    </svg>
    </div>
  );
};

// ---------- Bar Chart ----------
const BarChart = ({ months, height = 180, width = 600 }) => {
  const pad = { l: 36, r: 12, t: 12, b: 24 };
  const w = width - pad.l - pad.r;
  const h = height - pad.t - pad.b;

  const maxV = Math.max(...months.flatMap(m => [m.income, m.expense]), 1) * 1.1;
  const groupW = w / months.length;
  const barW = Math.min(18, groupW / 3);

  const [drawn, setDrawn] = React.useState(false);
  React.useEffect(() => {
    const t = setTimeout(() => setDrawn(true), 60);
    return () => clearTimeout(t);
  }, []);

  return (
    <svg width={width} height={height} style={{display:'block', maxWidth:'100%'}}>
      <line x1={pad.l} x2={pad.l + w} y1={pad.t + h} y2={pad.t + h} stroke="var(--line)"/>
      {months.map((m, i) => {
        const cx = pad.l + groupW * i + groupW / 2;
        const hIn = (m.income / maxV) * h;
        const hOut = (m.expense / maxV) * h;
        return (
          <g key={i}>
            <rect
              x={cx - barW - 2}
              y={pad.t + h - (drawn ? hIn : 0)}
              width={barW}
              height={drawn ? hIn : 0}
              fill="var(--mint)"
              rx="3"
              style={{transition: `all 0.7s cubic-bezier(0.2,0.8,0.2,1) ${i * 0.05}s`}}
            />
            <rect
              x={cx + 2}
              y={pad.t + h - (drawn ? hOut : 0)}
              width={barW}
              height={drawn ? hOut : 0}
              fill="var(--coral)"
              rx="3"
              style={{transition: `all 0.7s cubic-bezier(0.2,0.8,0.2,1) ${i * 0.05 + 0.05}s`}}
            />
            <text x={cx} y={height - 6} fontSize="11" fill="var(--ink-3)" textAnchor="middle" className="mono">{m.label}</text>
          </g>
        );
      })}
    </svg>
  );
};

// ---------- Sankey-style flow ----------
const SankeyFlow = ({ income, expenseByCategory, savings, debtPayments, height = 320 }) => {
  const [containerRef, width] = useContainerWidth(720);
  const narrow = width < 500;
  const pad = { l: narrow ? 52 : 80, r: narrow ? 100 : 80, t: 20, b: 20 };
  const labelFontSize = narrow ? 10 : 11.5;
  const valueFontSize = narrow ? 9.5 : 10.5;
  const w = width - pad.l - pad.r;
  const h = height - pad.t - pad.b;

  const total = income;
  if (!total) return null;

  // Right side: expenses (categories) + savings + debt
  const rightNodes = [
    ...expenseByCategory.map(c => ({ ...c, value: c.amount, isCategory: true })),
    ...(savings > 0 ? [{ id: 'savings', label: 'Savings', cssVar: '--mint', value: savings, isSavings: true }] : []),
    ...(debtPayments > 0 ? [{ id: 'debt', label: 'Debt payments', cssVar: '--ink', value: debtPayments }] : []),
  ];
  const rightTotal = rightNodes.reduce((a, n) => a + n.value, 0) || total;

  // Layout
  const leftX = pad.l;
  const rightX = pad.l + w;
  const nodeW = 12;
  let leftY = pad.t;
  const leftH = h * (rightTotal / Math.max(rightTotal, total));
  // Single income block on the left
  let acc = pad.t;
  const rightLayout = rightNodes.map(n => {
    const nh = (n.value / rightTotal) * h;
    const block = { ...n, y: acc, h: nh };
    acc += nh + 2;
    return block;
  });
  const totalH = acc - pad.t;

  const [drawn, setDrawn] = React.useState(false);
  React.useEffect(() => {
    const t = setTimeout(() => setDrawn(true), 80);
    return () => clearTimeout(t);
  }, []);

  let leftCursor = pad.t;
  const flows = rightLayout.map(r => {
    const flow = {
      ...r,
      sy: leftCursor,
      sh: r.h,
    };
    leftCursor += r.h + 2;
    return flow;
  });

  const [hover, setHover] = React.useState(null);

  const flowPath = (sy, sh, ey, eh) => {
    const x0 = leftX + nodeW;
    const x1 = rightX;
    const y0a = sy, y0b = sy + sh;
    const y1a = ey, y1b = ey + eh;
    const cx0 = x0 + (x1 - x0) * 0.5;
    const cx1 = x0 + (x1 - x0) * 0.5;
    return `M ${x0} ${y0a} C ${cx0} ${y0a} ${cx1} ${y1a} ${x1} ${y1a} L ${x1} ${y1b} C ${cx1} ${y1b} ${cx0} ${y0b} ${x0} ${y0b} Z`;
  };

  return (
    <div ref={containerRef} style={{width:'100%'}}>
    <svg width={width} height={height} style={{display:'block'}}>
      {/* Left node — Income */}
      <rect x={leftX} y={pad.t} width={nodeW} height={drawn ? totalH : 0} fill="var(--mint-deep)" rx="3"
        style={{transition:'height 0.7s cubic-bezier(0.2,0.8,0.2,1)'}}/>
      <text x={leftX - 8} y={pad.t - 4} fontSize="10" fill="var(--ink-3)" textAnchor="end" className="mono" style={{textTransform:'uppercase', letterSpacing:'0.06em'}}>Income</text>
      <text x={leftX - 8} y={pad.t + 12} fontSize={narrow ? 12 : 14} fill="var(--ink)" textAnchor="end" fontWeight="500">
        {fmtZARShort(total)}
      </text>

      {/* Flows */}
      {flows.map((f, i) => (
        <path
          key={i}
          d={flowPath(f.sy, f.sh, f.y, f.h)}
          fill={`var(${f.cssVar})`}
          opacity={hover == null ? 0.35 : (hover === i ? 0.7 : 0.15)}
          style={{transition:'opacity 0.2s, transform 0.7s cubic-bezier(0.2,0.8,0.2,1)', transformOrigin:'left', transform: drawn ? 'scaleX(1)' : 'scaleX(0)'}}
          onMouseEnter={() => setHover(i)}
          onMouseLeave={() => setHover(null)}
        />
      ))}

      {/* Right nodes */}
      {rightLayout.map((r, i) => (
        <g key={i}>
          <rect
            x={rightX} y={r.y} width={nodeW} height={drawn ? r.h : 0}
            fill={`var(${r.cssVar})`} rx="3"
            style={{transition:`height 0.7s cubic-bezier(0.2,0.8,0.2,1) ${i*0.04}s`}}
          />
          <text x={rightX + nodeW + 8} y={r.y + r.h/2 - 2} fontSize={labelFontSize} fill="var(--ink)" fontWeight="500">{r.label}</text>
          <text x={rightX + nodeW + 8} y={r.y + r.h/2 + 11} fontSize={valueFontSize} fill="var(--ink-3)" className="mono">{fmtZARShort(r.value)}</text>
        </g>
      ))}
    </svg>
    </div>
  );
};

// ---------- Stacked progress (debt) ----------
const StackedProgress = ({ items, height = 14 }) => {
  const total = items.reduce((a, x) => a + x.value, 0) || 1;
  const [drawn, setDrawn] = React.useState(false);
  React.useEffect(() => {
    const t = setTimeout(() => setDrawn(true), 80);
    return () => clearTimeout(t);
  }, []);
  return (
    <div style={{display:'flex', height, borderRadius: height/2, overflow:'hidden', background:'var(--line-2)'}}>
      {items.map((it, i) => (
        <div key={i} style={{
          width: drawn ? `${(it.value / total) * 100}%` : '0%',
          background: `var(${it.cssVar})`,
          transition: `width 0.9s cubic-bezier(0.2,0.8,0.2,1) ${i*0.06}s`,
        }}/>
      ))}
    </div>
  );
};

// ---------- Calendar Heatmap ----------
const CalendarHeatmap = ({ year = today().getFullYear() }) => {
  const { state } = useAppState();
  // Build a value per day in current year (sum of expenses)
  const dayMap = {};
  state.transactions.forEach(t => {
    if (t.type !== 'expense') return;
    const d = new Date(t.date);
    if (d.getFullYear() !== year) return;
    const key = `${d.getMonth()}-${d.getDate()}`;
    dayMap[key] = (dayMap[key] || 0) + t.amount;
  });
  const max = Math.max(...Object.values(dayMap), 1);

  // Build grid: 12 months x ~31 cells
  return (
    <div className="heatmap-grid" style={{display:'grid', gridTemplateColumns:'repeat(12, 1fr)', gap:8}}>
      {monthNames.map((mn, mi) => {
        const days = new Date(year, mi + 1, 0).getDate();
        return (
          <div key={mi}>
            <div style={{fontSize:10, color:'var(--ink-3)', marginBottom:4, fontWeight:500}} className="mono">{mn}</div>
            <div style={{display:'grid', gridTemplateColumns:'repeat(4, 1fr)', gap:2}}>
              {Array.from({length: days}, (_, di) => {
                const v = dayMap[`${mi}-${di+1}`] || 0;
                const intensity = max ? v / max : 0;
                const bg = v > 0 ? `oklch(${0.95 - intensity * 0.4} ${intensity * 0.13} 30)` : 'oklch(0.96 0.005 250)';
                return (
                  <div
                    key={di}
                    title={`${di+1} ${mn}: ${fmtZAR(v, {cents:false})}`}
                    style={{
                      width:'100%', aspectRatio:'1', borderRadius:2,
                      background: bg,
                      cursor:'pointer',
                      transition:'transform 0.15s',
                    }}
                    onMouseEnter={e => e.currentTarget.style.transform = 'scale(1.4)'}
                    onMouseLeave={e => e.currentTarget.style.transform = 'scale(1)'}
                  />
                );
              })}
            </div>
          </div>
        );
      })}
    </div>
  );
};

Object.assign(window, { Donut, AreaChart, BarChart, SankeyFlow, StackedProgress, CalendarHeatmap });
