// Reusable chart + table primitives — light theme

const fmtUSD = (n, dp = 2) => '$' + (n || 0).toLocaleString('en-US', { minimumFractionDigits: dp, maximumFractionDigits: dp });
const fmtNum = (n) => (n || 0).toLocaleString('en-US');
const fmtCompact = (n) => {
  if (n == null) return '0';
  if (n >= 1e9) return (n / 1e9).toFixed(1) + 'B';
  if (n >= 1e6) return (n / 1e6).toFixed(1) + 'M';
  if (n >= 1e3) return (n / 1e3).toFixed(1) + 'K';
  return Math.round(n).toString();
};
const fmtPct = (n, dp = 1) => (n * 100).toFixed(dp) + '%';
const fmtDelta = (n) => (n >= 0 ? '+' : '') + (n * 100).toFixed(1) + '%';

window.fmtUSD = fmtUSD;
window.fmtNum = fmtNum;
window.fmtCompact = fmtCompact;
window.fmtPct = fmtPct;
window.fmtDelta = fmtDelta;

// ResponsiveBox：用 ResizeObserver 偵測 container 寬度，把實際 px 傳進 children render-prop。
// 圖表元件本來都 hardcode width={920/760/...}，包進來後會自動 fit container（cards 寬度變動也會 reflow）。
// 用法：
//   <ResponsiveBox height={240}>{(w, h) => <StackedArea w={w} h={h} ... />}</ResponsiveBox>
function ResponsiveBox({ height, minWidth = 200, children }) {
  const ref = React.useRef(null);
  const [w, setW] = React.useState(0);
  React.useLayoutEffect(() => {
    if (!ref.current) return;
    const measure = () => {
      const cw = ref.current ? ref.current.clientWidth : 0;
      if (cw > 0) setW(Math.floor(cw));
    };
    measure();
    const ro = new ResizeObserver(measure);
    ro.observe(ref.current);
    window.addEventListener('resize', measure);
    return () => { ro.disconnect(); window.removeEventListener('resize', measure); };
  }, []);
  return (
    <div ref={ref} style={{ width: '100%', minWidth, height }}>
      {w > 0 ? children(w, height) : null}
    </div>
  );
}
window.ResponsiveBox = ResponsiveBox;

function Sparkline({ data, w = 80, h = 22, color = '#5C6470', fill = false, strokeWidth = 1.25 }) {
  if (!data || data.length === 0) return null;
  const max = Math.max(...data);
  const min = Math.min(...data);
  const range = max - min || 1;
  const stepX = w / (data.length - 1);
  const points = data.map((v, i) => [i * stepX, h - 2 - ((v - min) / range) * (h - 4)]);
  const path = points.map((p, i) => (i === 0 ? 'M' : 'L') + p[0].toFixed(1) + ',' + p[1].toFixed(1)).join(' ');
  const fillPath = path + ` L ${w},${h} L 0,${h} Z`;
  return (
    <svg width={w} height={h} style={{ display: 'block' }}>
      {fill && <path d={fillPath} fill={color} fillOpacity={0.14} />}
      <path d={path} stroke={color} strokeWidth={strokeWidth} fill="none" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

function StackedArea({ series, dates, colors, w = 800, h = 240, totalLabel = '總計' }) {
  const n = dates.length;
  const totals = dates.map((_, i) => series.reduce((s, ser) => s + ser.values[i], 0));
  const max = Math.max(...totals) * 1.05;
  const padL = 50, padR = 12, padT = 14, padB = 24;
  const innerW = w - padL - padR;
  const innerH = h - padT - padB;
  const stepX = innerW / (n - 1);

  const stack = [];
  let lower = new Array(n).fill(0);
  series.forEach((ser, si) => {
    const upper = lower.map((l, i) => l + ser.values[i]);
    const points = [];
    for (let i = 0; i < n; i++) {
      points.push([padL + i * stepX, padT + innerH - (upper[i] / max) * innerH]);
    }
    const lowerPts = [];
    for (let i = n - 1; i >= 0; i--) {
      lowerPts.push([padL + i * stepX, padT + innerH - (lower[i] / max) * innerH]);
    }
    const path = points.map((p, i) => (i === 0 ? 'M' : 'L') + p[0].toFixed(1) + ',' + p[1].toFixed(1)).join(' ')
      + ' ' + lowerPts.map(p => 'L' + p[0].toFixed(1) + ',' + p[1].toFixed(1)).join(' ') + ' Z';
    const linePath = points.map((p, i) => (i === 0 ? 'M' : 'L') + p[0].toFixed(1) + ',' + p[1].toFixed(1)).join(' ');
    stack.push({ ser, path, linePath, color: colors[si % colors.length] });
    lower = upper;
  });

  const yTicks = 4;
  const tickVals = Array.from({ length: yTicks + 1 }, (_, i) => max * (i / yTicks));
  const xTickIdx = [];
  for (let i = 0; i < n; i += 5) xTickIdx.push(i);
  if (xTickIdx[xTickIdx.length - 1] !== n - 1) xTickIdx.push(n - 1);

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

  return (
    <div style={{ position: 'relative' }}>
      <svg width={w} height={h} style={{ display: 'block' }}
        onMouseMove={(e) => {
          const rect = e.currentTarget.getBoundingClientRect();
          const x = e.clientX - rect.left;
          const i = Math.round((x - padL) / stepX);
          if (i >= 0 && i < n) setHover({ i, x: padL + i * stepX });
        }}
        onMouseLeave={() => setHover(null)}
      >
        {tickVals.map((v, i) => {
          const y = padT + innerH - (v / max) * innerH;
          return (
            <g key={i}>
              <line x1={padL} x2={w - padR} y1={y} y2={y} stroke="#EEEEE9" strokeDasharray="2 3" />
              <text x={padL - 8} y={y + 3} fontSize={10} fill="#9098A3" textAnchor="end" fontFamily="JetBrains Mono">${fmtCompact(v)}</text>
            </g>
          );
        })}
        {xTickIdx.map((i) => (
          <text key={i} x={padL + i * stepX} y={h - 6} fontSize={10} fill="#9098A3" textAnchor="middle" fontFamily="JetBrains Mono">{dates[i].slice(5)}</text>
        ))}
        {stack.map((s, i) => (
          <g key={i}>
            <path d={s.path} fill={s.color} fillOpacity={0.18} />
            <path d={s.linePath} stroke={s.color} strokeWidth={1.25} fill="none" />
          </g>
        ))}
        {hover && (
          <g>
            <line x1={hover.x} x2={hover.x} y1={padT} y2={padT + innerH} stroke="#C5C5C0" strokeWidth={1} />
            {stack.map((s, si) => {
              const v = s.ser.values[hover.i];
              let yAcc = 0;
              for (let k = 0; k <= si; k++) yAcc += stack[k].ser.values[hover.i];
              const y = padT + innerH - (yAcc / max) * innerH;
              return <circle key={si} cx={hover.x} cy={y} r={2.5} fill={s.color} stroke="#fff" strokeWidth={1.5} />;
            })}
          </g>
        )}
      </svg>
      {hover && (
        <div style={{
          position: 'absolute', left: Math.min(hover.x + 12, w - 200), top: 10,
          background: '#FFFFFF', border: '1px solid #D4D4CF', borderRadius: 6,
          padding: '8px 10px', fontSize: 11, pointerEvents: 'none', minWidth: 180,
          boxShadow: '0 6px 20px rgba(0,0,0,0.10)',
        }}>
          <div style={{ color: '#5C6470', fontFamily: 'JetBrains Mono', marginBottom: 6 }}>{dates[hover.i]}</div>
          {[...stack].reverse().map((s, i) => (
            <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 6, justifyContent: 'space-between', marginBottom: 2 }}>
              <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                <span style={{ width: 8, height: 8, background: s.color, borderRadius: 2 }}></span>
                <span style={{ color: '#17171A' }}>{s.ser.name}</span>
              </span>
              <span style={{ fontFamily: 'JetBrains Mono', color: '#17171A' }}>{fmtUSD(s.ser.values[hover.i])}</span>
            </div>
          ))}
          <div style={{ borderTop: '1px solid #EEEEE9', marginTop: 6, paddingTop: 6, display: 'flex', justifyContent: 'space-between', fontFamily: 'JetBrains Mono', color: '#7A6E00', fontWeight: 600 }}>
            <span>{totalLabel}</span>
            <span>{fmtUSD(totals[hover.i])}</span>
          </div>
        </div>
      )}
    </div>
  );
}

function Donut({ slices, size = 140, thickness = 22, centerLabel, centerValue }) {
  const total = slices.reduce((s, x) => s + x.value, 0) || 1;
  const cx = size / 2, cy = size / 2;
  const r = (size - thickness) / 2;
  let acc = 0;
  return (
    <svg width={size} height={size}>
      {slices.map((s, i) => {
        const start = acc / total * Math.PI * 2 - Math.PI / 2;
        acc += s.value;
        const end = acc / total * Math.PI * 2 - Math.PI / 2;
        const large = end - start > Math.PI ? 1 : 0;
        const x1 = cx + r * Math.cos(start), y1 = cy + r * Math.sin(start);
        const x2 = cx + r * Math.cos(end), y2 = cy + r * Math.sin(end);
        const path = `M ${x1} ${y1} A ${r} ${r} 0 ${large} 1 ${x2} ${y2}`;
        return <path key={i} d={path} stroke={s.color} strokeWidth={thickness} fill="none" />;
      })}
      {centerLabel && (
        <text x={cx} y={cy - 4} textAnchor="middle" fontSize={10} fill="#9098A3" fontFamily="JetBrains Mono">{centerLabel}</text>
      )}
      {centerValue && (
        <text x={cx} y={cy + 14} textAnchor="middle" fontSize={16} fill="#17171A" fontFamily="JetBrains Mono" fontWeight={600}>{centerValue}</text>
      )}
    </svg>
  );
}

function Pill({ children, color = 'gray' }) {
  const palette = {
    green:  { bg: '#DCFCE7', fg: '#15803D', border: '#86EFAC' },
    red:    { bg: '#FEE2E2', fg: '#DC2626', border: '#FCA5A5' },
    amber:  { bg: '#FEF3C7', fg: '#B45309', border: '#FCD34D' },
    blue:   { bg: '#DBEAFE', fg: '#1D4ED8', border: '#93C5FD' },
    purple: { bg: '#EDE9FE', fg: '#6D28D9', border: '#C4B5FD' },
    yellow: { bg: '#FFFAB8', fg: '#7A6E00', border: '#FFE600' },
    gray:   { bg: '#F0F0EE', fg: '#5C6470', border: '#D4D4CF' },
  };
  const p = palette[color] || palette.gray;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5,
      padding: '2px 7px', borderRadius: 4,
      background: p.bg, color: p.fg, border: `1px solid ${p.border}`,
      fontSize: 10.5, fontWeight: 500, lineHeight: 1.4,
      letterSpacing: '0.01em',
    }}>{children}</span>
  );
}

function TierBadge({ tier }) {
  const map = {
    enterprise: { color: 'purple', label: '企業版' },
    growth:     { color: 'blue', label: '成長版' },
    starter:    { color: 'gray', label: '入門版' },
    trial:      { color: 'amber', label: '試用' },
  };
  const m = map[tier] || map.starter;
  return <Pill color={m.color}>{m.label}</Pill>;
}

function StatusDot({ status }) {
  const map = {
    active:     { color: '#15803D', label: '使用中' },
    anomalous:  { color: '#DC2626', label: '異常' },
    dormant:    { color: '#9098A3', label: '沈睡' },
  };
  const m = map[status] || map.dormant;
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 11, color: '#5C6470' }}>
      <span style={{ width: 6, height: 6, borderRadius: '50%', background: m.color }}></span>
      {m.label}
    </span>
  );
}

window.Sparkline = Sparkline;
window.StackedArea = StackedArea;
window.Donut = Donut;
window.Pill = Pill;
window.TierBadge = TierBadge;
window.StatusDot = StatusDot;
