// Helm — 法說會:搜代號直接看 AI 整理(已整理=免費即看;沒整理=🔖記下來明天清晨排程自動補,不花錢;
//   趕時間才用「現在就查」走付費 API)+ 13 步檢核教材(收合)。照「想看的公司介紹」同一套排程模式。
//   方法論改編自 Anthropic 開源 Earnings Reviewer(Apache-2.0),台股在地化。誠實鐵則:每個重點附來源、
//   查無標「未查證」、舊場次明講、只列訊號不下結論。非投資建議。
(function () {
  const NS = window.HelmDesignSystem_9613a7;
  const { Field, Input, Button } = NS;

  function Item({ n, title, children }) {
    return (
      <div style={{ display: "flex", gap: 10, marginBottom: 12 }}>
        <span className="t-num" style={{ flexShrink: 0, width: 22, height: 22, borderRadius: 11, background: "var(--surface-sunken)", color: "var(--text-secondary)", fontSize: 12.5, display: "flex", alignItems: "center", justifyContent: "center", fontWeight: 700 }}>{n}</span>
        <div style={{ fontSize: 13.5, lineHeight: 1.65 }}>
          <b>{title}</b>
          <div style={{ color: "var(--text-secondary)", marginTop: 2 }}>{children}</div>
        </div>
      </div>
    );
  }
  // 段落渲染:【標題】粗體 + 內文;網址轉可點連結(法說重點的來源要一鍵可達)
  function linkify(text, keyBase) {
    var parts = String(text).split(/(https?:\/\/[^\s)」】]+)/g);
    return parts.map(function (t, i) {
      if (/^https?:\/\//.test(t)) return <a key={keyBase + "-" + i} href={t} target="_blank" rel="noopener noreferrer" style={{ color: "var(--accent-brass, #b9965a)", wordBreak: "break-all" }}>{t.replace(/^https?:\/\/(www\.)?/, "").slice(0, 40)}{t.length > 48 ? "…" : ""}</a>;
      return t;
    });
  }
  function analysisParas(text) {
    text = String(text || "");
    var cut = text.indexOf("【");   // 防禦:剪掉第一個段落標題前的 AI 過場語(舊快取也適用)
    if (cut > 0) text = text.slice(cut);
    return text.split(/\n+/).map(function (l) { return l.trim(); }).filter(Boolean).map(function (line, i) {
      var mm = line.match(/^【(.+?)】([\s\S]*)$/);
      if (mm) return <p key={i} className="stk-ai__sec" style={{ marginTop: i ? 10 : 0 }}><b>{mm[1]}</b>{mm[2] ? <span style={{ display: "block", marginTop: 2, fontWeight: 400 }}>{linkify(mm[2].trim(), "l" + i)}</span> : null}</p>;
      return <p key={i} className="stk-ai__foot">{linkify(line, "f" + i)}</p>;
    });
  }
  function estCost(u) {
    if (!u || u.input_tokens == null) return "";
    var nt = (u.input_tokens / 1e6 * 3 + (u.output_tokens || 0) / 1e6 * 15) * 31.5;
    return nt < 0.5 ? "<NT$0.5" : "約 NT$" + Math.round(nt);
  }
  function inferMarket(code) { return /^\d{4,6}[A-Z]?$/.test(String(code).trim().toUpperCase()) ? "tw" : "us"; }
  function dstr(ts) { return ts ? new Date(ts).toLocaleDateString("zh-TW") : ""; }
  function daysOld(ts) { return ts ? Math.floor((Date.now() - ts) / 86400000) : 0; }

  function EarningsScreen({ onClose, initialCode }) {
    const HD = window.HelmData || {};
    const DEMO = !!HD.DEMO;
    const canQuery = !!HD.earningsAI;
    const canWish = !!HD.addEarnWish;
    const [code, setCode] = React.useState("");
    const [phase, setPhase] = React.useState("idle");   // idle | checking | none | metrics | ai | done | error
    const [errMsg, setErrMsg] = React.useState("");
    const [result, setResult] = React.useState(null);   // {analysis, ts, src, cached, usage, name, code, market}
    const [covered, setCovered] = React.useState(null); // 已整理清單(null=載入中)
    const [wishes, setWishes] = React.useState(null);   // 排隊中清單
    const [wished, setWished] = React.useState(false);  // 目前這檔剛按過「記下來」
    const [guideOpen, setGuideOpen] = React.useState(false);   // 13 步教材(預設收合,查詢是主角)

    React.useEffect(function () {
      if (initialCode) lookup(initialCode);   // 從個股健檢跳進來:直接查這檔(有整理→即看;沒有→記下來流程)
      if (HD.listEarnings) HD.listEarnings().then(function (d) { setCovered(d || []); }).catch(function () { setCovered([]); });
      else setCovered([]);
      if (HD.listEarnWish) HD.listEarnWish().then(function (d) { setWishes(d || []); }).catch(function () { setWishes([]); });
      else setWishes([]);
    }, []);

    function nameOf(c) {
      var hit = (covered || []).find(function (x) { return x.code === c; });
      return (hit && hit.name) || "";
    }

    function lookup(cRaw, mkKnown) {   // 免費查快取:整理好了就直接看,絕不觸發付費查詢
      var c = String(cRaw).trim().toUpperCase();
      if (!c || !canQuery) return;
      setCode(c); setWished(false); setResult(null); setErrMsg("");
      var market = mkKnown || inferMarket(c);
      setPhase("checking");
      HD.earningsAI({ code: c, market: market, checkOnly: true }).then(function (res) {
        if (res && res.analysis) { setResult({ analysis: res.analysis, ts: res.ts, src: res.src, next: res.next || "", cached: true, code: c, market: market, name: nameOf(c) }); setPhase("done"); }
        else setPhase("none");
      }).catch(function () { setPhase("none"); });
    }

    function wish() {   // 記下來 → 明天清晨排程用訂閱自動整理(不花錢;同「想看的公司介紹」)
      if (!canWish) return;
      var c = String(code).trim().toUpperCase();
      var market = inferMarket(c);
      setWished(true);   // 樂觀更新
      setWishes(function (l) { return (l || []).some(function (x) { return x.code === c; }) ? l : (l || []).concat([{ code: c, market: market, addedAt: "今天" }]); });
      HD.addEarnWish({ code: c, name: nameOf(c), market: market });
    }

    function unwish(c) {
      if (!HD.removeEarnWish) return;
      setWishes(function (l) { return (l || []).filter(function (x) { return x.code !== c; }); });
      HD.removeEarnWish(c);
    }

    function metricsSummary(m, market) {
      if (!m) return "";
      var f = function (x) { return x == null ? "—" : ((x > 0 ? "+" : "") + x + "%"); };
      if (market === "tw") {
        return "名稱 " + (m.name || "") + ";近12月營收年增 " + f(m.revTtmYoY) + "、最新單月營收年增 " + f(m.revYoY) +
          ";本業(營業利益)TTM年增 " + f(m.opYoY) + ";TTM EPS 年增 " + f(m.epsYoY) +
          ";毛利率 " + (m.gross != null ? m.gross + "%" : "—") + "、營益率 " + (m.opm != null ? m.opm + "%" : "—") +
          ";財報截至 " + (m.lq || "");
      }
      return "名稱 " + (m.name || "") + ";年報營收年增 " + f(m.revYoY) + "、EPS 年增 " + f(m.epsYoY) +
        ";毛利率 " + (m.gross != null ? m.gross + "%" : "—") + ";年報截至 " + (m.asOf || "");
    }

    function runInstant(fresh) {   // 付費即查:趕時間才用;先抓 App 官方數字當對照再讓 AI 上網查證
      var c = String(code).trim().toUpperCase();
      if (!c) return;
      var market = inferMarket(c);
      setPhase("metrics"); setErrMsg(""); setResult(null);
      var getM = market === "tw"
        ? (window.HelmStockData ? window.HelmStockData.buildMetrics(c) : Promise.resolve(null))
        : (window.HelmStockData ? window.HelmStockData.buildUsMetrics(c) : Promise.resolve(null));
      getM.catch(function () { return null; }).then(function (r) {
        var m = r && r.metrics ? r.metrics : null;
        var summary = "";
        try { summary = metricsSummary(m, market); } catch (e) {}
        setPhase("ai");
        HD.earningsAI({ code: c, name: (m && m.name) || "", market: market, summary: summary, fresh: !!fresh }).then(function (res) {
          if (res && res.analysis) {
            setResult({ analysis: res.analysis, ts: res.ts, src: res.src || "instant", cached: !!res.cached, usage: res.usage, name: (m && m.name) || nameOf(c) || c, code: c, market: market });
            setPhase("done");
            setCovered(function (l) {   // 本地同步:剛查好的進「已整理」清單
              var rest = (l || []).filter(function (x) { return x.code !== c; });
              return [{ code: c, market: market, name: (m && m.name) || "", ts: Date.now(), src: "instant" }].concat(rest);
            });
            setWishes(function (l) { return (l || []).filter(function (x) { return x.code !== c; }); });
          }
          else {
            setPhase("error");
            setErrMsg(res && res.error === "no_key" ? "後端還沒設定 AI 金鑰(GAS 指令碼屬性 ANTHROPIC_API_KEY)。" : res && res.detail ? String(res.detail).slice(0, 120) : "查詢失敗,稍後再試。");
          }
        });
      });
    }

    var srcLabel = result && (result.src === "schedule" ? "每日排程整理" : "即時查證");

    return (
      <div className="fpage" role="dialog" aria-modal="true" aria-label="法說會">
        <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">法說會</span>
            <span aria-hidden="true" />
          </header>
          <div className="fpage__scroll">
            <div className="fpage__body">

              {canQuery && (
                <section className="fpage__card">
                  <div className="fpage__card-head"><span className="t-overline">搜代號看最新法說整理</span><span className="fpage__card-hint">AI 整理 · 附來源</span></div>
                  <Field label="股票代號" hint={canWish ? "整理好的直接看;還沒整理的按「記下來」,明天清晨自動整理好(不花錢)" : "台股數字(2330)、美股英文(AAPL)"}>
                    <Input placeholder="例:2330" value={code}
                      onChange={function (e) { setCode(e.target.value); setPhase("idle"); }}
                      onKeyDown={function (e) { if (e.key === "Enter") lookup(code); }} />
                  </Field>
                  {phase === "idle" && (
                    <Button variant="primary" size="sm" onClick={function () { lookup(code); }} disabled={!String(code).trim()}>查這檔</Button>
                  )}
                  {phase === "checking" && <div className="fx-now"><span className="fx-now__unit">查整理檔中</span><span className="fx-now__rate">…</span></div>}
                  {phase === "none" && (
                    <div className="stk-ai__confirm">
                      <p className="stk-ai__warn" style={{ marginBottom: 8 }}>{String(code).trim().toUpperCase()} 還沒整理過。</p>
                      {wished ? (
                        <p className="stk-ai__warn" style={{ color: "var(--status-success)" }}>✓ 已排入!明天清晨會自動整理好——到時候搜這個代號就看得到,不花錢。</p>
                      ) : (
                        <div className="stk-ai__btns" style={{ flexWrap: "wrap", gap: 8 }}>
                          {canWish && (
                            <button type="button" className="stk-ai__go" onClick={wish}><i className="ph ph-bookmark-simple" aria-hidden="true" /> 記下來,明天清晨自動整理(不花錢)</button>
                          )}
                          <button type="button" className={canWish ? "stk-ai__cancel" : "stk-ai__go"} onClick={function () { runInstant(false); }}>
                            {DEMO ? "現在就查(展示版:回示範內容、不花錢)" : "趕時間?現在就查 · 約 10~25 秒 · 約 NT$3~10"}
                          </button>
                        </div>
                      )}
                    </div>
                  )}
                  {phase === "metrics" && <div className="fx-now"><span className="fx-now__unit">先抓官方財報數字當對照…</span><span className="fx-now__rate">…</span></div>}
                  {phase === "ai" && <div className="fx-now"><span className="fx-now__unit">AI 上網查證法說會中(約 10~25 秒)</span><span className="fx-now__rate">…</span></div>}
                  {phase === "error" && <p className="stk-ai__warn" style={{ marginTop: 8 }}>⚠️ {errMsg}</p>}
                  {phase === "done" && result && (
                    <div style={{ marginTop: 8 }}>
                      <div className="fpage__card-head" style={{ marginBottom: 6 }}>
                        <span className="t-overline">{result.name || result.code}<span className="t-num" style={{ marginLeft: 6, color: "var(--text-tertiary)" }}>{result.code}</span></span>
                        <span className="fpage__card-hint">{result.cached ? "整理於 " + dstr(result.ts) + " · " + srcLabel : (result.usage && estCost(result.usage) ? "本次 " + estCost(result.usage) : "")}</span>
                      </div>
                      {result.cached && (function () {
                        var nx = (window.HelmStockData && window.HelmStockData.earnNextStale) ? window.HelmStockData.earnNextStale(result) : null;
                        if (nx) return <p className="stk-ai__warn" style={{ marginBottom: 8 }}>⚠️ 這份整理之後,下一場({nx.slice(5)})已經開過——排程會優先翻新;急著看用「重新查證」。</p>;
                        var dlAfter = (result.market !== "us" && window.HelmStockData && window.HelmStockData.twReportDeadlineAfter) ? window.HelmStockData.twReportDeadlineAfter(result.ts) : null;
                        if (dlAfter) {
                          var d = new Date(dlAfter);
                          return <p className="stk-ai__warn" style={{ marginBottom: 8 }}>⚠️ 這份整理之後,已有新一季財報公布({(d.getMonth() + 1) + "/" + d.getDate()} 截止)——多半也開過新場次。排程會自動翻新;急著看用「重新查證」。</p>;
                        }
                        if (daysOld(result.ts) > 70) return <p className="stk-ai__warn" style={{ marginBottom: 8 }}>這份整理已超過一季({daysOld(result.ts)} 天前),可能已有新一場法說。</p>;
                        return null;
                      })()}
                      <div className="stk-ai__body">{analysisParas(result.analysis)}</div>
                      {result.cached && (
                        <div className="stk-ai__btns" style={{ marginTop: 8, flexWrap: "wrap", gap: 8 }}>
                          {canWish && !wished && (
                            <button type="button" className="stk-prof__refresh" onClick={wish}>排明天自動重新整理(不花錢)</button>
                          )}
                          {wished && <span className="stk-ai__warn" style={{ color: "var(--status-success)" }}>✓ 已排入明天清晨</span>}
                          <button type="button" className="stk-prof__refresh" onClick={function () { runInstant(true); }}>{DEMO ? "重新查證(展示版不花錢)" : "現在重新查證(約 NT$3~10)"}</button>
                        </div>
                      )}
                      <p className="t-caption" style={{ marginTop: 8 }}>AI 整理、附來源連結;請以官方原文為準。只列訊號、不下買賣結論——判斷是你的。非投資建議。</p>
                    </div>
                  )}

                  {covered && covered.length > 0 && (
                    <div style={{ marginTop: 14 }}>
                      <div className="fpage__card-head"><span className="t-overline">已整理好(點了就看,免費)</span></div>
                      {covered.slice(0, 12).map(function (it) {
                        var old = daysOld(it.ts) > 70;
                        return (
                          <button key={it.market + it.code} type="button" onClick={function () { lookup(it.code, it.market); }}
                            style={{ display: "flex", alignItems: "baseline", gap: 8, width: "100%", textAlign: "left", background: "none", border: "none", cursor: "pointer", padding: "9px 0", borderTop: "1px solid rgba(128,128,128,0.18)", color: "inherit", font: "inherit" }}>
                            <span style={{ fontWeight: 600 }}>{it.name || it.code}</span>
                            <span className="t-num" style={{ fontSize: 12, opacity: 0.55 }}>{it.name ? it.code : ""}{it.market === "us" ? " · 美股" : ""}</span>
                            <span style={{ marginLeft: "auto", fontSize: 12, opacity: 0.6, flexShrink: 0 }}>{dstr(it.ts)}{old ? " · 舊" : ""}</span>
                          </button>
                        );
                      })}
                      {covered.length > 12 && <p className="cc-note" style={{ marginTop: 4 }}>還有 {covered.length - 12} 檔,搜代號就能看。</p>}
                    </div>
                  )}

                  {wishes && wishes.length > 0 && (
                    <div style={{ marginTop: 14 }}>
                      <div className="fpage__card-head"><span className="t-overline">排隊中 · 明天清晨自動整理</span></div>
                      {wishes.map(function (it) {
                        return (
                          <div key={"w" + it.code} style={{ display: "flex", alignItems: "center", gap: 8, padding: "7px 0", borderTop: "1px solid rgba(128,128,128,0.18)" }}>
                            <span style={{ fontWeight: 600 }}>{it.name || it.code}</span>
                            {it.name ? <span className="t-num" style={{ fontSize: 12, opacity: 0.55 }}>{it.code}</span> : null}
                            <button type="button" onClick={function () { unwish(it.code); }} aria-label="移除"
                              style={{ marginLeft: "auto", background: "none", border: "none", cursor: "pointer", opacity: 0.5, fontSize: 16, padding: 4, color: "inherit" }}>
                              <i className="ph ph-x" aria-hidden="true" />
                            </button>
                          </div>
                        );
                      })}
                    </div>
                  )}
                  {DEMO && <p className="cc-note" style={{ marginTop: 10 }}>展示版:內容為示範、記下來僅演示流程,不會真的排程。</p>}
                </section>
              )}

              <section className="fpage__card">
                <div className="fpage__card-head"><span className="t-overline">30 秒版</span></div>
                <div className="fire-desc">
                  <p>法說會是你每季「重新面試」持股的機會。重點永遠不是「賺多少」,而是三個問題:<b>①跟公司自己說過的話比,做到了沒?②為什麼?③我當初買它的理由,還成立嗎?</b></p>
                </div>
                <button type="button" style={{ padding: "6px 0 0", background: "none", border: "none", font: "inherit", fontSize: 12.5, color: "var(--text-tertiary)", cursor: "pointer" }}
                  onClick={function () { setGuideOpen(!guideOpen); }}>{guideOpen ? "收起教材 ▴" : "完整 13 步檢核教材 ▾"}</button>
              </section>

              {guideOpen && (
                <React.Fragment>
                  <section className="fpage__card">
                    <div className="fpage__card-head"><span className="t-overline">第 0 步 · 先防呆</span></div>
                    <Item n="1" title="確認是最新一季">搜尋引擎常撈到舊法說會。先寫下今天日期,對財報/法說日期——超過 3 個月就是舊聞,別當新料讀。</Item>
                    <Item n="2" title="台股獨有優勢:月營收">台股每月 10 日前公布上月營收(美股沒有)。法說會之間的空窗,月營收就是你的先行指標——連兩三個月偏離管理層說法,就是提早的警訊。</Item>
                  </section>

                  <section className="fpage__card">
                    <div className="fpage__card-head"><span className="t-overline">看數字 · 四件事</span></div>
                    <Item n="3" title="跟「公司自己的財測」比,量化寫下來">美股有分析師共識可比,台股中小型股沒有——誠實的替代:跟公司上季說過的目標比。差多少寫成「金額+百分比」,不准只寫「不錯」。</Item>
                    <Item n="4" title="拆解「為什麼」">達標或沒達標,原因是哪個產品線/地區/客戶?合併報表附註本來就有分部揭露。停在「達標了」三個字,等於沒讀。</Item>
                    <Item n="5" title="毛利率、營益率的趨勢與原因">是漲價、產品組合變好、成本下降,還是規模攤提?「賺更多」和「賺得更健康」是兩回事。</Item>
                    <Item n="6" title="財測「舊 → 新」並排">公司調整展望時,把舊說法和新說法並排寫,再寫「為什麼變」。只抄新數字會忘記它曾經說過什麼。</Item>
                  </section>

                  <section className="fpage__card">
                    <div className="fpage__card-head"><span className="t-overline">讀人 · 三件事</span></div>
                    <Item n="7" title="這家公司的財測「前科」">它歷來是「保守說、然後超標」還是「喊很大、常跳票」?財測的可信度要用歷史驗,不是用口氣猜。</Item>
                    <Item n="8" title="Q&A 裡被打太極的問題">分析師追問、管理層閃躲的那幾題,常常就是最該注意的地方。台股常只有影音回放沒有逐字稿(公開資訊觀測站可看),費工但值得。</Item>
                    <Item n="9" title="沒給財測就明講「沒給」">台股很多公司常年不開財測。沒說不等於沒事,但也不准自己腦補一個——寫「公司未提供」就好。</Item>
                  </section>

                  <section className="fpage__card">
                    <div className="fpage__card-head"><span className="t-overline">該不該續抱 · 收尾四步(核心)</span></div>
                    <Item n="10" title="修正幅度門檻:5%">這季新資訊讓你對它的獲利估計變動超過 5%,就值得認真重想一次;5% 以內多半是雜訊,別自己嚇自己。</Item>
                    <Item n="11" title="三角驗證">基本面方向(轉好/轉壞)× 股價已經反應了多少 × 風險報酬還划不划算——三個一起看。基本面變好但股價漲更多,不一定該加碼。</Item>
                    <Item n="12" title="論點逐條覆核(最防自欺的一條)">把你當初買進的每個理由攤開,逐條標「強化/不變/減弱」並寫證據。鐵則:<b>找反面證據要跟找正面證據一樣認真</b>。</Item>
                    <Item n="13" title="風險清單更新">有沒有新風險浮現?舊風險惡化還是緩解了?每季更新,而不是出事才想起來。</Item>
                  </section>

                  <section className="fpage__card">
                    <div className="fpage__card-head"><span className="t-overline">三條誠實原則</span></div>
                    <div className="fire-desc">
                      <p><b>1. 查無來源就標「未查證」</b>——每個數字要能說出從哪來;查不到就標明,不拿猜的冒充查過的。</p>
                      <p><b>2. 公司口徑是公關語言</b>——新聞稿和法說會簡報是公司「想讓你看到的樣子」,帶著懷疑心讀,去附註和數字裡找它沒強調的事。</p>
                      <p><b>3. 不用搶快</b>——搶 24 小時內做判斷是券商文化,不是你的。法說會後找個週末安靜讀完再想,比當天跟著盤面情緒做決定好得多。</p>
                    </div>
                  </section>

                  <section className="fpage__card">
                    <div className="fpage__card-head"><span className="t-overline">去哪看</span></div>
                    <div className="fire-desc">
                      <p><b>台股</b>:公開資訊觀測站 → 法人說明會專區(簡報 PDF + 影音回放);月營收同站「營運概況」。</p>
                      <p><b>美股</b>:公司 IR 網站(新聞稿+逐字稿)、SEC EDGAR(正式申報)。逐字稿文化完整,比台股好讀。</p>
                      <p style={{ color: "var(--text-tertiary)" }}>搭配本 App:個股健檢看體質數字、「記下來」讓側寫隔天自動補上業務與護城河。</p>
                    </div>
                  </section>
                </React.Fragment>
              )}

              <p className="t-caption" style={{ padding: "0 4px" }}>教材、非投資建議;引用之方法論源自 Apache-2.0 開源專案,經在地化改編,與 Anthropic 官方無關。</p>
            </div>
          </div>
        </div>
      </div>
    );
  }

  window.EarningsScreen = EarningsScreen;
})();
