/* global React */
const { useState, useMemo, useEffect } = React;

const fmtKRW = (n) => "₩" + Math.round(n).toLocaleString("ko-KR");
const fmtUSD = (n) => "$" + n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const fmtPct = (n) => (n >= 0 ? "+" : "") + n.toFixed(2) + "%";

const EVAL_LABEL = { hold: "Hold", trim: "Trim", sell: "Sell", add: "Add" };

function EvalChip({ value }) {
  return <span className={"eval-chip eval-" + value}><span className="badge-dot" />{EVAL_LABEL[value] || value}</span>;
}

function MoodIndicator({ mood, label }) {
  return (
    <div className="mood">
      <span className={"mood-dot mood-" + mood} />
      <span style={{ fontSize: 13, fontWeight: 700, color: "var(--ink-deep)" }}>{label}</span>
    </div>
  );
}

function BriefingCard({ data, flag }) {
  return (
    <div className={"briefing-card " + flag}>
      <div className="flag">{flag === "kr" ? "KR" : "US"}</div>
      <h3>{data.name}</h3>
      <div className="sub">{data.tag} · {data.openIn}</div>
      <MoodIndicator mood={data.mood} label={data.moodLabel} />
      <div className="index-row">
        {data.indices.map((idx) => (
          <div className="index-cell" key={idx.label}>
            <div className="label">{idx.label}</div>
            <div className="value num">{idx.value}</div>
            <div className={"delta " + idx.dir}>{idx.delta}</div>
          </div>
        ))}
      </div>
      <p>{data.summary}</p>
      <div className="watchlist">
        {data.watch.map((w) => <span key={w} className="badge">{w}</span>)}
      </div>
    </div>
  );
}

function Sparkline({ data, width = 280, height = 56 }) {
  if (!data || data.length < 2) return null;
  const min = Math.min(...data);
  const max = Math.max(...data);
  const range = max - min || 1;
  const step = width / (data.length - 1);
  const pts = data.map((v, i) => {
    const x = i * step;
    const y = height - ((v - min) / range) * (height - 4) - 2;
    return x.toFixed(1) + "," + y.toFixed(1);
  }).join(" ");
  const first = data[0];
  const last = data[data.length - 1];
  const stroke = last >= first ? "var(--success)" : "var(--critical)";
  const fill   = last >= first ? "rgba(29,152,86,0.10)" : "rgba(224,36,94,0.10)";
  // area path
  const lastX = (data.length - 1) * step;
  const lastY = height - ((last - min) / range) * (height - 4) - 2;
  const area = `M0,${height} L${pts.split(" ").join(" L")} L${lastX.toFixed(1)},${height} Z`;
  return (
    <svg width={width} height={height} style={{ display: "block" }} aria-label="가격 추이">
      <path d={area} fill={fill} stroke="none" />
      <polyline fill="none" stroke={stroke} strokeWidth="1.5" points={pts} />
      <circle cx={lastX} cy={lastY} r="2.5" fill={stroke} />
    </svg>
  );
}

function TechIndicators({ ind }) {
  if (!ind) return null;
  const fmt = (n, d = 1) => n == null || !isFinite(n) ? "—" : n.toFixed(d);
  const sign = (n) => n == null || !isFinite(n) ? "" : (n >= 0 ? "+" : "");
  const dirCls = (n) => n == null || !isFinite(n) ? "" : n >= 0 ? "pl-up" : "pl-down";
  const rsiTone = ind.rsi == null ? "var(--steel)"
    : ind.rsi >= 70 ? "var(--critical)"
    : ind.rsi <= 30 ? "var(--primary)" : "var(--charcoal)";
  const rsiTag = ind.rsi == null ? "" : ind.rsi >= 70 ? "과매수" : ind.rsi <= 30 ? "과매도" : "중립";
  return (
    <div className="tech-grid">
      <div className="tech-cell">
        <div className="lbl">RSI(14)</div>
        <div className="val" style={{ color: rsiTone }}>
          {fmt(ind.rsi)}
          <span style={{ fontSize: 11, marginLeft: 4, fontWeight: 500 }}>{rsiTag}</span>
        </div>
      </div>
      <div className="tech-cell">
        <div className="lbl">vs 20일선</div>
        <div className={"val " + dirCls(ind.pctFromSma20)}>{sign(ind.pctFromSma20)}{fmt(ind.pctFromSma20)}%</div>
      </div>
      <div className="tech-cell">
        <div className="lbl">vs 60일선</div>
        <div className={"val " + dirCls(ind.pctFromSma60)}>{sign(ind.pctFromSma60)}{fmt(ind.pctFromSma60)}%</div>
      </div>
      <div className="tech-cell">
        <div className="lbl">vs 200일선</div>
        <div className={"val " + dirCls(ind.pctFromSma200)}>{sign(ind.pctFromSma200)}{fmt(ind.pctFromSma200)}%</div>
      </div>
      <div className="tech-cell">
        <div className="lbl">52주 고점</div>
        <div className={"val " + dirCls(ind.pctFromMax52w)}>{sign(ind.pctFromMax52w)}{fmt(ind.pctFromMax52w)}%</div>
      </div>
      <div className="tech-cell">
        <div className="lbl">거래량 5/60</div>
        <div className="val">{fmt(ind.volRatio, 2)}배</div>
      </div>
    </div>
  );
}

function ChevronDown() {
  return (
    <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
      <path d="M3 5l4 4 4-4" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

function HoldingRow({ h, market, expanded, onToggle, onSell, onRemove, onEdit, onReanalyze, onLoadHistory, historyLoading, reanalyzingIds }) {
  const isReanalyzing = !!(reanalyzingIds && reanalyzingIds[h.id]);
  const isUS = market === "us";
  const fmtP = isUS ? fmtUSD : fmtKRW;
  const buyTotal = h.buyPrice * h.qty;
  const curTotal = h.currentPrice * h.qty;
  const pl = curTotal - buyTotal;
  const plPct = (pl / buyTotal) * 100;
  const dir = pl > 0 ? "pl-up" : pl < 0 ? "pl-down" : "pl-flat";
  const isLoadingHist = !!(historyLoading && historyLoading[h.id]);

  // 행 펼침 시 + 지표 없음 + 로딩 중 아님 → 자동 로드
  useEffect(() => {
    if (expanded && !h.indicators && !isLoadingHist && onLoadHistory) {
      onLoadHistory(h, market);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [expanded]);

  return (
    <React.Fragment>
      <tr className="row-main" onClick={onToggle}>
        <td className="l">
          <div className="ticker">
            <span className="name">{h.name}</span>
            <span className="code">{h.code}</span>
          </div>
        </td>
        <td className="num mob-hide">{h.buyDate}</td>
        <td className="num mob-hide">{fmtP(h.buyPrice)}</td>
        <td className="num mob-hide">{h.qty}</td>
        <td className="num mob-hide">{fmtP(buyTotal)}</td>
        <td className="num">{fmtP(h.currentPrice)}</td>
        <td className="num"><strong>{fmtP(curTotal)}</strong></td>
        <td>
          <div className={"pl-cell " + dir}>
            <span className="amt">{(pl >= 0 ? "+" : "−") + fmtP(Math.abs(pl)).replace(/^[-+]/, "")}</span>
            <span className="pct">{fmtPct(plPct)}</span>
          </div>
        </td>
        <td><EvalChip value={h.eval} /></td>
        <td style={{ width: 40 }}>
          <button className={"row-toggle " + (expanded ? "open" : "")} aria-label="펼치기">
            <ChevronDown />
          </button>
        </td>
      </tr>
      {expanded && (
        <tr className="row-detail">
          <td colSpan={10}>
            <div className="panel">
              <div className="panel-head">
                <EvalChip value={h.eval} />
                <span className="panel-meta-divider" />
                <span className="panel-eval-label">신뢰도</span>
                <span style={{ fontSize: 13, fontWeight: 700, color: "var(--ink-deep)" }}>{h.confidence || "보통"}</span>
                <span className="panel-meta-divider" />
                <span className="panel-meta">
                  <span className="badge-dot" />
                  {h.analyzedAt
                    ? `Claude · ${new Date(h.analyzedAt).toLocaleString("ko-KR", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" })} 분석`
                    : "Claude · 분석 미실행"}
                </span>
                <div className="panel-actions" style={{ display: "flex", gap: 8 }}>
                  {onReanalyze && (
                    <button className="btn btn-ghost btn-sm"
                      onClick={(e) => { e.stopPropagation(); onReanalyze(); }}
                      disabled={isReanalyzing}
                      title="이 종목만 Claude로 재분석">
                      {isReanalyzing ? "재분석 중..." : "↻ 재분석"}
                    </button>
                  )}
                  <button
                    className="btn btn-ghost btn-sm"
                    onClick={(e) => {
                      e.stopPropagation();
                      if (confirm(`${h.name} 종목을 목록에서 제거할까요?\n(매도가 아닌 잘못된 입력 정리용 — 매매 일지에 기록되지 않습니다)`)) onRemove();
                    }}
                  >제거</button>
                  <button className="btn btn-secondary btn-sm" onClick={(e) => { e.stopPropagation(); onEdit(); }}>수정</button>
                  <button className="btn btn-buy btn-sm" onClick={(e) => { e.stopPropagation(); onSell(); }}>매도</button>
                </div>
              </div>

              {(h.historySpark || h.indicators || isLoadingHist) && (
                <div className="tech-block">
                  <div className="tech-spark">
                    {h.historySpark ? (
                      <Sparkline data={h.historySpark} width={280} height={56} />
                    ) : (
                      <div className="spark-placeholder">{isLoadingHist ? "차트 로드 중..." : "차트 없음"}</div>
                    )}
                    <div className="spark-meta">
                      {h.historySpark ? `최근 ${h.historySpark.length}일 종가` : ""}
                      {h.historyAsOf ? ` · ${new Date(h.historyAsOf).toLocaleString("ko-KR", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" })}` : ""}
                    </div>
                  </div>
                  <TechIndicators ind={h.indicators} />
                </div>
              )}

              <div className="panel-summary">
                <div className="label">한 줄 판단 근거</div>
                <p>{h.reason}</p>
              </div>

              <div className="panel-grid">
                <div className="panel-block bullets">
                  <div className="block-head">
                    <span className="icon">▲</span>
                    <span className="title">주요 포인트 (Bull)</span>
                  </div>
                  <ul>
                    {h.bullets.map((b, i) => <li key={i}>{b}</li>)}
                  </ul>
                </div>
                <div className="panel-block risks">
                  <div className="block-head">
                    <span className="icon">▼</span>
                    <span className="title">리스크 / 모니터링</span>
                  </div>
                  <ul>
                    {h.risks.map((r, i) => <li key={i}>{r}</li>)}
                  </ul>
                </div>
              </div>

              {h.portfolioNote && (
                <div className="panel-pf-note">
                  <span className="label">포트폴리오 분산 관점</span>
                  <p>{h.portfolioNote}</p>
                </div>
              )}

              <div className="panel-foot">
                <span className="model-tag">
                  <span className="dot" />
                  {h.analyzedAt
                    ? `Claude · ${new Date(h.analyzedAt).toLocaleString("ko-KR", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" })} 분석`
                    : "Claude · 분석 미실행"}
                  {h.priceAsOf ? ` · 시세 ${h.priceAsOf}` : ""}
                </span>
                <span>판단을 갱신하려면 우측 상단 <strong>분석 실행</strong> 또는 <strong>↻ 재분석</strong></span>
              </div>
            </div>
          </td>
        </tr>
      )}
    </React.Fragment>
  );
}

function PortfolioTable({ title, market, holdings, expandedId, setExpandedId, onSell, onAdd, onRemove, onEdit, onReanalyze, onLoadHistory, historyLoading, reanalyzingIds }) {
  const isUS = market === "us";
  const fmtP = isUS ? fmtUSD : fmtKRW;
  const totals = useMemo(() => {
    let buy = 0, cur = 0;
    holdings.forEach((h) => { buy += h.buyPrice * h.qty; cur += h.currentPrice * h.qty; });
    return { buy, cur, pl: cur - buy, pct: buy ? ((cur - buy) / buy) * 100 : 0 };
  }, [holdings]);

  const dir = totals.pl > 0 ? "pl-up" : totals.pl < 0 ? "pl-down" : "pl-flat";

  return (
    <div className="portfolio-card">
      <div className="header">
        <h3>{title} <span style={{ color: "var(--steel)", fontWeight: 400, fontSize: 14, marginLeft: 8 }}>· {holdings.length}종목</span></h3>
        <div className="summary" style={{ display: "flex", alignItems: "center", gap: 24 }}>
          <div className="stat">
            <div className="lbl">평가금액</div>
            <div className="val num">{fmtP(totals.cur)}</div>
          </div>
          <div className="stat">
            <div className="lbl">매수원가</div>
            <div className="val num" style={{ color: "var(--charcoal)" }}>{fmtP(totals.buy)}</div>
          </div>
          <div className="stat">
            <div className="lbl">손익</div>
            <div className={"val num " + dir}>
              {(totals.pl >= 0 ? "+" : "−") + fmtP(Math.abs(totals.pl)).replace(/^[-+]/, "")}
              <span style={{ fontSize: 12, marginLeft: 6 }}>({fmtPct(totals.pct)})</span>
            </div>
          </div>
          <button className="btn btn-primary btn-sm" onClick={onAdd} style={{ marginLeft: 8 }}>+ 종목 추가</button>
        </div>
      </div>
      <div style={{ overflowX: "auto" }}>
        <table className="ptable">
          <thead>
            <tr>
              <th className="l">종목</th>
              <th className="mob-hide">매수일</th>
              <th className="mob-hide">매수가</th>
              <th className="mob-hide">수량</th>
              <th className="mob-hide">매수금액</th>
              <th>현재가</th>
              <th>평가금액</th>
              <th>손익(%)</th>
              <th>평가</th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            {holdings.map((h) => (
              <HoldingRow
                key={h.id}
                h={h}
                market={market}
                expanded={expandedId === h.id}
                onToggle={() => setExpandedId(expandedId === h.id ? null : h.id)}
                onSell={() => onSell(h, market)}
                onRemove={() => onRemove(h.id, market)}
                onEdit={() => onEdit(h, market)}
                onReanalyze={onReanalyze ? () => onReanalyze(h, market) : null}
                onLoadHistory={onLoadHistory}
                historyLoading={historyLoading}
                reanalyzingIds={reanalyzingIds}
              />
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}

function CashBar({ krw, usd, fxRate, setKrw, setUsd, fxAsOf }) {
  const krwInUsd = krw / fxRate;
  const usdInKrw = usd * fxRate;
  const totalKrw = krw + usdInKrw;

  return (
    <div className="cash-bar">
      <div className="cash-cell">
        <span className="lbl">원화 예수금</span>
        <div className="row">
          <span className="ccy">₩</span>
          <input
            type="text"
            value={krw.toLocaleString("ko-KR")}
            onChange={(e) => setKrw(parseFloat(e.target.value.replace(/[^\d.]/g, "")) || 0)}
          />
        </div>
        <span style={{ fontSize: 12, color: "var(--steel)" }}>≈ ${krwInUsd.toLocaleString("en-US", { maximumFractionDigits: 0 })}</span>
      </div>
      <div className="cash-cell">
        <span className="lbl">달러 예수금</span>
        <div className="row">
          <span className="ccy">$</span>
          <input
            type="text"
            value={usd.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
            onChange={(e) => setUsd(parseFloat(e.target.value.replace(/[^\d.]/g, "")) || 0)}
          />
        </div>
        <span style={{ fontSize: 12, color: "var(--steel)" }}>≈ ₩{Math.round(usdInKrw).toLocaleString("ko-KR")}</span>
      </div>
      <div className="cash-cell">
        <span className="lbl">적용 환율</span>
        <div className="row"><span className="ccy">USD/KRW</span></div>
        <span className="num" style={{ fontSize: 22, fontWeight: 700, letterSpacing: -0.4 }}>{fxRate.toLocaleString("ko-KR", { maximumFractionDigits: 2 })}</span>
        <span style={{ fontSize: 12, color: "var(--steel)" }}>{fxAsOf ? `Frankfurter · ${fxAsOf}` : "수동 환율"}</span>
      </div>
      <div className="cash-cell summary">
        <span className="lbl">총 예수금 (원화 환산)</span>
        <span className="val num">₩{Math.round(totalKrw).toLocaleString("ko-KR")}</span>
        <span className="sub">즉시 매수 가능 자금</span>
      </div>
    </div>
  );
}

function SoldRecords({ records, onDelete }) {
  return (
    <div className="sold-card">
      <div className="sold-head">
        <span>종목 / 매도일</span>
        <span style={{ textAlign: "right" }}>매수가</span>
        <span style={{ textAlign: "right" }}>매도가</span>
        <span style={{ textAlign: "right" }}>수량</span>
        <span style={{ textAlign: "right" }}>실현 손익</span>
        <span>매도 사유</span>
        <span style={{ textAlign: "right" }}></span>
      </div>
      {records.length === 0 && (
        <div className="journal-empty">
          기록된 매도 거래가 없습니다.<br/>
          대시보드에서 보유 종목을 펼쳐 <strong>매도</strong> 버튼으로 거래를 기록할 수 있습니다.
        </div>
      )}
      {records.map((r) => {
        const isUS = r.market === "us";
        const fmtP = isUS ? fmtUSD : fmtKRW;
        const pl = (r.sellPrice - r.buyPrice) * r.qty;
        const plPct = ((r.sellPrice - r.buyPrice) / r.buyPrice) * 100;
        const dir = pl > 0 ? "pl-up" : pl < 0 ? "pl-down" : "pl-flat";
        return (
          <div className="sold-row" key={r.id}>
            <div>
              <span className="lbl">{r.market === "kr" ? "국내" : "미국"} · {r.code}</span>
              <span className="val" style={{ display: "block" }}>{r.name}</span>
              <span style={{ fontSize: 12, color: "var(--steel)" }}>매도 {r.sellDate} · 보유 {r.buyDate}부터</span>
            </div>
            <div style={{ textAlign: "right" }}>
              <span className="lbl">매수가</span>
              <span className="val">{fmtP(r.buyPrice)}</span>
            </div>
            <div style={{ textAlign: "right" }}>
              <span className="lbl">매도가</span>
              <span className="val">{fmtP(r.sellPrice)}</span>
            </div>
            <div style={{ textAlign: "right" }}>
              <span className="lbl">수량</span>
              <span className="val">{r.qty}</span>
            </div>
            <div style={{ textAlign: "right" }}>
              <span className="lbl">손익</span>
              <span className={"val " + dir}>{(pl >= 0 ? "+" : "−") + fmtP(Math.abs(pl)).replace(/^[-+]/, "")}</span>
              <span className={"val " + dir} style={{ display: "block", fontSize: 12 }}>{fmtPct(plPct)}</span>
            </div>
            <div className="reason">{r.reason}</div>
            <div style={{ textAlign: "right" }}>
              <button
                className="btn btn-ghost btn-sm"
                onClick={() => {
                  if (confirm(`${r.name} 매도 기록을 삭제할까요?\n(되돌릴 수 없습니다 — 보유 목록에는 영향 없음)`)) onDelete(r.id);
                }}
                title="이 매도 기록만 삭제"
              >삭제</button>
            </div>
          </div>
        );
      })}
    </div>
  );
}

function SellModal({ holding, market, onClose, onSubmit }) {
  const isUS = market === "us";
  const today = new Date().toISOString().slice(0, 10);
  const [sellDate, setSellDate] = useState(today);
  const [sellPrice, setSellPrice] = useState(holding.currentPrice);
  const [qty, setQty] = useState(holding.qty);
  const [reason, setReason] = useState("");

  const submit = (e) => {
    e.preventDefault();
    if (!reason.trim()) { alert("매도 사유를 입력해주세요."); return; }
    onSubmit({
      id: "s" + Date.now(),
      name: holding.name, code: holding.code, market,
      buyDate: holding.buyDate, sellDate,
      buyPrice: holding.buyPrice,
      sellPrice: parseFloat(sellPrice),
      qty: parseInt(qty, 10),
      reason: reason.trim()
    });
  };

  return (
    <div className="modal-overlay">
      <div className="modal">
        <h3>매도</h3>
        <div className="sub">{holding.name} ({holding.code}) · 보유 {holding.qty}주</div>
        <form onSubmit={submit}>
          <div className="grid2">
            <div className="field">
              <label>매도일</label>
              <input type="date" value={sellDate} onChange={(e) => setSellDate(e.target.value)} />
            </div>
            <div className="field">
              <label>매도 수량</label>
              <input type="number" value={qty} onChange={(e) => setQty(e.target.value)} max={holding.qty} min={1} />
            </div>
          </div>
          <div className="field">
            <label>매도가 ({isUS ? "USD" : "KRW"})</label>
            <input type="number" step="0.01" value={sellPrice} onChange={(e) => setSellPrice(e.target.value)} />
          </div>
          <div className="field">
            <label>매도 사유 (필수)</label>
            <textarea
              placeholder="예: 목표 수익률 달성, 펀더멘털 훼손, 포트폴리오 리밸런싱 등 구체적인 사유"
              value={reason}
              onChange={(e) => setReason(e.target.value)}
            />
          </div>
          <div className="actions">
            <button type="button" className="btn btn-ghost" onClick={onClose}>취소</button>
            <button type="submit" className="btn btn-buy">매도 저장</button>
          </div>
        </form>
      </div>
    </div>
  );
}

/* ----- Alerts page ----- */

const ALERT_ICON = { up: "↑", down: "↓", flat: "≈", news: "i" };

function AlertCard({ a, onMarkRead }) {
  return (
    <div className={"alert-card " + a.type + (a.unread ? " unread" : "")}>
      <div className={"alert-icon " + a.type}>{ALERT_ICON[a.type]}</div>
      <div className="alert-body">
        <div className="alert-head">
          <span className="alert-title">{a.name} <span style={{ color: "var(--steel)", fontWeight: 400, fontSize: 13 }}>· {a.code}</span></span>
          {a.fromEval && a.toEval && (
            <span className="alert-shift">
              <span className={"from " + a.fromEval}>{EVAL_LABEL[a.fromEval]}</span>
              <span className="arrow">→</span>
              <span className={"to " + a.toEval}>{EVAL_LABEL[a.toEval]}</span>
            </span>
          )}
        </div>
        <div className="alert-detail">{a.detail}</div>
        <div className="alert-meta">
          <span className="alert-time">{a.time}</span>
          {a.related && <span>· {a.related}</span>}
          <span>· {a.market === "kr" ? "국내" : "미국"}</span>
        </div>
      </div>
      <div className="alert-actions">
        {a.unread && <button className="btn btn-ghost btn-sm" onClick={() => onMarkRead(a.id)}>읽음</button>}
        <button className="btn btn-secondary btn-sm">상세 보기</button>
      </div>
    </div>
  );
}

function AlertsPage({ alerts, onMarkRead, onMarkAllRead, onDeleteAll }) {
  const [filter, setFilter] = useState("all");
  const counts = useMemo(() => ({
    all: alerts.length,
    unread: alerts.filter((a) => a.unread).length,
    upgrade: alerts.filter((a) => a.type === "up").length,
    downgrade: alerts.filter((a) => a.type === "down" || a.type === "flat").length,
    news: alerts.filter((a) => a.type === "news").length
  }), [alerts]);

  const filtered = alerts.filter((a) => {
    if (filter === "all") return true;
    if (filter === "unread") return a.unread;
    if (filter === "upgrade") return a.type === "up";
    if (filter === "downgrade") return a.type === "down" || a.type === "flat";
    if (filter === "news") return a.type === "news";
    return true;
  });

  const FILTERS = [
    { id: "all", label: "전체" },
    { id: "unread", label: "안 읽음" },
    { id: "upgrade", label: "상향" },
    { id: "downgrade", label: "하향" },
    { id: "news", label: "관찰" }
  ];

  return (
    <section className="section">
      <div className="section-head">
        <h2><strong>알림</strong> · LLM 판단 변동</h2>
        <button className="btn-link" onClick={onMarkAllRead}>모두 읽음 처리</button>
        <button className="btn-link" style={{ color: "var(--danger, #e53935)", marginLeft: 12 }} onClick={onDeleteAll}>모두 삭제</button>
      </div>
      <div className="alerts-toolbar">
        {FILTERS.map((f) => (
          <button
            key={f.id}
            className={"filter-pill " + (filter === f.id ? "active" : "")}
            onClick={() => setFilter(f.id)}
          >
            {f.label}<span className="count">{counts[f.id]}</span>
          </button>
        ))}
      </div>
      <div className="alerts-list">
        {filtered.length === 0 && (
          <div className="journal-empty" style={{ background: "var(--surface-soft)", borderRadius: "var(--r-xl)" }}>
            해당 조건의 알림이 없습니다.
          </div>
        )}
        {filtered.map((a) => <AlertCard key={a.id} a={a} onMarkRead={onMarkRead} />)}
      </div>
    </section>
  );
}

/* ----- Add / Edit Holding Modal ----- */

function HoldingFormModal({ market, initial, mode, onClose, onSubmit }) {
  const isUS = market === "us";
  const isEdit = mode === "edit";
  const today = new Date().toISOString().slice(0, 10);
  const [name, setName] = useState(initial?.name ?? "");
  const [code, setCode] = useState(initial?.code ?? "");
  const [buyDate, setBuyDate] = useState(initial?.buyDate ?? today);
  const [buyPrice, setBuyPrice] = useState(initial?.buyPrice ?? "");
  const [qty, setQty] = useState(initial?.qty ?? "");
  const [currentPrice, setCurrentPrice] = useState(initial?.currentPrice ?? "");
  const [fetching, setFetching] = useState(false);
  const [statusMsg, setStatusMsg] = useState("");
  const [lookingUp, setLookingUp] = useState(false);
  const [autoFilledName, setAutoFilledName] = useState(false); // 자동 채워진 이름인지 추적

  // 코드 입력란 blur 시 종목명 자동 조회 — 이름이 비어있거나 직전에 자동 채워진 경우만
  const lookupName = async () => {
    const c = code.trim();
    if (!c || lookingUp) return;
    if (name.trim() && !autoFilledName) return; // 사용자가 직접 입력한 이름은 보존
    setLookingUp(true);
    try {
      const info = await window.api.lookupSymbol(c);
      if (info?.name) {
        setName(info.name);
        setAutoFilledName(true);
      }
      // Yahoo가 반환한 정확한 심볼로 코드 정규화 (예: "005930" → "005930.KS")
      if (info?.symbol && info.symbol.toUpperCase() !== c.toUpperCase()) {
        setCode(info.symbol.toUpperCase());
      }
    } catch (e) {
      console.warn("[lookup] fail:", e.message);
      // 조용히 실패 — 사용자가 직접 입력하면 됨
    } finally {
      setLookingUp(false);
    }
  };

  const submit = async (e) => {
    e.preventDefault();
    if (!code.trim() || !buyPrice || !qty) {
      alert("코드·매수가·수량은 필수 항목입니다.");
      return;
    }
    const bp = parseFloat(buyPrice);
    const q  = parseInt(qty, 10);
    const cleanCode = code.trim().toUpperCase();
    let cp = (currentPrice === "" || currentPrice == null) ? null : parseFloat(currentPrice);
    let priceAsOf;

    // 현재가 비어있으면 시세 자동 조회
    if (cp == null || isNaN(cp)) {
      setFetching(true);
      setStatusMsg(`${cleanCode} 시세 조회 중...`);
      try {
        const r = await window.api.fetchPrice(cleanCode);
        cp = r.price;
        priceAsOf = r.asOf;
        setStatusMsg(`시세 ${cp} (${r.source})`);
      } catch (err) {
        setFetching(false);
        const ok = confirm(
          `현재가 조회 실패:\n${err.message}\n\n` +
          `매수가(${bp})를 임시 현재가로 설정해 진행할까요?\n(취소하면 코드를 점검할 수 있도록 모달이 유지됩니다)`
        );
        if (!ok) { setStatusMsg("조회 실패 — 코드 형식 점검 (US: AAPL / KR: 005930.KS)"); return; }
        cp = bp;
      }
      setFetching(false);
    }

    const base = isEdit ? { ...initial } : {
      id: "new" + Date.now(),
      eval: "hold", confidence: "분석 대기",
      reason: "신규 추가된 종목입니다. 다음 분석 실행 시 LLM 판단이 산출됩니다.",
      bullets: ["신규 등록 — LLM 분석 대기 중"],
      risks: ["분석 미완료 상태에서는 평가가 임시값입니다"]
    };
    onSubmit({
      ...base,
      name: name.trim() || cleanCode, code: cleanCode,
      buyDate, buyPrice: bp, qty: q, currentPrice: cp,
      priceAsOf: priceAsOf || base.priceAsOf
    });
  };

  return (
    <div className="modal-overlay">
      <div className="modal">
        <h3>{isEdit ? "종목 수정" : (isUS ? "미국" : "국내") + " 종목 추가"}</h3>
        <div className="sub">
          {isEdit
            ? "현재가를 비우고 저장하면 자동으로 시세를 조회합니다. LLM 분석 필드는 다음 분석 실행에서 갱신됩니다."
            : "신규 종목은 다음 분석 실행에서 LLM 판단이 부여됩니다. 현재가를 비우면 저장 시 시세를 자동 조회합니다."}
        </div>
        <form onSubmit={submit}>
          <div className="grid2">
            <div className="field">
              <label>티커 / 코드</label>
              <input
                value={code}
                onChange={(e) => { setCode(e.target.value); if (autoFilledName) { setName(""); setAutoFilledName(false); } }}
                onBlur={lookupName}
                placeholder={isUS ? "AAPL" : "005930 또는 005930.KS"}
                autoFocus={!isEdit}
              />
            </div>
            <div className="field">
              <label>종목명 {lookingUp && <span style={{ color: "var(--steel)", fontWeight: 400, textTransform: "none", letterSpacing: 0 }}>· 조회 중...</span>}</label>
              <input
                value={name}
                onChange={(e) => { setName(e.target.value); setAutoFilledName(false); }}
                placeholder={isUS ? "코드 입력 후 Tab — 자동 조회" : "코드 입력 후 Tab — 자동 조회"}
              />
            </div>
          </div>
          <div className="grid2">
            <div className="field">
              <label>매수일</label>
              <input type="date" value={buyDate} onChange={(e) => setBuyDate(e.target.value)} />
            </div>
            <div className="field">
              <label>수량</label>
              <input type="number" min="1" value={qty} onChange={(e) => setQty(e.target.value)} />
            </div>
          </div>
          <div className="grid2">
            <div className="field">
              <label>매수가 ({isUS ? "USD" : "KRW"})</label>
              <input type="number" step="0.01" value={buyPrice} onChange={(e) => setBuyPrice(e.target.value)} />
            </div>
            <div className="field">
              <label>현재가 (비우면 자동 조회)</label>
              <input
                type="number" step="0.01"
                value={currentPrice}
                onChange={(e) => setCurrentPrice(e.target.value)}
                placeholder="비워두면 저장 시 시세 자동 조회"
              />
            </div>
          </div>
          {statusMsg && (
            <div className={"field-status " + (fetching ? "info" : "ok")}>{statusMsg}</div>
          )}
          <div className="actions">
            <button type="button" className="btn btn-ghost" onClick={onClose} disabled={fetching}>취소</button>
            <button type="submit" className="btn btn-buy" disabled={fetching}>
              {fetching ? "시세 조회 중..." : (isEdit ? "저장" : "추가")}
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}

// Backwards-compatible alias
function AddHoldingModal(props) { return <HoldingFormModal {...props} mode="add" />; }
function EditHoldingModal(props) { return <HoldingFormModal {...props} mode="edit" />; }

/* ----- Settings Modal ----- */

function SettingsModal({ settings, onClose, onSave, onClear }) {
  const [model, setModel] = useState(settings.model || "claude-haiku-4-5-20251001");
  const [autoFx, setAutoFx] = useState(settings.autoFx !== false);
  const [autoPrices, setAutoPrices] = useState(settings.autoPrices !== false);
  const [autoRefreshMin, setAutoRefreshMin] = useState(
    settings.autoRefreshMin == null ? 5 : Number(settings.autoRefreshMin)
  );
  const [investStyle, setInvestStyle] = useState(settings.investStyle || "장투");
  const [stopLossPct, setStopLossPct] = useState(settings.stopLossPct == null ? -10 : Number(settings.stopLossPct));
  const [useWebSearch, setUseWebSearch] = useState(settings.useWebSearch !== false);
  const [testState, setTestState] = useState({ kind: "idle", msg: "" });

  const test = async () => {
    setTestState({ kind: "info", msg: "서버 → Anthropic API 연결 테스트 중..." });
    try {
      const text = await window.api.claudeMessage({
        model, max_tokens: 64,
        system: "한 단어로만 답하세요.",
        user: 'Reply with the single word "OK".'
      });
      setTestState({ kind: "ok", msg: "응답 정상: " + text.slice(0, 40) });
    } catch (e) {
      setTestState({ kind: "err", msg: e.message });
    }
  };

  const submit = (e) => {
    e.preventDefault();
    onSave({
      model, autoFx, autoPrices,
      autoRefreshMin: Math.max(0, Math.floor(Number(autoRefreshMin) || 0)),
      investStyle,
      stopLossPct: Math.min(0, Number(stopLossPct) || 0),
      useWebSearch
    });
  };

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal" onClick={(e) => e.stopPropagation()}>
        <h3>설정</h3>
        <div className="sub">
          API 키는 서버 <code>.env</code> 파일에 보관됩니다. 브라우저에 노출되지 않습니다.
          공용 PC에서는 사용 후 <button type="button" className="btn-link" style={{ display: "inline" }} onClick={onClear}>전체 초기화</button>를 권장합니다.
        </div>
        <form onSubmit={submit}>
          <div className="field">
            <label>모델</label>
            <select value={model} onChange={(e) => setModel(e.target.value)}>
              <option value="claude-haiku-4-5-20251001">Claude Haiku 4.5 (빠름·저비용)</option>
              <option value="claude-sonnet-4-6">Claude Sonnet 4.6 (균형)</option>
              <option value="claude-opus-4-7">Claude Opus 4.7 (고품질·고비용)</option>
            </select>
          </div>
          <div className="field">
            <label>투자 프로파일 — LLM 판단에 반영</label>
            <div className="grid2">
              <div>
                <select value={investStyle} onChange={(e) => setInvestStyle(e.target.value)}>
                  <option value="장투">장투 (수개월~수년 보유, 단기 과열에 trim 자제)</option>
                  <option value="스윙">스윙 (수주~수개월, RSI/이평 시그널 적극 반영)</option>
                  <option value="단타">단타 (며칠~수주, 기술적 시그널에 민감)</option>
                </select>
              </div>
              <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
                <span style={{ fontSize: 14, color: "var(--ink)" }}>손절선</span>
                <input
                  type="number" min="-50" max="0" step="1"
                  value={stopLossPct}
                  onChange={(e) => setStopLossPct(e.target.value)}
                  style={{ width: 80, height: 36, padding: "0 10px", textAlign: "right" }}
                />
                <span style={{ fontSize: 14, color: "var(--steel)" }}>%</span>
              </div>
            </div>
          </div>
          <div className="field">
            <label>웹 검색 — Claude가 직접 최신 실적·뉴스 조회</label>
            <label style={{ display: "flex", gap: 8, alignItems: "center", textTransform: "none", letterSpacing: 0, fontWeight: 400, color: "var(--ink)" }}>
              <input type="checkbox" checked={useWebSearch} onChange={(e) => setUseWebSearch(e.target.checked)} style={{ width: 16, height: 16 }} />
              사용함 — 환각 위험 크게 감소. 비용 종목당 약 $0.02 추가 (검색 2회 한도)
            </label>
          </div>
          <div className="field">
            <label>자동 갱신</label>
            <label style={{ display: "flex", gap: 8, alignItems: "center", textTransform: "none", letterSpacing: 0, fontWeight: 400, color: "var(--ink)", marginBottom: 6 }}>
              <input type="checkbox" checked={autoFx} onChange={(e) => setAutoFx(e.target.checked)} style={{ width: 16, height: 16 }} />
              페이지 로드 시 환율 자동 조회 (Frankfurter)
            </label>
            <label style={{ display: "flex", gap: 8, alignItems: "center", textTransform: "none", letterSpacing: 0, fontWeight: 400, color: "var(--ink)", marginBottom: 6 }}>
              <input type="checkbox" checked={autoPrices} onChange={(e) => setAutoPrices(e.target.checked)} style={{ width: 16, height: 16 }} />
              페이지 로드 시 주가 자동 조회 (Stooq)
            </label>
            <div style={{ display: "flex", gap: 8, alignItems: "center", marginTop: 8 }}>
              <span style={{ fontSize: 14, color: "var(--ink)" }}>현재가 자동 갱신 주기</span>
              <input
                type="number" min="0" max="60"
                value={autoRefreshMin}
                onChange={(e) => setAutoRefreshMin(e.target.value)}
                style={{ width: 80, height: 36, padding: "0 10px", textAlign: "right" }}
              />
              <span style={{ fontSize: 14, color: "var(--steel)" }}>분 (0 = 사용 안 함, 탭 활성 시에만 동작)</span>
            </div>
          </div>
          {testState.kind !== "idle" && (
            <div className={"field-status " + testState.kind}>{testState.msg}</div>
          )}
          <div className="actions">
            <button type="button" className="btn btn-ghost" onClick={onClear} title="저장된 모든 데이터 삭제">전체 초기화</button>
            <button type="button" className="btn btn-secondary" onClick={test}>연결 테스트</button>
            <button type="button" className="btn btn-ghost" onClick={onClose}>취소</button>
            <button type="submit" className="btn btn-buy">저장</button>
          </div>
        </form>
      </div>
    </div>
  );
}

/* ----- Portfolio Overall Analysis Card ----- */

const PRIORITY_COLOR = { "즉시": "var(--critical)", "단기": "var(--attention)", "중기": "var(--primary)" };
const RISK_COLOR     = { "낮음": "var(--success)", "보통": "var(--attention)", "높음": "var(--critical)", "매우 높음": "var(--critical)" };
const SECTOR_COLOR   = { "과중": "var(--critical)", "적정": "var(--success)", "부족": "var(--steel)" };

function PortfolioAnalysisCard({ analysis, onRun, loading }) {
  if (!analysis) {
    return (
      <div className="pfa-empty">
        <p>포트폴리오 전체 관점의 리밸런싱·섹터 배분·최적화 조언이 여기에 표시됩니다.<br />
          종목이 1개 이상 있고 Claude API 키가 설정된 상태에서 실행하세요.</p>
        <button className="btn btn-buy" onClick={onRun} disabled={loading}>
          {loading ? "분석 중..." : "포트폴리오 종합 분석 실행"}
        </button>
      </div>
    );
  }

  const scoreTone = analysis.score >= 7 ? "var(--success)"
    : analysis.score >= 5 ? "var(--attention)" : "var(--critical)";

  return (
    <div className="pfa-card">
      {/* 상단 헤더 */}
      <div className="pfa-header">
        <div className="pfa-score-block">
          <span className="pfa-score-num" style={{ color: scoreTone }}>{analysis.score}</span>
          <span className="pfa-score-denom">/10</span>
          <div className="pfa-score-comment">{analysis.scoreComment}</div>
        </div>
        <div className="pfa-summary-block">
          <p className="pfa-summary-text">{analysis.summary}</p>
          {analysis.analyzedAt && (
            <div className="pfa-meta">
              Claude · {new Date(analysis.analyzedAt).toLocaleString("ko-KR", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" })} 분석
            </div>
          )}
        </div>
        <button className="btn btn-ghost btn-sm" onClick={onRun} disabled={loading} style={{ flexShrink: 0 }}>
          {loading ? "분석 중..." : "↻ 재분석"}
        </button>
      </div>

      <div className="pfa-grid">
        {/* 집중도 */}
        <div className="pfa-block">
          <div className="pfa-block-head">집중도 현황</div>
          <div className="pfa-conc-row"><span className="pfa-lbl">최대 종목</span><span className="pfa-val">{analysis.concentration?.topHolding} · {analysis.concentration?.topHoldingPct?.toFixed(1)}%</span></div>
          <div className="pfa-conc-row"><span className="pfa-lbl">KR / US / 현금</span><span className="pfa-val">{analysis.concentration?.krPct?.toFixed(1)}% / {analysis.concentration?.usPct?.toFixed(1)}% / {analysis.concentration?.cashPct?.toFixed(1)}%</span></div>
          <div className="pfa-conc-row"><span className="pfa-lbl">집중 리스크</span><span className="pfa-val" style={{ color: RISK_COLOR[analysis.concentration?.risk], fontWeight: 700 }}>{analysis.concentration?.risk}</span></div>
        </div>

        {/* 섹터 배분 */}
        <div className="pfa-block">
          <div className="pfa-block-head">섹터 배분</div>
          {(analysis.sectorBreakdown || []).map((s, i) => (
            <div key={i} className="pfa-sector-row">
              <span className="pfa-sector-name">{s.sector}</span>
              <div className="pfa-sector-bar-wrap">
                <div className="pfa-sector-bar" style={{ width: Math.min(s.pct || 0, 100) + "%", background: SECTOR_COLOR[s.eval] }} />
              </div>
              <span className="pfa-sector-pct">{(s.pct || 0).toFixed(0)}%</span>
              <span className="pfa-sector-eval" style={{ color: SECTOR_COLOR[s.eval] }}>{s.eval}</span>
            </div>
          ))}
        </div>

        {/* 리밸런싱 액션 */}
        <div className="pfa-block pfa-block-full">
          <div className="pfa-block-head">리밸런싱 우선순위</div>
          <div className="pfa-action-list">
            {(analysis.rebalancingActions || []).map((a, i) => (
              <div key={i} className="pfa-action-row">
                <span className="pfa-priority" style={{ color: PRIORITY_COLOR[a.priority] }}>{a.priority}</span>
                <span className="pfa-action-type">{a.action}</span>
                <span className="pfa-action-target">{a.target}</span>
                <span className="pfa-action-reason">{a.reason}</span>
              </div>
            ))}
          </div>
        </div>

        {/* 최적화 제안 */}
        <div className="pfa-block">
          <div className="pfa-block-head">포트폴리오 최적화</div>
          <ul className="pfa-opt-list">
            {(analysis.portfolioOptimization || []).map((opt, i) => <li key={i}>{opt}</li>)}
          </ul>
        </div>

        {/* 강점·약점 */}
        <div className="pfa-block">
          <div className="pfa-block-head">강점 / 약점</div>
          {(analysis.strengthsAndWeaknesses?.strengths || []).map((s, i) => (
            <div key={"s" + i} className="pfa-sw-row strength"><span className="pfa-sw-icon">+</span>{s}</div>
          ))}
          {(analysis.strengthsAndWeaknesses?.weaknesses || []).map((w, i) => (
            <div key={"w" + i} className="pfa-sw-row weakness"><span className="pfa-sw-icon">-</span>{w}</div>
          ))}
        </div>

        {/* 환율·환전 권고 */}
        {analysis.fxRecommendation && (
          <div className="pfa-block">
            <div className="pfa-block-head">환율·환전 분석</div>
            <div className="pfa-conc-row">
              <span className="pfa-lbl">환율 수준</span>
              <span className="pfa-val" style={{ fontWeight: 700, color:
                analysis.fxRecommendation.rateAssessment === "고환율" ? "var(--critical)"
                : analysis.fxRecommendation.rateAssessment === "저환율" ? "var(--success)"
                : "var(--ink)" }}>
                {analysis.fxRecommendation.rateAssessment}
              </span>
            </div>
            <div className="pfa-conc-row">
              <span className="pfa-lbl">환전 권장</span>
              <span className="pfa-val" style={{ fontWeight: 700, color: analysis.fxRecommendation.shouldConvert ? "var(--attention)" : "var(--steel)" }}>
                {analysis.fxRecommendation.shouldConvert ? "권장" : "비권장"}
              </span>
            </div>
            {analysis.fxRecommendation.suggestedAmount && (
              <div className="pfa-conc-row">
                <span className="pfa-lbl">권장 금액</span>
                <span className="pfa-val">{analysis.fxRecommendation.suggestedAmount}</span>
              </div>
            )}
            <p style={{ margin: "6px 0 0", fontSize: 13, color: "var(--ink)", lineHeight: 1.6 }}>
              {analysis.fxRecommendation.recommendation}
            </p>
          </div>
        )}

        {/* 3개 에이전트 합의 */}
        {analysis.agentViews && analysis.agentViews.length > 0 && (
          <div className="pfa-block">
            <div className="pfa-block-head">3개 관점 에이전트 합의</div>
            {analysis.agentConsensus && (
              <div className="pfa-conc-row" style={{ marginBottom: 6 }}>
                <span className="pfa-lbl">합의 수준</span>
                <span className="pfa-val" style={{ fontWeight: 700, color:
                  analysis.agentConsensus === "높음" ? "var(--success)"
                  : analysis.agentConsensus === "낮음" ? "var(--critical)"
                  : "var(--attention)" }}>
                  {analysis.agentConsensus}
                </span>
              </div>
            )}
            {analysis.agentViews.map((av, i) => (
              <div key={i} className="pfa-conc-row">
                <span className="pfa-lbl">{av.perspective}</span>
                <span className="pfa-val">
                  {av.score}/10 · <span style={{ color:
                    av.verdict === "긍정적" ? "var(--success)"
                    : av.verdict === "부정적" ? "var(--critical)"
                    : "var(--steel)" }}>{av.verdict}</span>
                </span>
              </div>
            ))}
          </div>
        )}
      </div>

      {/* 전망 */}
      <div className="pfa-outlook">
        <span className="pfa-outlook-label">3~6개월 전망</span>
        <p>{analysis.outlook}</p>
      </div>
    </div>
  );
}

Object.assign(window, {
  BriefingCard, PortfolioTable, CashBar, SoldRecords, SellModal,
  AddHoldingModal, EditHoldingModal, HoldingFormModal, AlertsPage,
  SettingsModal, PortfolioAnalysisCard,
  fmtKRW, fmtUSD, fmtPct
});
