// PortfolioTools.jsx — 포트폴리오 공용 헬퍼 + 도구 패널(신규추가/AI최적화/시뮬레이션).

const PAL = ['#0A4FA3', '#00A0E0', '#E0922A', '#6D5BD0', '#0E7C5A', '#C76A1F', '#1F8A8A', '#3E63B8', '#8A6D3B', '#B0476B', '#4C7BD9', '#D4A23A'];
function fmtEok(v) {const n = Math.round(v);return n >= 10000 ? (n / 10000).toFixed(2) + '조' : n.toLocaleString() + '억';}
function holdColor(h, i) {return PAL[i % PAL.length];}

// 종목별 위험기여 비율 (합 = 1)
function riskContribs(holds) {
  const arr = holds.map((h) => {const a = UMAP[h.id];let c = 0;
    for (const g of holds) {const b = UMAP[g.id];c += g.w / 100 * corr(a, b) * b.vol;}
    return { id: h.id, c: h.w / 100 * a.vol * c };});
  const sum = arr.reduce((s, x) => s + x.c, 0) || 1;
  return arr.map((x) => ({ id: x.id, frac: x.c / sum }));
}

// 한도 소진율 미터 (80/100 눈금)
function LimitMeter({ pct, h = 10 }) {
  const c = pct >= 100 ? T.alert : pct >= 80 ? T.warn : T.ok;
  return (
    <div style={{ position: 'relative', height: h, background: T.c150, borderRadius: 6 }}>
      <div style={{ width: Math.min(100, pct) + '%', height: '100%', background: c, borderRadius: 6, transition: 'width .5s cubic-bezier(.4,0,.2,1)' }} />
      {[80, 100].map((t) => <span key={t} style={{ position: 'absolute', left: Math.min(100, t) + '%', top: -2, width: 2, height: h + 4, background: t === 100 ? T.alert : T.c400, borderRadius: 2, transform: 'translateX(-1px)' }} />)}
    </div>);
}
function MiniBar({ pct, color, w = 64 }) {
  return <div style={{ width: w, height: 6, background: T.c150, borderRadius: 4, overflow: 'hidden', display: 'inline-block', verticalAlign: 'middle' }}>
    <div style={{ width: Math.min(100, pct) + '%', height: '100%', background: color, borderRadius: 4 }} /></div>;
}

// 변경 전/후 비교 행. betterLow=true면 감소가 개선(초록). neutral이면 좋고나쁜 색 없이 표시.
function CompareRow({ label, before, after, unit, digits = 0, betterLow, neutral }) {
  const diff = after - before;
  const improved = betterLow ? diff < 0 : diff > 0;
  const flat = Math.abs(diff) < (digits > 0 ? 0.005 : 0.5);
  const col = neutral ? T.c600 : flat ? T.c400 : improved ? T.ok : T.alert;
  const fmt = (v) => digits > 0 ? v.toFixed(digits) : Math.round(v).toLocaleString();
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '10px 0', borderBottom: '1px solid ' + T.c150 }}>
      <span style={{ fontFamily: T.font, fontWeight: 600, fontSize: 12.5, color: T.c600, flex: 1, textAlign: 'left', whiteSpace: 'nowrap' }}>{label}</span>
      <span className="num" style={{ fontFamily: T.font, fontWeight: 600, fontSize: 12.5, color: T.c500, minWidth: 46, textAlign: 'right' }}>{fmt(before)}{unit}</span>
      <Icon name="arrowRight" size={13} color={T.c300} style={{ flex: 'none' }} />
      <span className="num" style={{ fontFamily: T.font, fontWeight: 800, fontSize: 14, color: T.c900, minWidth: 56, textAlign: 'right' }}>{fmt(after)}{unit}</span>
      <span className="num" style={{ minWidth: 54, textAlign: 'right', fontFamily: T.font, fontWeight: 700, fontSize: 12, color: col }}>{flat ? '—' : (diff > 0 ? '+' : '') + fmt(diff) + unit}</span>
    </div>);
}

// 비중 변경 후 dilute & normalize 하여 신규 종목 추가
function withAdded(holds, id, w) {
  const scaled = holds.map((h) => ({ id: h.id, w: h.w * (100 - w) / 100 }));
  const existing = scaled.find((h) => h.id === id);
  if (existing) {existing.w += w;return existing && scaled;}
  return [...scaled, { id, w }];
}

// ============ 종목 매수·매도 패널 (유니버스 ↔ 내 포트폴리오) ============
function AddStockPanel({ pf, onCommit, asset }) {
  const held = new Set(pf.holds.map((h) => h.id));
  const [q, setQ] = React.useState('');
  const match = (c) => !q || (c.name + c.id + (c.sector || '') + (c.country || '')).toLowerCase().includes(q.toLowerCase());
  const universe = UNIV.filter((u) => !held.has(u.id) && (!asset || u.type === asset)).map((u) => ({ id: u.id, name: u.name, sector: u.sector, country: u.country, type: u.type })).filter(match);
  const portfolio = pf.holds.map((h) => ({ id: h.id, name: UMAP[h.id].name, sector: UMAP[h.id].sector, country: UMAP[h.id].country, type: UMAP[h.id].type, _w: h.w, _amt: h.w / 100 * pf.value })).filter(match);

  const [sel, setSel] = React.useState({ id: universe[0] && universe[0].id, side: 'add' });
  const [amt, setAmt] = React.useState(300);
  const action = sel.side;

  // 선택이 유효하지 않으면 목록 첫 항목으로 되돌림
  React.useEffect(() => {
    if (action === 'add' && !universe.some((u) => u.id === sel.id)) setSel({ id: universe[0] && universe[0].id, side: 'add' });
    if (action === 'remove' && !held.has(sel.id)) setSel({ id: portfolio[0] && portfolio[0].id, side: 'remove' });
  }, [pf.holds.length]);

  // 선택 종목이 바뀌면 금액 기본값 조정 (매도는 보유 전량, 매수는 300억)
  React.useEffect(() => {
    if (sel.side === 'remove') {const h = pf.holds.find((x) => x.id === sel.id);setAmt(h ? Math.round(h.w / 100 * pf.value) : 0);}
    else setAmt(300);
  }, [sel.id, sel.side]);

  const pick = (id, side) => setSel({ id, side });
  const selHold = pf.holds.find((h) => h.id === sel.id);
  const selAmt = selHold ? selHold.w / 100 * pf.value : 0;
  const base = summarize(pf);
  const u = UMAP[sel.id];

  let nextHolds, nextValue, valid;
  if (action === 'add') {
    const a = Math.max(0, amt);
    nextValue = pf.value + a;
    nextHolds = normalize([...pf.holds.map((h) => ({ id: h.id, w: h.w * pf.value / nextValue })), { id: sel.id, w: a / nextValue * 100 }]);
    valid = a > 0 && !!sel.id;
  } else {
    const rem = Math.min(Math.max(0, amt), selAmt);
    nextValue = Math.max(1, pf.value - rem);
    const abs = pf.holds.map((h) => ({ id: h.id, w: h.id === sel.id ? h.w / 100 * pf.value - rem : h.w / 100 * pf.value }));
    const tot = abs.reduce((s, x) => s + Math.max(0, x.w), 0) || 1;
    nextHolds = abs.filter((x) => x.w > 0.01).map((x) => ({ id: x.id, w: +(x.w / tot * 100).toFixed(2) }));
    valid = rem > 0 && nextHolds.length >= 1 && !!sel.id;
  }
  const next = summarize({ ...pf, holds: nextHolds, value: nextValue });
  // 요인 기여 변화 (before→after)
  const decB = {}; factorDecompose(pf).forEach((d) => decB[d.id] = d);
  const decA = {}; factorDecompose({ ...pf, holds: nextHolds, value: nextValue }).forEach((d) => decA[d.id] = d);
  const chips = action === 'add' ? [100, 300, 500, 1000] : [['¼', selAmt / 4], ['½', selAmt / 2], ['전량', selAmt]];

  // 종목 목록 한 칸
  const Column = ({ title, tag, tagColor, items, empty, side, meta }) => (
    <div style={{ display: 'flex', flexDirection: 'column', minWidth: 0 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 8 }}>
        <span style={{ width: 6, height: 6, borderRadius: 6, background: tagColor, flex: 'none' }} />
        <span style={{ fontFamily: T.font, fontWeight: 700, fontSize: 12, color: T.c700 }}>{title}</span>
        <span style={{ marginLeft: 'auto', fontFamily: T.font, fontWeight: 600, fontSize: 10.5, color: T.c400 }}>{tag}</span>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 4, height: 246, overflowY: 'auto', border: '1px solid ' + T.line, borderRadius: 10, padding: 6, background: T.white }}>
        {items.length === 0 && <div style={{ padding: '42px 10px', textAlign: 'center', fontFamily: T.font, fontWeight: 500, fontSize: 12, color: T.c400 }}>{empty}</div>}
        {items.map((c) => {const on = c.id === sel.id && sel.side === side;const acc = side === 'remove' ? { l: T.alertL, s: T.alertS } : { l: T.blueLine, s: T.blueSurface };
          return <button key={c.id} onClick={() => pick(c.id, side)} style={{ display: 'flex', alignItems: 'center', gap: 9, padding: '8px 10px', borderRadius: 8, cursor: 'pointer',
            border: '1px solid ' + (on ? acc.l : 'transparent'), background: on ? acc.s : 'transparent', textAlign: 'left' }}>
            <span style={{ width: 7, height: 7, borderRadius: 9, background: SECTOR_HUE[c.sector] || T.c400, flex: 'none' }} />
            <span style={{ fontFamily: T.font, fontWeight: 700, fontSize: 12.5, color: T.c900, flex: 1, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{c.name}</span>
            <span className="num" style={{ fontFamily: T.font, fontWeight: 500, fontSize: 11, color: T.c400, whiteSpace: 'nowrap' }}>{meta(c)}</span>
          </button>;})}
      </div>
    </div>);

  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0,1.32fr) minmax(0,1fr)', gap: 18, alignItems: 'start' }}>
      {/* ── 좌: 종목 매수 · 매도 ── */}
      <div style={{ border: '1px solid ' + T.line, borderRadius: 12, padding: '14px 16px 16px', background: T.c50, display: 'flex', flexDirection: 'column', gap: 12 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <Icon name="refresh" size={15} color={T.c500} />
          <span style={{ fontFamily: T.font, fontWeight: 700, fontSize: 13, color: T.c800 }}>종목 매수 · 매도</span>
          <span style={{ fontFamily: T.font, fontWeight: 500, fontSize: 11, color: T.c400 }}>유니버스에서 매수 · 보유에서 매도</span>
          <div style={{ marginLeft: 'auto' }}><Search value={q} onChange={setQ} placeholder={asset === '채권' ? '종목명·ISIN·국가 검색' : '종목명·티커·섹터 검색'} w={218} /></div>
        </div>
        {/* 두 칸: 유니버스 / 내 포트폴리오 */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
          <Column title="종목 유니버스" tag={`${universe.length}종목`} tagColor={T.blue} side="add" items={universe}
            empty={q ? `'${q}' 검색 결과가 없어요.` : '매수 가능한 종목이 없어요.'} meta={(c) => `${c.country} · ${c.sector}`} />
          <Column title="내 포트폴리오" tag={`${portfolio.length}종목`} tagColor={T.ok} side="remove" items={portfolio}
            empty={q ? `'${q}' 검색 결과가 없어요.` : '보유 종목이 없어요.'} meta={(c) => `${c._w.toFixed(1)}%`} />
        </div>
        {/* 선택 종목 + 금액 + 실행 */}
        {sel.id && <div style={{ background: T.white, border: '1px solid ' + T.line, borderRadius: 11, padding: '12px 14px' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
            <Pill kind={action === 'remove' ? 'alert' : 'blue'} small icon={action === 'add' ? 'plus' : 'x'}>{action === 'add' ? '매수' : '매도'}</Pill>
            <span style={{ fontFamily: T.font, fontWeight: 700, fontSize: 13, color: T.c900 }}>{u ? u.name : ''}</span>
            {action === 'remove' && <span style={{ marginLeft: 'auto', fontFamily: T.font, fontWeight: 500, fontSize: 11, color: T.c400 }}>보유 {Math.round(selAmt).toLocaleString()}억</span>}
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, background: T.c100, border: '1px solid ' + T.c250, borderRadius: 10, padding: '8px 12px' }}>
            <span style={{ fontFamily: T.font, fontWeight: 600, fontSize: 11.5, color: T.c500, flex: 'none' }}>{action === 'add' ? '매수' : '매도'}</span>
            <input type="number" value={amt} min={0} max={action === 'remove' ? Math.round(selAmt) : undefined} step={50}
            onChange={(e) => setAmt(Math.max(0, Math.round(+e.target.value || 0)))}
            style={{ flex: 1, border: 0, outline: 'none', background: 'transparent', textAlign: 'right', fontFamily: T.font, fontWeight: 800, fontSize: 20, color: T.c900, minWidth: 0 }} />
            <span style={{ fontFamily: T.font, fontWeight: 700, fontSize: 13, color: T.c400, flex: 'none' }}>억원</span>
          </div>
          <div style={{ display: 'flex', gap: 6, marginTop: 8 }}>
            {chips.map((c, i) => {const lab = action === 'add' ? c.toLocaleString() + '억' : c[0];const v = action === 'add' ? c : Math.round(c[1]);
              return <button key={i} onClick={() => setAmt(v)} style={{ flex: 1, border: '1px solid ' + T.c250, background: T.white, borderRadius: 7, padding: '5px 0', cursor: 'pointer', fontFamily: T.font, fontWeight: 600, fontSize: 11.5, color: T.c600 }}>{lab}</button>;})}
          </div>
          <Btn variant={action === 'remove' ? 'danger' : 'primary'} icon={action === 'add' ? 'plus' : 'x'} disabled={!valid} onClick={() => { onCommit(nextHolds, nextValue); showToast(`${u ? u.name : ''} ${Math.round(action === 'add' ? Math.max(0, amt) : Math.min(Math.max(0, amt), selAmt)).toLocaleString()}억 ${action === 'add' ? '매수' : '매도'}를 반영했어요 (모의)`); }} style={{ width: '100%', marginTop: 10 }}>{action === 'add' ? `${u ? u.name : ''} 매수하기` : `${u ? u.name : ''} 매도하기`}</Btn>
        </div>}
      </div>

      {/* ── 우: 실시간 리스크 측정 결과 ── */}
      <div style={{ border: '1px solid ' + T.line, borderRadius: 12, padding: '4px 18px 16px', background: T.c50 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '14px 0 4px' }}>
          <span style={{ width: 7, height: 7, borderRadius: 9, background: T.ok, boxShadow: '0 0 0 3px ' + T.okS }} />
          <span style={{ fontFamily: T.font, fontWeight: 700, fontSize: 13, color: T.c800 }}>리스크 측정 결과</span>
        </div>
        <CompareRow label="평가액" before={base.value} after={next.value} unit="억" neutral />
        <CompareRow label="포트폴리오 리스크 (CVaR)" before={base.risk} after={next.risk} unit="억" betterLow />
        <CompareRow label="한도 소진율" before={base.util} after={next.util} unit="%" digits={1} betterLow />
        <CompareRow label="잔여 리스크 한도" before={base.remain} after={next.remain} unit="억" />
        <CompareRow label="기대 수익률 (연)" before={base.ret} after={next.ret} unit="%" digits={1} />
        <CompareRow label="포트 변동성 (연)" before={base.vol} after={next.vol} unit="%" digits={1} betterLow />
        {/* 요인 기여 변화 */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 7, padding: '13px 0 2px' }}>
          <Icon name="layers" size={13} color={T.c500} />
          <span style={{ fontFamily: T.font, fontWeight: 700, fontSize: 11.5, color: T.c700 }}>팩터별 리스크 기여</span>
        </div>
        {(asset === '주식' ? ['eq', 'fx'] : asset === '채권' ? ['rate', 'credit', 'fx'] : ['rate', 'credit', 'eq', 'fx']).map((fid) =>
          <CompareRow key={fid} label={FMAP[fid].name} before={decB[fid].amt} after={decA[fid].amt} unit="억" betterLow />)}
        <div style={{ marginTop: 12 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
            <span style={{ fontFamily: T.font, fontWeight: 600, fontSize: 11, color: T.c500 }}>{action === 'add' ? '매수' : '매도'} 후 한도 소진율</span>
            <span className="num" style={{ fontFamily: T.font, fontWeight: 700, fontSize: 12, color: next.util >= 100 ? T.alert : next.util >= 80 ? T.warn : T.ok }}>{next.util.toFixed(1)}%</span>
          </div>
          <LimitMeter pct={next.util} />
          {next.util >= 100 && <div style={{ marginTop: 9, display: 'flex', alignItems: 'center', gap: 7 }}><Icon name="alert" size={14} color={T.alert} /><span style={{ fontFamily: T.font, fontWeight: 600, fontSize: 11.5, color: T.alert }}>리스크 한도를 초과해요. 금액 조정이 필요해요.</span></div>}
          {next.util >= 80 && next.util < 100 && <div style={{ marginTop: 9, display: 'flex', alignItems: 'center', gap: 7 }}><Icon name="alert" size={14} color={T.warn} /><span style={{ fontFamily: T.font, fontWeight: 600, fontSize: 11.5, color: T.warn }}>한도 소진율이 80%를 넘어 한도 임박 구간이에요.</span></div>}
        </div>
      </div>
    </div>);
}

// 특정 요인 노출 축소 목표 최적화: 노출도 역가중 틸트 후 현재 비중과 블렌드
function optimizeFactor(pf, fid) {
  const expo = (u) => fid === 'rate' ? (u.type === '채권' ? u.dur || 3 : 0)
    : fid === 'credit' ? (u.type === '채권' ? (u.dur || 3) * 0.5 * (u.credit || 0) : 0)
    : fid === 'eq' ? (u.type === '주식' ? u.beta || 1 : 0)
    : (u.fx ? 1 : 0);
  const maxE = Math.max(...pf.holds.map((h) => expo(UMAP[h.id])), 0.01);
  const score = pf.holds.map((h) => Math.max(0.06, 1 - 0.85 * expo(UMAP[h.id]) / maxE));
  let target = pf.holds.map((h, i) => h.w * score[i]);
  const tt = target.reduce((a, b) => a + b, 0) || 1; target = target.map((w) => w / tt * 100);
  let blended = pf.holds.map((h, i) => 0.55 * target[i] + 0.45 * h.w);
  const bt = blended.reduce((a, b) => a + b, 0); blended = blended.map((w) => w / bt * 100);
  const newHolds = pf.holds.map((h, i) => ({ id: h.id, w: +blended[i].toFixed(2) }));
  const before = summarize(pf);
  const after = summarize({ ...pf, holds: newHolds });
  return { newHolds, before, after,
    deltas: pf.holds.map((h, i) => ({ id: h.id, name: UMAP[h.id].name, type: UMAP[h.id].type, from: h.w, to: +newHolds[i].w.toFixed(1), diff: +(newHolds[i].w - h.w).toFixed(1) })) };
}

// ============ AI 최적화 패널 ============
function OptimizePanel({ pf, onCommit, asset, initialGoal }) {
  const [goal, setGoal] = React.useState(initialGoal || 'risk');
  const [res, setRes] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  React.useEffect(() => { if (initialGoal) { setGoal(initialGoal); setRes(null); } }, [initialGoal]);
  const GOALS = asset === '주식' ? [
    { v: 'risk', l: '수익 대비 변동성' },
    { v: 'eq', l: '베타 축소' },
    { v: 'fx', l: '환 노출 축소' }] : [
    { v: 'risk', l: '수익 대비 변동성' },
    { v: 'rate', l: '금리 노출 축소' },
    { v: 'credit', l: '신용 노출 축소' },
    { v: 'fx', l: '환 노출 축소' }];
  const run = () => {setBusy(true);setRes(null);setTimeout(() => {
    const r = goal === 'risk' ? optimize(pf) : optimizeFactor(pf, goal);
    const fb = {}; factorDecompose(pf).forEach((d) => fb[d.id] = d);
    const fa = {}; factorDecompose({ ...pf, holds: r.newHolds }).forEach((d) => fa[d.id] = d);
    setRes({ ...r, fb, fa, goal });setBusy(false);}, 850);};
  React.useEffect(() => {setRes(null);}, [pf]);

  if (!res) return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 16, padding: '26px 20px', textAlign: 'center' }}>
      <div style={{ width: 54, height: 54, borderRadius: 15, background: 'linear-gradient(135deg,#0A4FA3,#00A0E0)', display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0 6px 18px rgba(10,79,163,.25)' }}><Icon name="sparkles" size={28} color="#fff" /></div>
      <div>
        <div style={{ fontFamily: T.font, fontWeight: 700, fontSize: 15, color: T.c900 }}>AI 리스크 최적화</div>
        <div style={{ fontFamily: T.font, fontWeight: 500, fontSize: 12.5, color: T.c500, marginTop: 5, maxWidth: 460, lineHeight: 1.6 }}>{goal === 'risk' ? '위험조정수익(수익률 대비 변동성)과 종목 간 상관관계를 분석해 동일 수익 수준에서 리스크를 낮추는 비중을 추천해요.' : `${FMAP[goal].name} 노출이 큰 종목의 비중을 줄여 ${FMAP[goal].name} 기여 위험액을 낮추는 비중을 추천해요.`}</div>
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        <span style={{ fontFamily: T.font, fontWeight: 600, fontSize: 11.5, color: T.c500 }}>최적화 목표</span>
        <Segmented options={GOALS} value={goal} onChange={setGoal} size="sm" />
      </div>
      <Btn variant="primary" icon="sparkles" onClick={run} disabled={busy}>{busy ? '분석 중…' : 'AI 최적화 실행'}</Btn>
    </div>);

  const { before, after, deltas } = res;
  const goalName = res.goal === 'risk' ? null : FMAP[res.goal].name;
  const facCut = goalName ? (1 - res.fa[res.goal].amt / (res.fb[res.goal].amt || 1)) * 100 : 0;
  const riskCut = before.risk - after.risk;
  const nChanged = deltas.filter((d) => Math.abs(d.diff) >= 0.05).length;
  const kpis = [
    { l: '리스크 절감 (CVaR)', v: (riskCut >= 0 ? '−' : '+') + Math.abs(Math.round(riskCut)).toLocaleString(), u: '억', sub: `${((1 - after.risk / before.risk) * 100).toFixed(1)}% 감소`, c: riskCut >= 0 ? T.ok : T.alert },
    goalName ? { l: goalName + ' 노출 절감', v: '−' + Math.abs(facCut).toFixed(0), u: '%', sub: `${Math.round(res.fb[res.goal].amt).toLocaleString()}억 → ${Math.round(res.fa[res.goal].amt).toLocaleString()}억`, c: T.ok }
      : { l: '변동성 (연)', v: after.vol.toFixed(1), u: '%', sub: `현재 ${before.vol.toFixed(1)}%`, c: T.c900 },
    { l: '적용 후 소진율', v: after.util.toFixed(1), u: '%', sub: `현재 ${before.util.toFixed(1)}%`, c: after.util >= 100 ? T.alert : after.util >= 80 ? T.warn : T.ok },
    { l: '수익률 영향 (연)', v: (after.ret - before.ret >= 0 ? '+' : '') + (after.ret - before.ret).toFixed(1), u: '%p', sub: `${before.ret.toFixed(1)}% → ${after.ret.toFixed(1)}%`, c: dirColor(after.ret - before.ret) },
    { l: '리밸런싱 대상', v: String(nChanged), u: '종목', sub: `보유 ${deltas.length}종목 중`, c: T.c900 }];
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      {/* 결과 요약 스트립 */}
      <div style={{ display: 'grid', gridTemplateColumns: `repeat(${kpis.length},1fr)`, gap: 10 }}>
        {kpis.map((k) =>
          <div key={k.l} style={{ border: '1px solid ' + T.line, borderRadius: 11, padding: '12px 14px', background: T.white }}>
            <div style={{ fontFamily: T.font, fontWeight: 600, fontSize: 10.5, color: T.c500, textAlign: 'left' }}>{k.l}</div>
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 3, marginTop: 6 }}>
              <span className="num" style={{ fontFamily: T.font, fontWeight: 800, fontSize: 21, color: k.c, letterSpacing: '-.02em' }}>{k.v}</span>
              <span style={{ fontFamily: T.font, fontWeight: 600, fontSize: 11, color: T.c400 }}>{k.u}</span>
            </div>
            <div style={{ fontFamily: T.font, fontWeight: 500, fontSize: 10.5, color: T.c400, marginTop: 3, textAlign: 'left' }}>{k.sub}</div>
          </div>)}
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr minmax(0,360px)', gap: 20 }}>
      <div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
          <Pill kind="blue" icon="sparkles">AI 추천</Pill>
          <Pill kind="neutral" small>목표 · {GOALS.find((g) => g.v === res.goal).l}</Pill>
          <span style={{ fontFamily: T.font, fontWeight: 600, fontSize: 12, color: T.c500 }}>종목별 권장 비중 변화</span>
        </div>
        <div style={{ border: '1px solid ' + T.line, borderRadius: 10, overflow: 'hidden' }}>
          <DataTable dense rows={deltas.map((d, i) => ({ ...d, _key: d.id }))} initialSort={{ k: 'diff', dir: 'desc' }}
          cols={[
          { k: 'name', t: '종목', render: (r) => <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontWeight: 700, color: T.c900 }}><Icon name={r.type === '주식' ? 'trendUp' : 'bond'} size={13} color={T.c400} />{r.name}</span> },
          { k: 'from', t: '현재', num: true, render: (r) => <span className="num" style={{ color: T.c500 }}>{r.from.toFixed(1)}%</span> },
          { k: 'to', t: '추천', num: true, render: (r) => <span className="num" style={{ fontWeight: 700, color: T.c900 }}>{r.to.toFixed(1)}%</span> },
          { k: 'diff', t: '변화', num: true, render: (r) => {const c = Math.abs(r.diff) < 0.05 ? T.c400 : r.diff > 0 ? T.ok : T.alert;return <span className="num" style={{ display: 'inline-flex', alignItems: 'center', gap: 3, fontWeight: 700, color: c }}>{Math.abs(r.diff) >= 0.05 && <Icon name={r.diff > 0 ? 'trendUp' : 'trendDown'} size={12} color={c} stroke={2.4} />}{r.diff > 0 ? '+' : ''}{r.diff.toFixed(1)}%p</span>;}, sortVal: (r) => r.diff }]
          } />
        </div>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div style={{ border: '1px solid ' + T.line, borderRadius: 12, padding: '4px 16px 14px', background: T.c50 }}>
          <div style={{ fontFamily: T.font, fontWeight: 700, fontSize: 12.5, color: T.c800, padding: '13px 0 2px', textAlign: 'left' }}>최적화 효과</div>
          <CompareRow label="리스크 (CVaR)" before={before.risk} after={after.risk} unit="억" betterLow />
          <CompareRow label="변동성 (연)" before={before.vol} after={after.vol} unit="%" digits={1} betterLow />
          <CompareRow label="수익률 (연)" before={before.ret} after={after.ret} unit="%" digits={1} />
          <CompareRow label="소진율" before={before.util} after={after.util} unit="%" digits={1} betterLow />
          <div style={{ display: 'flex', alignItems: 'center', gap: 7, padding: '12px 0 2px' }}>
            <Icon name="layers" size={13} color={T.c500} />
            <span style={{ fontFamily: T.font, fontWeight: 700, fontSize: 11.5, color: T.c700 }}>팩터별 리스크 기여</span>
          </div>
          {(asset === '주식' ? ['eq', 'fx'] : asset === '채권' ? ['rate', 'credit', 'fx'] : ['rate', 'credit', 'eq', 'fx']).map((fid) =>
            <CompareRow key={fid} label={FMAP[fid].name} before={res.fb[fid].amt} after={res.fa[fid].amt} unit="억" betterLow />)}
        </div>
        <div style={{ background: T.okS, border: '1px solid ' + T.okL, borderRadius: 10, padding: '11px 13px', display: 'flex', gap: 9 }}>
          <Icon name="checkCircle" size={16} color={T.ok} />
          <span style={{ fontFamily: T.font, fontWeight: 500, fontSize: 11.5, color: T.c700, textAlign: 'left', lineHeight: 1.55 }}>
            {goalName ?
              <React.Fragment>{goalName} 기여 위험액을 <b style={{ color: T.ok }}>{facCut.toFixed(0)}%</b> 줄이면서 수익률 영향은 {(after.ret - before.ret).toFixed(1)}%p로 최소화했어요.</React.Fragment> :
              <React.Fragment>리스크를 <b style={{ color: T.ok }}>{Math.round(before.risk - after.risk).toLocaleString()}억 ({((1 - after.risk / before.risk) * 100).toFixed(1)}%)</b> 낮추면서 {Math.abs(after.ret - before.ret) < 0.8 ? '수익률은 거의 유지돼요.' : `수익률 영향은 ${(after.ret - before.ret).toFixed(1)}%p로 최소화했어요.`}</React.Fragment>}</span></div>
        <div style={{ display: 'flex', gap: 9 }}>
          <Btn variant="secondary" icon="refresh" onClick={run} style={{ flex: 'none' }}>다시</Btn>
          <Btn variant="primary" icon="check" onClick={() => { onCommit(res.newHolds); showToast(`AI 추천 비중을 적용했어요 · 리스크 ${Math.round(riskCut).toLocaleString()}억 절감`); }} style={{ flex: 1 }}>추천 비중 적용</Btn>
        </div>
      </div>
      </div>
    </div>);
}

// ============ 리스크 시뮬레이션 패널 ============
function SimulatePanel({ pf }) {
  const [scId, setScId] = React.useState(SCENARIOS[0].id);
  const sc = SCENARIOS.find((s) => s.id === scId);
  const base = summarize(pf);
  const sim = simulate(pf, sc);
  const breach = sim.util >= 100;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <div>
        <div style={{ fontFamily: T.font, fontWeight: 600, fontSize: 11.5, color: T.c500, marginBottom: 9, textAlign: 'left' }}>위기 시나리오 선택</div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 10 }}>
          {SCENARIOS.map((s) => {const on = s.id === scId;
            return <button key={s.id} onClick={() => setScId(s.id)} style={{ textAlign: 'left', border: '1.5px solid ' + (on ? T.blue : T.line), background: on ? T.blueSurface : T.white, borderRadius: 11, padding: '12px 13px', cursor: 'pointer', display: 'flex', flexDirection: 'column', gap: 5, transition: 'all .12s' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
                <div style={{ width: 26, height: 26, borderRadius: 7, background: on ? T.blue : T.c100, display: 'flex', alignItems: 'center', justifyContent: 'center', color: on ? '#fff' : T.c500, flex: 'none' }}><Icon name={s.icon} size={15} stroke={2} /></div>
                <span style={{ fontFamily: T.font, fontWeight: 700, fontSize: 12.5, color: on ? T.blue600 : T.c800 }}>{s.name}</span>
                {on && <Icon name="checkCircle" size={16} color={T.blue} style={{ marginLeft: 'auto' }} />}
              </div>
              <span style={{ fontFamily: T.font, fontWeight: 500, fontSize: 10.5, color: T.c500, lineHeight: 1.45 }}>{s.date} · {s.desc}</span>
            </button>;})}
        </div>
      </div>

      {/* 충격 파라미터 */}
      <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
        {[['주식', `${sc.eq}%`, T.down], ['금리', `${sc.rateBp > 0 ? '+' : ''}${sc.rateBp}bp`, sc.rateBp > 0 ? T.up : T.down], ['신용스프레드', `+${sc.creditBp}bp`, T.up], ['원/달러', `+${sc.fxPct}%`, T.up], ['변동성', `×${sc.volX}`, T.warn]].map(([k, v, c]) =>
        <span key={k} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, background: T.white, border: '1px solid ' + T.line, borderRadius: 8, padding: '6px 11px' }}>
            <span style={{ fontFamily: T.font, fontWeight: 500, fontSize: 11, color: T.c500 }}>{k}</span>
            <span className="num" style={{ fontFamily: T.font, fontWeight: 700, fontSize: 12, color: c }}>{v}</span></span>
        )}
      </div>

      {/* 결과 요약 */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 12 }}>
        {[
        { l: '예상 손익', v: (sim.portPnl > 0 ? '+' : '') + sim.portPnl.toFixed(1) + '%', s: fmtEok(sim.lossAmt), c: sim.portPnl < 0 ? T.down : T.up },
        { l: '시나리오 후 평가액', v: fmtEok(sim.newValue), s: `현재 ${fmtEok(pf.value)}`, c: T.c900 },
        { l: '시나리오 후 위험액', v: fmtEok(sim.newRisk), s: `현재 ${fmtEok(base.risk)} · +${Math.round((sim.newRisk / base.risk - 1) * 100)}%`, c: T.alert },
        { l: '한도 소진율', v: sim.util.toFixed(1) + '%', s: breach ? '한도 초과' : '한도 내', c: breach ? T.alert : sim.util >= 80 ? T.warn : T.ok }].
        map((k) =>
        <div key={k.l} style={{ border: '1px solid ' + T.line, borderRadius: 11, padding: '13px 15px', background: T.white }}>
            <div style={{ fontFamily: T.font, fontWeight: 600, fontSize: 11, color: T.c500, textAlign: 'left' }}>{k.l}</div>
            <div className="num" style={{ fontFamily: T.font, fontWeight: 800, fontSize: 23, color: k.c, letterSpacing: '-.02em', marginTop: 6, textAlign: 'left' }}>{k.v}</div>
            <div style={{ fontFamily: T.font, fontWeight: 500, fontSize: 11, color: T.c400, marginTop: 4, textAlign: 'left' }}>{k.s}</div>
          </div>
        )}
      </div>

      {breach && <div style={{ background: T.alertS, border: '1px solid ' + T.alertL, borderRadius: 10, padding: '11px 14px', display: 'flex', gap: 9, alignItems: 'center' }}>
        <Icon name="alert" size={16} color={T.alert} /><span style={{ fontFamily: T.font, fontWeight: 600, fontSize: 12.5, color: T.alert, textAlign: 'left' }}><b>{sc.name}</b> 발생 시 리스크 한도를 <b className="num">{(sim.util - 100).toFixed(0)}%p</b> 초과해요. 헤지·비중축소 대응이 필요해요.</span></div>}

      {/* 종목별 영향 */}
      <div>
        <div style={{ fontFamily: T.font, fontWeight: 600, fontSize: 11.5, color: T.c500, marginBottom: 10, textAlign: 'left' }}>종목별 시나리오 손익</div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          {sim.perHold.slice().sort((a, b) => a.pnl - b.pnl).map((x) => {const neg = x.pnl < 0;const mag = Math.min(50, Math.abs(x.pnl));
            return <div key={x.id} style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <span style={{ width: 140, fontFamily: T.font, fontWeight: 600, fontSize: 12, color: T.c700, textAlign: 'left', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', flex: 'none' }}>{x.name}</span>
              <span style={{ width: 42, fontFamily: T.font, fontWeight: 500, fontSize: 11, color: T.c400, flex: 'none' }}>{x.w.toFixed(1)}%</span>
              <div style={{ flex: 1, position: 'relative', height: 14, display: 'flex', justifyContent: 'center' }}>
                <div style={{ position: 'absolute', left: '50%', top: 0, bottom: 0, width: 1, background: T.c200 }} />
                <div style={{ position: 'absolute', top: 2, height: 10, borderRadius: 3, background: neg ? T.down : T.up,
                  width: mag / 50 * 48 + '%', [neg ? 'right' : 'left']: '50%' }} />
              </div>
              <span className="num" style={{ width: 58, textAlign: 'right', fontFamily: T.font, fontWeight: 700, fontSize: 12.5, color: neg ? T.down : T.up, flex: 'none' }}>{x.pnl > 0 ? '+' : ''}{x.pnl.toFixed(1)}%</span>
            </div>;})}
        </div>
      </div>
    </div>);
}

Object.assign(window, { PAL, fmtEok, riskContribs, LimitMeter, MiniBar, CompareRow, withAdded, optimizeFactor, AddStockPanel, OptimizePanel, SimulatePanel });
