// Helm — 估值試算 · DCF(現金流折現)。兩段式:5 年顯式期 + 永續終值。純前端、欄位跨裝置記憶(HelmPrefs helm-dcf-*)。
//   定位(2026-07 使用者明確要求加入):進階教材——DCF 的價值是「逼你把假設寫出來」,敏感度表才是主角,
//   不是報明牌;假設動 1%,答案動 20-30% 是常態。非投資建議。
(function () {
  const NS = window.HelmDesignSystem_9613a7;
  const { Field, Input } = NS;
  function num(v) { return parseFloat(String(v).replace(/,/g, "")) || 0; }
  function lsGet(k) { return window.HelmPrefs ? window.HelmPrefs.get(k) : (function () { try { return localStorage.getItem(k); } catch (e) { return null; } })(); }
  function lsSet(k, v) { if (window.HelmPrefs) window.HelmPrefs.set(k, v); else { try { localStorage.setItem(k, v); } catch (e) {} } }
  function lsDel(k) { if (window.HelmPrefs) window.HelmPrefs.del(k); else { try { localStorage.removeItem(k); } catch (e) {} } }

  const K = { fcf: "helm-dcf-fcf", g1: "helm-dcf-g1", g2: "helm-dcf-g2", r: "helm-dcf-r", shares: "helm-dcf-shares", debt: "helm-dcf-debt", price: "helm-dcf-price" };

  // 兩段式 DCF:回 { ev, tvShare, perShare } (單位:億 / 億股 → 元/股);r<=g2 回 null(數學上會爆炸)
  function dcf(fcf0, g1, g2, r, shares, netDebt) {
    if (!(fcf0 > 0) || !(shares > 0) || r <= g2) return null;
    const R = r / 100, G1 = g1 / 100, G2 = g2 / 100;
    let pv = 0, f = fcf0;
    for (let t = 1; t <= 5; t++) { f = f * (1 + G1); pv += f / Math.pow(1 + R, t); }
    const tv = (f * (1 + G2)) / (R - G2);
    const tvPv = tv / Math.pow(1 + R, 5);
    const ev = pv + tvPv;
    const eq = ev - (netDebt || 0);
    return { ev: ev, tvShare: tvPv / ev, perShare: eq / shares };
  }
  function p1(n) { return n == null ? "—" : (Math.round(n * 10) / 10).toLocaleString("en-US"); }

  function DcfScreen({ onClose }) {
    const [fcf, setFcf] = React.useState(function () { return lsGet(K.fcf) || ""; });
    const [g1, setG1] = React.useState(function () { return lsGet(K.g1) || "10"; });
    const [g2, setG2] = React.useState(function () { return lsGet(K.g2) || "2.5"; });
    const [r, setR] = React.useState(function () { return lsGet(K.r) || "10"; });
    const [shares, setShares] = React.useState(function () { return lsGet(K.shares) || ""; });
    const [debt, setDebt] = React.useState(function () { return lsGet(K.debt) || "0"; });
    const [price, setPrice] = React.useState(function () { return lsGet(K.price) || ""; });
    function persist(setter, key) { return function (e) { const v = e.target.value; setter(v); lsSet(key, v); }; }
    function resetAll() { Object.keys(K).forEach(function (k) { lsDel(K[k]); }); setFcf(""); setG1("10"); setG2("2.5"); setR("10"); setShares(""); setDebt("0"); setPrice(""); }

    const F = num(fcf), G1v = num(g1), G2v = num(g2), Rv = num(r), SH = num(shares), ND = num(debt), PX = num(price);
    const ready = F > 0 && SH > 0;
    const badR = ready && Rv <= G2v;
    const res = ready && !badR ? dcf(F, G1v, G2v, Rv, SH, ND) : null;
    const margin = res && PX > 0 ? (res.perShare - PX) / res.perShare * 100 : null;
    const g2High = G2v > 3.5;

    // 敏感度矩陣:折現率 ±2%(列)× 永續成長 ±1%(欄)
    const rAxis = [Rv - 2, Rv - 1, Rv, Rv + 1, Rv + 2];
    const gAxis = [G2v - 1, G2v - 0.5, G2v, G2v + 0.5, G2v + 1];

    return (
      <div className="fpage" role="dialog" aria-modal="true" aria-label="估值試算 DCF">
        <div className="fpage__panel">
          <header className="fpage__bar">
            <button className="fpage__cancel" onClick={onClose}><i className="ph ph-arrow-left" aria-hidden="true" />返回</button>
            <span className="fpage__title">估值試算 · DCF</span>
            <span aria-hidden="true" />
          </header>
          <div className="fpage__scroll">
            <div className="fpage__body">

              <section className="fpage__card">
                <div className="fpage__card-head"><span className="t-overline">依你的假設,這家公司每股值</span></div>
                {res ? (
                  <React.Fragment>
                    <div className="fx-now"><span className="fx-now__unit">每股內在價值</span><span className="fx-now__rate t-num">{p1(res.perShare)} 元</span></div>
                    {margin != null && (
                      <div className="goal__nums">
                        <span className="t-num">現價 {p1(PX)} 元</span>
                        <span className="goal__pct t-num" style={{ color: margin > 0 ? "var(--status-success)" : "var(--status-error)" }}>
                          {margin > 0 ? "低於估值 " + p1(margin) + "%(有安全邊際)" : "高於估值 " + p1(-margin) + "%(在為樂觀付費)"}
                        </span>
                      </div>
                    )}
                    <div className="tax-break">
                      <div className="tax-row"><span>5 年現金流折現(顯式期)</span><span className="t-num">{p1(res.ev * (1 - res.tvShare))} 億</span></div>
                      <div className="tax-row"><span>永續終值折現(第 6 年起到永遠)</span><span className="t-num">{p1(res.ev * res.tvShare)} 億</span></div>
                      <div className="tax-row tax-row--sum"><span>企業價值 − 淨負債 ÷ 股數</span><span className="t-num">{p1(res.perShare)} 元/股</span></div>
                    </div>
                    {res.tvShare > 0.75 && (
                      <p className="t-caption" style={{ marginTop: 8, color: "var(--status-error)" }}>
                        ⚠ 終值佔估值 {Math.round(res.tvShare * 100)}%——你的答案幾乎全押在「第 6 年以後到永遠」的想像上,顯式期的預測反而無關緊要。這通常代表成長假設或折現率需要重想。
                      </p>
                    )}
                  </React.Fragment>
                ) : badR ? (
                  <p className="t-caption" style={{ color: "var(--status-error)" }}>折現率必須大於永續成長率(否則公式會算出無限大——現實不存在永遠比報酬率長得快的公司)。</p>
                ) : (
                  <p className="t-caption">填好下面的「自由現金流」和「股數」就會開始算。</p>
                )}
                {g2High && <p className="t-caption" style={{ marginTop: 6, color: "var(--status-error)" }}>⚠ 永續成長 {p1(G2v)}% 超過長期 GDP+通膨(約 3~3.5%)——等於假設這家公司「永遠」跑贏整個經濟體,那是在騙自己。</p>}
              </section>

              <section className="fpage__card">
                <div className="fpage__card-head"><span className="t-overline">敏感度表(這張才是主角)</span><span className="fpage__card-hint">元/股</span></div>
                {res ? (
                  <React.Fragment>
                    <div style={{ overflowX: "auto", margin: "4px 0" }}>
                      <table style={{ borderCollapse: "collapse", width: "100%", fontSize: 12.5, minWidth: 300 }}>
                        <thead>
                          <tr>
                            <th style={{ textAlign: "left", padding: "4px 6px", color: "var(--text-tertiary)", fontWeight: 500 }}>折現率＼永續成長</th>
                            {gAxis.map(function (g) { return <th key={"g" + g} className="t-num" style={{ padding: "4px 6px", color: g === G2v ? "var(--text-primary)" : "var(--text-tertiary)", fontWeight: g === G2v ? 700 : 500 }}>{p1(g)}%</th>; })}
                          </tr>
                        </thead>
                        <tbody>
                          {rAxis.map(function (rr) {
                            return (
                              <tr key={"r" + rr}>
                                <td className="t-num" style={{ padding: "4px 6px", color: rr === Rv ? "var(--text-primary)" : "var(--text-tertiary)", fontWeight: rr === Rv ? 700 : 400 }}>{p1(rr)}%</td>
                                {gAxis.map(function (gg) {
                                  const c = dcf(F, G1v, gg, rr, SH, ND);
                                  const center = rr === Rv && gg === G2v;
                                  return <td key={"c" + rr + "_" + gg} className="t-num" style={{ padding: "4px 6px", textAlign: "center", fontWeight: center ? 700 : 400, background: center ? "var(--surface-sunken)" : "transparent", borderRadius: center ? 6 : 0 }}>{c ? p1(c.perShare) : "—"}</td>;
                                })}
                              </tr>
                            );
                          })}
                        </tbody>
                      </table>
                    </div>
                    <p className="t-caption" style={{ lineHeight: 1.6 }}>同一家公司、同一份現金流,只是折現率 ±2%、永續成長 ±1%,答案就是這整片範圍。<b>DCF 給的不是「一個價格」,是「一片假設的地圖」</b>——現價落在地圖哪裡,比中間那格數字更有意義。</p>
                  </React.Fragment>
                ) : (
                  <p className="t-caption">填好假設後,這裡會顯示 5×5 的假設組合矩陣。</p>
                )}
              </section>

              <section className="fpage__card">
                <div className="fpage__card-head"><span className="t-overline">你的假設</span><button type="button" className="fpage__cancel" style={{ fontSize: 12.5 }} onClick={resetAll}>重設</button></div>
                <div className="fpage__fields">
                  <Field label="最近一年自由現金流(億)" hint="營業現金流 − 資本支出。台股:個股健檢一鍵帶入/公開資訊觀測站;美股:年報(10-K,美國上市公司的法定年度報告)裡的現金流量表,公司 IR 網站免費下載。景氣循環股建議用 3~5 年平均,別用最好或最壞的一年">
                    <Input inputMode="decimal" placeholder="例:1500" value={fcf} onChange={persist(setFcf, K.fcf)} />
                  </Field>
                  <Field label="前 5 年成長率(%/年)" hint="未來 5 年 FCF 平均年增。參考過去 5 年實績再往保守調——分析師與你都傾向抄「興奮期」的數字">
                    <Input inputMode="decimal" placeholder="例:10" value={g1} onChange={persist(setG1, K.g1)} />
                  </Field>
                  <Field label="永續成長率(%/年)" hint="第 6 年起「到永遠」的成長。上限=長期 GDP+通膨(約 2~3%);再高就是假設它永遠跑贏全經濟">
                    <Input inputMode="decimal" placeholder="例:2.5" value={g2} onChange={persist(setG2, K.g2)} />
                  </Field>
                  <Field label="折現率(%/年)" hint="= 你要求的年化報酬。越高越保守、估值越低。參考帶:大型穩定股 7~9%、成長股 9~12%、高風險 12~15%;不確定就用 10% 再看敏感度表">
                    <Input inputMode="decimal" placeholder="例:10" value={r} onChange={persist(setR, K.r)} />
                  </Field>
                  <Field label="流通股數(億股)" hint="台股:公開資訊觀測站/看盤軟體「股本」÷10 就是億股(面額 10 元);美股:10-K 的 shares outstanding">
                    <Input inputMode="decimal" placeholder="例:259.3(台積電)" value={shares} onChange={persist(setShares, K.shares)} />
                  </Field>
                  <Field label="淨負債(億,可填負數)" hint="有息負債 − 現金。現金比負債多就填負數(等於加分);抓不到先填 0,影響通常不大">
                    <Input inputMode="decimal" placeholder="例:0" value={debt} onChange={persist(setDebt, K.debt)} />
                  </Field>
                  <Field label="現在股價(元,選填)" hint="填了就幫你算安全邊際(估值和現價差多少)">
                    <Input inputMode="decimal" placeholder="例:1000" value={price} onChange={persist(setPrice, K.price)} />
                  </Field>
                </div>
              </section>

              <section className="fpage__card">
                <div className="fpage__card-head"><span className="t-overline">DCF 的三個經典陷阱</span></div>
                <div className="fire-desc">
                  <p><b>1. 終值獨裁</b>:上面「終值佔比」超過 7 成時,你其實是在為「第 6 年以後的永遠」定價,前 5 年算得再細都是儀式。</p>
                  <p><b>2. 成長率抄自興奮期</b>:大漲後估成長率,人會不自覺往樂觀抄。解法:抄公司過去 5~10 年「實際」數字,再砍一點。</p>
                  <p><b>3. 倒著算</b>:先有想買的結論,再調折現率湊出「便宜」。解法:先寫死你的折現率(你要求的報酬),永遠不為了結論回頭改它。</p>
                  <p style={{ marginTop: 8 }}>誠實定位:DCF 最大的價值是<b>逼你把「憑感覺」變成「寫下來的假設」</b>——寫下來,才有得檢討。它算出的不是真理,是你信念的價格。</p>
                </div>
                <p className="t-caption" style={{ marginTop: 8 }}>教材與試算工具,非投資建議;數字品質取決於你的輸入。</p>
              </section>

            </div>
          </div>
        </div>
      </div>
    );
  }

  window.DcfScreen = DcfScreen;
})();
