// shared.jsx — Reusable atoms: SignalBadge, Score, Sparkline, Chart, Ticker, KbdHint
// Bloomberg-style data terminal components.

// ─────────────────────────────────────────────────────────
// Grade pill (S / A / B / C)
// ─────────────────────────────────────────────────────────
function GradePill({ grade, size = 'md' }) {
  const c = grade === 'S' ? T.gradeS : grade === 'A' ? T.gradeA : grade === 'B' ? T.gradeB : T.gradeC;
  const sizes = {
    sm: { w: 18, h: 18, fs: 10 },
    md: { w: 24, h: 24, fs: 12 },
    lg: { w: 36, h: 36, fs: 16 },
    xl: { w: 56, h: 56, fs: 24 },
  }[size] || { w: 24, h: 24, fs: 12 };
  return (
    <div style={{
      width: sizes.w, height: sizes.h, borderRadius: 4,
      background: c, color: T.bg,
      fontFamily: T.mono, fontWeight: 700, fontSize: sizes.fs,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      flexShrink: 0, letterSpacing: 0,
    }}>{grade}</div>
  );
}

// ─────────────────────────────────────────────────────────
// Score gauge (0–100), horizontal bar with grade markers
// ─────────────────────────────────────────────────────────
function ScoreBar({ score, height = 8, showTicks = true }) {
  const pos = Math.max(0, Math.min(100, score));
  const c = score >= 80 ? T.gradeS : score >= 65 ? T.gradeA : score >= 50 ? T.gradeB : T.gradeC;
  return (
    <div style={{ width: '100%' }}>
      <div style={{
        position: 'relative', height, borderRadius: height / 2,
        background: T.cardLo, overflow: 'hidden',
        boxShadow: 'inset 0 0 0 1px ' + T.line,
      }}>
        <div style={{
          position: 'absolute', left: 0, top: 0, bottom: 0,
          width: pos + '%', background: c,
          boxShadow: '0 0 12px ' + c + '88',
          transition: 'width .3s',
        }} />
      </div>
      {showTicks && (
        <div style={{
          display: 'flex', justifyContent: 'space-between',
          fontFamily: T.mono, fontSize: 9, color: T.fgFaint, marginTop: 4,
        }}>
          <span>C ·50</span><span>B ·65</span><span>A ·80</span><span>S ·100</span>
        </div>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────
// Circular score (used in cards / detail headers)
// ─────────────────────────────────────────────────────────
function ScoreRing({ score, size = 64, stroke = 6, label }) {
  const r = (size - stroke) / 2;
  const C = 2 * Math.PI * r;
  const off = C - (Math.max(0, Math.min(100, score)) / 100) * C;
  const c = score >= 80 ? T.gradeS : score >= 65 ? T.gradeA : score >= 50 ? T.gradeB : T.gradeC;
  const grade = score >= 80 ? 'S' : score >= 65 ? 'A' : score >= 50 ? 'B' : 'C';
  return (
    <div style={{ position: 'relative', width: size, height: size, flexShrink: 0 }}>
      <svg width={size} height={size} style={{ transform: 'rotate(-90deg)' }}>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={T.line} strokeWidth={stroke} />
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={c} strokeWidth={stroke}
          strokeDasharray={C} strokeDashoffset={off} strokeLinecap="round" style={{ filter: `drop-shadow(0 0 4px ${c}88)` }} />
      </svg>
      <div style={{
        position: 'absolute', inset: 0, display: 'flex',
        flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
        fontFamily: T.mono,
      }}>
        <div style={{ fontSize: size * 0.32, fontWeight: 700, color: c, lineHeight: 1 }}>{score}</div>
        {label && <div style={{ fontSize: size * 0.14, color: T.fgMuted, marginTop: 2 }}>{label}</div>}
        {!label && <div style={{ fontSize: size * 0.16, color: T.fgMuted, marginTop: 2 }}>{grade}-tier</div>}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────
// Sparkline — simple SVG poly
// ─────────────────────────────────────────────────────────
function Spark({ data, w = 80, h = 26, color, fill = true }) {
  if (!data || data.length < 2) return <svg width={w} height={h} />;
  const min = Math.min(...data), max = Math.max(...data);
  const range = max - min || 1;
  const c = color || (data[data.length - 1] >= data[0] ? T.up : T.down);
  const pts = data.map((v, i) => {
    const x = (i / (data.length - 1)) * (w - 2) + 1;
    const y = h - 2 - ((v - min) / range) * (h - 4);
    return [x, y];
  });
  const path = 'M ' + pts.map(p => p.join(' ')).join(' L ');
  const area = path + ` L ${pts[pts.length-1][0]} ${h} L ${pts[0][0]} ${h} Z`;
  return (
    <svg width={w} height={h} style={{ display: 'block' }}>
      {fill && <path d={area} fill={c} fillOpacity="0.12" />}
      <path d={path} stroke={c} strokeWidth="1.4" fill="none" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

// ─────────────────────────────────────────────────────────
// Direction arrow + delta percent
// ─────────────────────────────────────────────────────────
function Delta({ value, suffix = '%', size = 12 }) {
  const up = value > 0, down = value < 0;
  const c = up ? T.up : down ? T.down : T.fgMuted;
  const arrow = up ? '▲' : down ? '▼' : '–';
  return (
    <span style={{ color: c, fontFamily: T.mono, fontSize: size, fontWeight: 600, whiteSpace: 'nowrap' }}>
      {arrow}&nbsp;{Math.abs(value).toFixed(2)}{suffix}
    </span>
  );
}

// ─────────────────────────────────────────────────────────
// Signal badge (BUY / WATCH / HOLD)
// ─────────────────────────────────────────────────────────
function SignalBadge({ kind = 'BUY', size = 'md' }) {
  const map = {
    BUY:    { bg: T.upBg, fg: T.up,   label: '강력매수' },
    ACC:    { bg: T.upBg, fg: T.up,   label: '매수' },
    WATCH:  { bg: T.hotBg, fg: T.hot, label: '관망' },
    HOLD:   { bg: T.infoBg, fg: T.info, label: '보유' },
    AVOID:  { bg: T.downBg, fg: T.down, label: '회피' },
  };
  const m = map[kind] || map.BUY;
  const pad = size === 'sm' ? '2px 7px' : '4px 10px';
  const fs = size === 'sm' ? 10 : 11;
  return (
    <span style={{
      padding: pad, borderRadius: 3,
      background: m.bg, color: m.fg,
      fontFamily: T.mono, fontSize: fs, fontWeight: 700,
      letterSpacing: 0.4, textTransform: 'none',
      border: '1px solid ' + m.fg + '40',
      whiteSpace: 'nowrap',
    }}>{m.label}</span>
  );
}

// ─────────────────────────────────────────────────────────
// Panel — bordered card with optional caption header
// ─────────────────────────────────────────────────────────
function Panel({ title, subtitle, action, children, style = {}, pad = 16, noPad = false }) {
  return (
    <div style={{
      background: T.card, border: '1px solid ' + T.line,
      borderRadius: 4, display: 'flex', flexDirection: 'column',
      ...style,
    }}>
      {title && (
        <div style={{
          padding: '10px 14px', borderBottom: '1px solid ' + T.line,
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          flexShrink: 0,
        }}>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, minWidth: 0 }}>
            <div style={{
              fontSize: 11, fontWeight: 700, color: T.fgDim,
              letterSpacing: 1.2, textTransform: 'uppercase',
              fontFamily: T.mono, whiteSpace: 'nowrap',
            }}>{title}</div>
            {subtitle && <div style={{ fontSize: 11, color: T.fgMuted, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{subtitle}</div>}
          </div>
          {action}
        </div>
      )}
      <div style={{ padding: noPad ? 0 : pad, flex: 1, minHeight: 0 }}>{children}</div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────
// KPI block: label + big number + delta
// ─────────────────────────────────────────────────────────
function KPI({ label, value, unit, deltaValue, sub, big = false }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 4, minWidth: 0 }}>
      <div style={{ fontSize: 10, color: T.fgMuted, fontFamily: T.mono, letterSpacing: 1, textTransform: 'uppercase' }}>{label}</div>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>
        <span className="uj-num" style={{ fontSize: big ? 28 : 20, fontWeight: 600, color: T.fg, lineHeight: 1 }}>{value}</span>
        {unit && <span style={{ fontSize: 11, color: T.fgMuted }}>{unit}</span>}
      </div>
      {deltaValue != null && <Delta value={deltaValue} />}
      {sub && <div style={{ fontSize: 10, color: T.fgFaint, fontFamily: T.mono }}>{sub}</div>}
    </div>
  );
}

// ─────────────────────────────────────────────────────────
// Live dot — blinking green/yellow indicator
// ─────────────────────────────────────────────────────────
function LiveDot({ color = T.up, label = 'LIVE' }) {
  return (
    <div style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontFamily: T.mono, fontSize: 10, color: T.fgDim, letterSpacing: 1 }}>
      <span className="uj-blink" style={{ width: 6, height: 6, borderRadius: 3, background: color, boxShadow: '0 0 6px ' + color }} />
      {label}
    </div>
  );
}

// ─────────────────────────────────────────────────────────
// Line chart with optional forecast (dashed) and confidence band
// data: [{t:'2020-01', v: 110000}, ...]
// ─────────────────────────────────────────────────────────
function LineChart({
  history = [],
  forecast = [],
  band, // [[lo, hi], ...] aligned w/ forecast
  width = 600, height = 220,
  yLabel = '만원',
  highlightLast = true,
}) {
  const allV = [
    ...history.map(d => d.v),
    ...forecast.map(d => d.v),
    ...(band ? band.flat() : []),
  ];
  const min = Math.min(...allV);
  const max = Math.max(...allV);
  const padY = (max - min) * 0.08 || 1;
  const yMin = min - padY, yMax = max + padY;
  const total = history.length + forecast.length - (history.length && forecast.length ? 0 : 0);
  const pad = { l: 50, r: 16, t: 14, b: 28 };
  const innerW = width - pad.l - pad.r;
  const innerH = height - pad.t - pad.b;
  const xAt = (i) => pad.l + (i / (total - 1)) * innerW;
  const yAt = (v) => pad.t + (1 - (v - yMin) / (yMax - yMin)) * innerH;

  const histPath = 'M ' + history.map((d, i) => xAt(i) + ' ' + yAt(d.v)).join(' L ');
  // forecast starts at last historical point
  const startI = history.length - 1;
  const fcPath = forecast.length
    ? 'M ' + xAt(startI) + ' ' + yAt(history[history.length - 1].v) + ' '
      + forecast.map((d, i) => 'L ' + xAt(startI + 1 + i) + ' ' + yAt(d.v)).join(' ')
    : '';
  // band area
  let bandPath = '';
  if (band && band.length === forecast.length) {
    const upper = band.map((b, i) => xAt(startI + 1 + i) + ' ' + yAt(b[1]));
    const lower = band.map((b, i) => xAt(startI + 1 + i) + ' ' + yAt(b[0])).reverse();
    bandPath = 'M ' + xAt(startI) + ' ' + yAt(history[history.length-1].v) + ' L ' +
      upper.join(' L ') + ' L ' + lower.join(' L ') + ' Z';
  }

  // Y gridlines
  const yTicks = 4;
  const ticks = Array.from({ length: yTicks + 1 }, (_, i) => yMin + (i / yTicks) * (yMax - yMin));

  // X labels: first, every Nth, last
  const xLabels = [];
  const labelEvery = Math.max(1, Math.floor(total / 6));
  for (let i = 0; i < history.length; i += labelEvery) xLabels.push({ i, t: history[i].t });
  if (forecast.length) xLabels.push({ i: history.length + forecast.length - 1, t: forecast[forecast.length-1].t });

  return (
    <svg width={width} height={height} style={{ display: 'block' }}>
      {/* gridlines */}
      {ticks.map((v, i) => (
        <g key={i}>
          <line x1={pad.l} x2={width - pad.r} y1={yAt(v)} y2={yAt(v)} stroke={T.line} strokeWidth="1" strokeDasharray="2 3" />
          <text x={pad.l - 8} y={yAt(v) + 3} fontSize="9" fill={T.fgFaint} textAnchor="end" fontFamily={T.mono}>
            {(v / 10000).toFixed(1) + '억'}
          </text>
        </g>
      ))}
      {/* vertical separator at forecast start */}
      {forecast.length > 0 && (
        <line x1={xAt(startI)} x2={xAt(startI)} y1={pad.t} y2={height - pad.b}
          stroke={T.hot} strokeWidth="1" strokeDasharray="3 3" opacity="0.5" />
      )}
      {forecast.length > 0 && (
        <text x={xAt(startI) + 4} y={pad.t + 9} fontSize="9" fill={T.hot} fontFamily={T.mono}>NOW</text>
      )}
      {/* confidence band */}
      {bandPath && <path d={bandPath} fill={T.ai} fillOpacity="0.18" />}
      {/* historical */}
      <path d={histPath} stroke={T.fg} strokeWidth="1.6" fill="none" strokeLinecap="round" strokeLinejoin="round" />
      {/* forecast dashed */}
      {fcPath && <path d={fcPath} stroke={T.ai} strokeWidth="1.6" fill="none" strokeDasharray="4 4" strokeLinecap="round" />}
      {/* highlight last point */}
      {highlightLast && history.length > 0 && (
        <circle cx={xAt(history.length - 1)} cy={yAt(history[history.length - 1].v)} r="3" fill={T.hot} stroke={T.bg} strokeWidth="1.5" />
      )}
      {/* X labels */}
      {xLabels.map((l, i) => (
        <text key={i} x={xAt(l.i)} y={height - 10} fontSize="9" fill={T.fgFaint} textAnchor="middle" fontFamily={T.mono}>{l.t}</text>
      ))}
    </svg>
  );
}

// ─────────────────────────────────────────────────────────
// Bar chart (horizontal) for factor weights
// ─────────────────────────────────────────────────────────
function FactorBar({ label, value, max = 100, color, sub, impact = 'positive' }) {
  const c = color || (impact === 'positive' ? T.up : impact === 'negative' ? T.down : T.info);
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 4, padding: '8px 0' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 }}>
          <span style={{ fontSize: 12, color: T.fg, fontWeight: 500 }}>{label}</span>
          {sub && <span style={{ fontSize: 10, color: T.fgMuted, fontFamily: T.mono }}>· {sub}</span>}
        </div>
        <span className="uj-num" style={{ color: c, fontSize: 12, fontWeight: 600 }}>
          {impact === 'positive' ? '+' : impact === 'negative' ? '−' : ''}{Math.abs(value).toFixed(1)}
        </span>
      </div>
      <div style={{ height: 4, background: T.cardLo, borderRadius: 2, overflow: 'hidden' }}>
        <div style={{ height: '100%', width: Math.min(100, Math.abs(value) / max * 100) + '%', background: c, boxShadow: '0 0 8px ' + c + '60' }} />
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────
// Ticker scrolling marquee
// ─────────────────────────────────────────────────────────
function Ticker({ items }) {
  return (
    <div style={{
      overflow: 'hidden', whiteSpace: 'nowrap',
      borderTop: '1px solid ' + T.line, borderBottom: '1px solid ' + T.line,
      background: T.bgAlt, height: 28,
      position: 'relative',
    }}>
      <div style={{
        display: 'inline-block', whiteSpace: 'nowrap', paddingLeft: '100%',
        animation: 'ujticker 90s linear infinite',
      }}>
        {items.concat(items).map((it, i) => (
          <span key={i} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, padding: '0 22px', height: 28, fontFamily: T.mono, fontSize: 11 }}>
            <span style={{ color: T.fgDim, fontWeight: 600 }}>{it.code}</span>
            <span style={{ color: T.fg }}>{it.label}</span>
            <span style={{ color: T.fgFaint }}>{won(it.price)}</span>
            <Delta value={it.delta} size={10} />
          </span>
        ))}
      </div>
      <style>{`@keyframes ujticker{0%{transform:translateX(0)}100%{transform:translateX(-50%)}}`}</style>
    </div>
  );
}

Object.assign(window, {
  GradePill, ScoreBar, ScoreRing, Spark, Delta,
  SignalBadge, Panel, KPI, LiveDot, LineChart, FactorBar, Ticker,
});
