// charts.jsx — lightweight SVG charts for UROSYS. Exports to window.

// Semicircle gauge for CAR (%). thresholds: alert < warnMin, warn < okMin, ok >=.
function Gauge({ value, min = 0, max = 300, threshold = 180, okMin = 180, warnMin = 150, unit = '%', size = 230 }) {
  const w = size,h = size * 0.62,cx = w / 2,cy = h - 6,r = w / 2 - 18;
  const a0 = Math.PI,a1 = 0; // 180° -> 0°
  const pol = (frac) => {const a = a0 + (a1 - a0) * frac;return [cx + r * Math.cos(a), cy + r * Math.sin(a)];};
  const arc = (f0, f1) => {const [x0, y0] = pol(f0),[x1, y1] = pol(f1);return `M ${x0} ${y0} A ${r} ${r} 0 0 1 ${x1} ${y1}`;};
  const clamp = (v) => Math.max(0, Math.min(1, (v - min) / (max - min)));
  const vf = clamp(value),tf = clamp(threshold);
  const color = value >= okMin ? T.ok : value >= warnMin ? T.warn : T.alert;
  const [nx, ny] = pol(vf);
  return (
    <svg width={w} height={h + 30} viewBox={`0 0 ${w} ${h + 30}`} style={{ display: 'block', margin: '0 auto' }}>
      {/* zones */}
      <path d={arc(0, clamp(warnMin))} fill="none" stroke={T.alertS} strokeWidth="13" strokeLinecap="round" />
      <path d={arc(clamp(warnMin), clamp(okMin))} fill="none" stroke={T.warnS} strokeWidth="13" />
      <path d={arc(clamp(okMin), 1)} fill="none" stroke={T.okS} strokeWidth="13" strokeLinecap="round" />
      {/* value arc */}
      <path d={arc(0, vf)} fill="none" stroke={color} strokeWidth="13" strokeLinecap="round" />
      {/* threshold tick */}
      <line x1={cx + (r - 10) * Math.cos(a0 + (a1 - a0) * tf)} y1={cy + (r - 10) * Math.sin(a0 + (a1 - a0) * tf)}
      x2={cx + (r + 10) * Math.cos(a0 + (a1 - a0) * tf)} y2={cy + (r + 10) * Math.sin(a0 + (a1 - a0) * tf)}
      stroke={T.c700} strokeWidth="2.4" strokeLinecap="round" />
      <circle cx={nx} cy={ny} r="6" fill="#fff" stroke={color} strokeWidth="3" />
      {/* center value */}
      <text x={cx} y={cy - 26} textAnchor="middle" fontFamily={T.font} fontWeight="700" fontSize="34" fill={T.c900} style={{ letterSpacing: '-.02em' }} className="num">{value.toFixed(1)}<tspan fontSize="17" fill={T.c400} dx="2">{unit}</tspan></text>
      <text x={cx} y={cy - 6} textAnchor="middle" fontFamily={T.font} fontWeight="600" fontSize="11" fill={T.c400}>Threshold {threshold}{unit}</text>
      <text x={pol(0)[0]} y={pol(0)[1] + 22} textAnchor="middle" fontFamily={T.font} fontWeight="600" fontSize="11" fill={T.c400}>{min}</text>
      <text x={pol(1)[0]} y={pol(1)[1] + 22} textAnchor="middle" fontFamily={T.font} fontWeight="600" fontSize="11" fill={T.c400}>{max}</text>
    </svg>);

}

// CAR 12-month trend line with threshold reference
function TrendChart({ data, threshold = 180, w = 640, h = 200, unit = '%' }) {
  const padL = 40,padR = 14,padT = 16,padB = 26;
  const iw = w - padL - padR,ih = h - padT - padB;
  const vals = data.map((d) => d.v);
  const min = Math.min(...vals, threshold) - 10,max = Math.max(...vals, threshold) + 10;
  const x = (i) => padL + i / (data.length - 1) * iw;
  const y = (v) => padT + ih - (v - min) / (max - min) * ih;
  const line = data.map((d, i) => `${x(i)},${y(d.v)}`).join(' ');
  const area = `${padL},${padT + ih} ${line} ${padL + iw},${padT + ih}`;
  const yTicks = [min, (min + max) / 2, max].map(Math.round);
  return (
    <svg width="100%" viewBox={`0 0 ${w} ${h}`} style={{ display: 'block' }}>
      <defs><linearGradient id="tg" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stopColor={T.blue} stopOpacity=".14" /><stop offset="1" stopColor={T.blue} stopOpacity="0" /></linearGradient></defs>
      {yTicks.map((t, i) => <g key={i}><line x1={padL} y1={y(t)} x2={w - padR} y2={y(t)} stroke={T.c150} /><text x={padL - 8} y={y(t) + 3.5} textAnchor="end" fontFamily={T.font} fontSize="10" fontWeight="600" fill={T.c400} className="num">{t}</text></g>)}
      {/* threshold */}
      <line x1={padL} y1={y(threshold)} x2={w - padR} y2={y(threshold)} stroke={T.alert} strokeWidth="1.4" strokeDasharray="5 4" />
      <text x={w - padR} y={y(threshold) - 6} textAnchor="end" fontFamily={T.font} fontSize="10.5" fontWeight="700" fill={T.alert}>Regulatory minimum {threshold}{unit}</text>
      <polygon points={area} fill="url(#tg)" />
      <polyline points={line} fill="none" stroke={T.blue} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" />
      {data.map((d, i) => <g key={i}>
        {i === data.length - 1 && <circle cx={x(i)} cy={y(d.v)} r="4" fill="#fff" stroke={T.blue} strokeWidth="2.5" />}
        <text x={x(i)} y={h - 8} textAnchor="middle" fontFamily={T.font} fontSize="10" fontWeight="600" fill={T.c400}>{d.m}</text>
      </g>)}
    </svg>);

}

// horizontal stacked composition bar + legend
function StackBar({ segments, total, unit = 'tỷ' }) {
  return (
    <div>
      <div style={{ display: 'flex', height: 14, borderRadius: 7, overflow: 'hidden', background: T.c150 }}>
        {segments.map((s) => <div key={s.label} style={{ width: s.value / total * 100 + '%', background: s.color }} title={s.label} />)}
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 0, marginTop: 14 }}>
        {segments.map((s) =>
        <div key={s.label} style={{ display: 'flex', alignItems: 'center', gap: 9, padding: '8px 0', borderBottom: '1px solid ' + T.c150 }}>
            <span style={{ width: 9, height: 9, borderRadius: 3, background: s.color, flex: 'none' }} />
            <span style={{ fontFamily: T.font, fontWeight: 600, fontSize: 13, color: T.c700 }}>{s.label}</span>
            <span style={{ marginLeft: 'auto', fontFamily: T.font, fontWeight: 500, fontSize: 11.5, color: T.c400 }}>{(s.value / total * 100).toFixed(1)}%</span>
            <span className="num" style={{ width: 96, textAlign: 'right', fontFamily: T.font, fontWeight: 700, fontSize: 13.5, color: T.c900 }}>{s.value.toLocaleString()}</span>
          </div>
        )}
        <div style={{ display: 'flex', alignItems: 'center', padding: '10px 0 0' }}>
          <span style={{ fontFamily: T.font, fontWeight: 700, fontSize: 13, color: T.c900 }}>Total Value-at-Risk</span>
          <span className="num" style={{ marginLeft: 'auto', fontFamily: T.font, fontWeight: 700, fontSize: 15, color: T.c900 }}>{total.toLocaleString()} <span style={{ fontWeight: 600, fontSize: 11.5, color: T.c400 }}>{unit}</span></span>
        </div>
      </div>
    </div>);

}

// utilization meter (current vs limit) with threshold ticks
function MeterBar({ pct, ticks = [], color }) {
  const c = color || (pct >= 100 ? T.alert : pct >= 80 ? T.warn : T.ok);
  return (
    <div style={{ position: 'relative', height: 8, background: T.c150, borderRadius: 6, fontSize: "13px" }}>
      <div style={{ ...{ width: Math.min(100, pct) + '%', height: '100%', background: c, borderRadius: 6 }, width: "48px", background: "rgb(10, 79, 163)" }} />
      {ticks.map((t) => <span key={t} style={{ position: 'absolute', left: t + '%', top: -2, width: 2, height: 12, background: T.c400, borderRadius: 2 }} />)}
    </div>);

}
// Multi-series trend — mode 'line' (overlaid) or 'stack' (stacked area). data: {days:[], series:{name:[]}, colors:{name}}
function MultiTrend({ days, series, colors, unit = '억', h = 240, mode = 'line', yTickN = 4, zero = true, marker = null, markers = [] }) {
  const w = 720,padL = 46,padR = 16,padT = 14,padB = 26;
  const iw = w - padL - padR,ih = h - padT - padB;
  const names = Object.keys(series),N = days.length;
  let max = 0, min = 0;
  if (mode === 'stack') {for (let i = 0; i < N; i++) {let s = 0;names.forEach((n) => s += series[n][i]);if (s > max) max = s;}max = max * 1.08;} else {
    names.forEach((n) => series[n].forEach((v) => {if (v > max) max = v;}));
    if (zero) {max = max * 1.08;} else {
      min = Infinity;names.forEach((n) => series[n].forEach((v) => {if (v < min) min = v;}));
      const pad = (max - min) * 0.15 || max * 0.02;min = Math.max(0, min - pad);max = max + pad;
    }
  }
  const span = max - min || 1;
  const x = (i) => padL + i / (N - 1) * iw,y = (v) => padT + ih - (v - min) / span * ih;
  const ticks = Array.from({ length: yTickN + 1 }, (_, i) => Math.round(min + span * i / yTickN));
  const fmt = (v) => v >= 10000 ? (v / 10000).toFixed(1) + '만' : v.toLocaleString();
  const cum = names.map(() => new Array(N).fill(0));
  if (mode === 'stack') {for (let i = 0; i < N; i++) {let acc = 0;names.forEach((n, ni) => {acc += series[n][i];cum[ni][i] = acc;});}}
  // helpers: interpolate value at frac position on first series
  const valAt = (frac) => { const nm = names[0]; const pos = frac * (N - 1); const i0 = Math.floor(pos), i1 = Math.min(N - 1, i0 + 1), f = pos - i0; return series[nm][i0] * (1 - f) + series[nm][i1] * f; };
  return (
    <svg width="100%" viewBox={`0 0 ${w} ${h}`} style={{ display: 'block' }}>
      <defs>{names.map((n) => <linearGradient key={n} id={'g_' + n} x1="0" y1="0" x2="0" y2="1"><stop offset="0" stopColor={colors[n]} stopOpacity=".18" /><stop offset="1" stopColor={colors[n]} stopOpacity="0" /></linearGradient>)}</defs>
      {ticks.map((t, i) => <g key={i}><line x1={padL} y1={y(t)} x2={w - padR} y2={y(t)} stroke={T.c150} /><text x={padL - 8} y={y(t) + 3.5} textAnchor="end" fontFamily={T.font} fontSize="10" fontWeight="600" fill={T.c400} className="num">{fmt(t)}</text></g>)}
      {mode === 'stack' ?
      names.slice().reverse().map((n) => {const ni = names.indexOf(n);
        const top = cum[ni].map((v, i) => `${x(i)},${y(v)}`).join(' ');
        const bot = (ni > 0 ? cum[ni - 1] : new Array(N).fill(0)).map((v, i) => `${x(i)},${y(v)}`).reverse().join(' ');
        return <polygon key={n} points={`${top} ${bot}`} fill={colors[n]} fillOpacity=".82" stroke="#fff" strokeWidth=".6" />;}) :
      names.map((n) => {const pts = series[n].map((v, i) => `${x(i)},${y(v)}`).join(' ');
        return <g key={n}><polygon points={`${padL},${padT + ih} ${pts} ${padL + iw},${padT + ih}`} fill={`url(#g_${n})`} /><polyline points={pts} fill="none" stroke={colors[n]} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" /><circle cx={x(N - 1)} cy={y(series[n][N - 1])} r="3.4" fill="#fff" stroke={colors[n]} strokeWidth="2.2" /></g>;})}
      {/* vline markers (gray remark lines) — drawn before dots so dots appear on top */}
      {markers.filter((m) => m.type === 'vline').map((m, idx) => {const mx = padL + m.frac * iw; return <g key={'vl' + idx}><line x1={mx} y1={padT} x2={mx} y2={padT + ih} stroke={m.color} strokeWidth="1" strokeDasharray="3 3" opacity=".7" />{m.label && <text x={Math.min(Math.max(mx, padL + 22), w - padR - 22)} y={padT + 19} textAnchor="middle" fontFamily={T.font} fontSize="8.5" fontWeight="700" fill={m.color} opacity=".85">{m.label}</text>}</g>;})}
      {/* legacy single marker (danger date: dashed line + dot + top label) */}
      {marker && (() => {const mx = padL + marker.frac * iw;return <g><line x1={mx} y1={padT} x2={mx} y2={padT + ih} stroke={marker.color} strokeWidth="1.3" strokeDasharray="4 3" /><circle cx={mx} cy={y(valAt(marker.frac))} r="5" fill={marker.color} stroke="#fff" strokeWidth="2" /><text x={Math.min(Math.max(mx, padL + 34), w - padR - 34)} y={padT + 9} textAnchor="middle" fontFamily={T.font} fontSize="9.5" fontWeight="700" fill={marker.color}>{marker.label}</text></g>;})()}
      {/* dot markers (sell signal red dots) — drawn last so always on top */}
      {markers.filter((m) => m.type === 'dot').map((m, idx) => {const mx = padL + m.frac * iw; return <g key={'dt' + idx}><circle cx={mx} cy={y(valAt(m.frac))} r="4.5" fill={m.color} stroke="#fff" strokeWidth="2" /></g>;})}
      {days.map((d, i) => i % 5 === 0 || i === N - 1 ? <text key={i} x={x(i)} y={h - 8} textAnchor="middle" fontFamily={T.font} fontSize="9.5" fontWeight="600" fill={T.c400}>{d}</text> : null)}
    </svg>);

}

// Donut composition. data:[{label,value,color}], center label/value
function Donut({ data, size = 170, thick = 26, centerTop, centerBig, unit }) {
  const total = data.reduce((s, d) => s + d.value, 0);
  const r = (size - thick) / 2,cx = size / 2,cy = size / 2,C = 2 * Math.PI * r;
  let off = 0;
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 18, justifyContent: "flex-start" }}>
      <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} style={{ flex: 'none' }}>
        <g transform={`rotate(-90 ${cx} ${cy})`}>
          <circle cx={cx} cy={cy} r={r} fill="none" stroke={T.c150} strokeWidth={thick} />
          {data.map((d, i) => {const frac = d.value / total;const dash = frac * C;const el = <circle key={i} cx={cx} cy={cy} r={r} fill="none" stroke={d.color} strokeWidth={thick} strokeDasharray={`${dash} ${C - dash}`} strokeDashoffset={-off} />;off += dash;return el;})}
        </g>
        {centerTop && <text x={cx} y={cy - 6} textAnchor="middle" fontFamily={T.font} fontSize="10.5" fontWeight="600" fill={T.c500}>{centerTop}</text>}
        {centerBig && <text x={cx} y={cy + (centerTop ? 14 : 5)} textAnchor="middle" fontFamily={T.font} fontWeight="700" fontSize="20" fill={T.c900} className="num">{centerBig}<tspan fontSize="11" fill={T.c400} dx="1">{unit}</tspan></text>}
      </svg>
      <div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 0 }}>
        {data.map((d, i) =>
        <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '5.5px 0', borderBottom: i < data.length - 1 ? '1px solid ' + T.c150 : 'none' }}>
            <span style={{ width: 9, height: 9, borderRadius: 3, background: d.color, flex: 'none' }} />
            <span style={{ fontFamily: T.font, fontWeight: 600, fontSize: 12.5, color: T.c700, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{d.label}</span>
            <span style={{ marginLeft: 'auto', fontFamily: T.font, fontWeight: 700, fontSize: 12.5, color: T.c900 }} className="num">{(d.value / total * 100).toFixed(1)}%</span>
          </div>
        )}
      </div>
    </div>);

}

// Treemap-style heat cells. data:[{label,value,intensity 0..1}]
function HeatMap({ data, h = 180, max }) {
  const total = data.reduce((s, d) => s + d.value, 0);
  const mx = max || Math.max(...data.map((d) => d.intensity));
  const heat = (t) => {const f = Math.min(1, t / mx); // light->deep red
    const r = Math.round(248 - (248 - 176) * f),g = Math.round(232 - (232 - 30) * f),b = Math.round(232 - (232 - 36) * f);return `rgb(${r},${g},${b})`;};
  // simple row layout: widths balance value (sqrt-compressed) with a readable floor
  return (
    <div style={{ display: 'flex', gap: 5, height: h }}>
      {data.map((d, i) => {const f = Math.min(1, d.intensity / mx);const dark = f > 0.45;
        return (
          <div key={i} style={{ flexGrow: d.value, flexBasis: 0, minWidth: 72, background: heat(d.intensity), borderRadius: 8, padding: '12px 13px', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', border: '1px solid rgba(0,0,0,.04)', overflow: 'hidden' }}>
          <div style={{ fontFamily: T.font, fontWeight: 700, fontSize: 12.5, color: dark ? '#fff' : T.c800, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{d.label}</div>
          <div>
            <div className="num" style={{ fontFamily: T.font, fontWeight: 700, fontSize: 17, color: dark ? '#fff' : T.c900, letterSpacing: '-.01em' }}>{d.value.toLocaleString()}<span style={{ fontSize: 10.5, fontWeight: 600, opacity: .7 }}> 억</span></div>
            <div style={{ fontFamily: T.font, fontWeight: 600, fontSize: 10.5, color: dark ? 'rgba(255,255,255,.82)' : T.c500, marginTop: 2, whiteSpace: 'nowrap' }}>강도 {(d.intensity * 100).toFixed(2)}%</div>
          </div>
        </div>);
      })}
    </div>);

}

// Ranked horizontal bars. data:[{label,value,color,sub}]
function HBars({ data, unit = '억', h }) {
  const max = Math.max(...data.map((d) => d.value));
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 11 }}>
      {data.map((d, i) =>
      <div key={i}>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginBottom: 5 }}>
            <span style={{ fontFamily: T.font, fontWeight: 600, fontSize: 12.5, color: T.c700 }}>{d.label}</span>
            {d.sub && <span style={{ fontFamily: T.font, fontWeight: 500, fontSize: 11, color: T.c400 }}>{d.sub}</span>}
            <span className="num" style={{ marginLeft: 'auto', fontFamily: T.font, fontWeight: 700, fontSize: 13, color: T.c900 }}>{d.value.toLocaleString()} <span style={{ fontWeight: 600, fontSize: 10.5, color: T.c400 }}>{unit}</span></span>
          </div>
          <div style={{ height: 9, background: T.c150, borderRadius: 5, overflow: 'hidden' }}>
            <div style={{ width: d.value / max * 100 + '%', height: '100%', background: d.color || T.blue, borderRadius: 5 }} />
          </div>
        </div>
      )}
    </div>);

}

// tiny inline sparkline
function Spark({ data, color = T.blue, w = 84, h = 26, fillArea = true, full = false }) {
  const min = Math.min(...data),max = Math.max(...data),rng = max - min || 1;
  const x = (i) => i / (data.length - 1) * w,y = (v) => h - 2 - (v - min) / rng * (h - 4);
  const pts = data.map((v, i) => `${x(i)},${y(v)}`).join(' ');
  return (
    <svg width={full ? '100%' : w} height={h} viewBox={`0 0 ${w} ${h}`} preserveAspectRatio={full ? 'none' : 'xMidYMid meet'} style={{ display: 'block' }}>
      {fillArea && <polygon points={`0,${h} ${pts} ${w},${h}`} fill={color} fillOpacity=".10" />}
      <polyline points={pts} fill="none" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" vectorEffect={full ? 'non-scaling-stroke' : undefined} />
      <circle cx={x(data.length - 1)} cy={y(data[data.length - 1])} r="2.4" fill={color} />
    </svg>);

}

Object.assign(window, { Gauge, TrendChart, StackBar, MeterBar, MultiTrend, Donut, HeatMap, HBars, Spark });
