/* 聆譯 — shared components & hooks */
const { useState, useEffect, useRef } = React;
const DATA = window.APP_DATA;

/* ---------------- icons ---------------- */
const I = {
  mic: <path d="M12 2a3 3 0 0 0-3 3v6a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Zm7 9a7 7 0 0 1-14 0M12 18v4" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>,
  tab: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="4" width="18" height="14" rx="2"/><path d="M3 8h18M8 21h8"/></g>,
  speaker: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 9v6h4l5 4V5L8 9H4Z"/><path d="M16 9a3 3 0 0 1 0 6M18.5 6.5a7 7 0 0 1 0 11"/></g>,
  speakerOff: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 9v6h4l5 4V5L8 9H4Z"/><path d="M22 9l-5 5M17 9l5 5"/></g>,
  power: <g fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 3v9"/><path d="M6.5 6.5a8 8 0 1 0 11 0"/></g>,
  stop: <rect x="6" y="6" width="12" height="12" rx="3" fill="currentColor"/>,
  swap: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M7 4 4 7l3 3"/><path d="M4 7h11a4 4 0 0 1 0 8h-1"/></g>,
  flip: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 3v18"/><path d="M7 8 4 11l3 3M17 16l3-3-3-3"/></g>,
  search: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></g>,
  check: <path d="m5 12 5 5 9-10" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"/>,
  caretDown: <path d="m6 9 6 6 6-6" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>,
  user: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="8" r="3.5"/><path d="M5 20a7 7 0 0 1 14 0"/></g>,
  chat: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 5h16v10H9l-4 4V5Z"/></g>,
  ear: <path d="M9 22a3 3 0 0 1-3-3c0-2-2-3-2-7a8 8 0 0 1 16 0c0 3-2 4-4 4-1 0-2 .5-2 2a3 3 0 0 1-3 3" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>,
  gear: <g fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 13a1.7 1.7 0 0 0 .34 1.87l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.7 1.7 0 0 0-1.87-.34 1.7 1.7 0 0 0-1 1.56V21a2 2 0 0 1-4 0v-.09a1.7 1.7 0 0 0-1.11-1.56 1.7 1.7 0 0 0-1.87.34l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.7 1.7 0 0 0 .34-1.87 1.7 1.7 0 0 0-1.56-1H3a2 2 0 0 1 0-4h.09a1.7 1.7 0 0 0 1.56-1.11 1.7 1.7 0 0 0-.34-1.87l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.7 1.7 0 0 0 1.87.34H9a1.7 1.7 0 0 0 1-1.56V3a2 2 0 0 1 4 0v.09a1.7 1.7 0 0 0 1 1.56 1.7 1.7 0 0 0 1.87-.34l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.7 1.7 0 0 0-.34 1.87V9a1.7 1.7 0 0 0 1.56 1H21a2 2 0 0 1 0 4h-.09a1.7 1.7 0 0 0-1.51 1Z"/></g>,
  back: <path d="m15 5-7 7 7 7" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/>,
  close: <path d="M6 6l12 12M18 6 6 18" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round"/>,
  chevR: <path d="m9 5 7 7-7 7" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>,
  headphone: <path d="M4 14v-2a8 8 0 0 1 16 0v2M4 14a2 2 0 0 1 2 2v2a2 2 0 0 1-4 0v-2a2 2 0 0 1 2-2Zm16 0a2 2 0 0 1 2 2v2a2 2 0 0 1-4 0v-2a2 2 0 0 1 2-2Z" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>,
  upload: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 16V4M8 8l4-4 4 4"/><path d="M4 16v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/></g>,
  doc: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M6 3h8l4 4v14H6V3Z"/><path d="M14 3v4h4M9 12h6M9 16h6"/></g>,
  info: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 11v5M12 8h.01"/></g>,
  globe: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c2.5 2.4 2.5 15.6 0 18M12 3c-2.5 2.4-2.5 15.6 0 18"/></g>,
  gauge: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 18a8 8 0 1 1 16 0"/><path d="M12 18l4-5"/></g>,
  spark: <path d="M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8L12 3Z" fill="currentColor"/>,
  key: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="8" cy="15" r="4"/><path d="M10.8 12.2 20 3M17 6l2.5 2.5M14 9l2.5 2.5"/></g>,
  eye: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></g>,
  eyeOff: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M10.6 6.2A9.7 9.7 0 0 1 12 5c6.5 0 10 7 10 7a17 17 0 0 1-3.2 4M6.6 6.7A17 17 0 0 0 2 12s3.5 7 10 7a9.6 9.6 0 0 0 4.2-.9M3 3l18 18M9.9 9.9a3 3 0 0 0 4.2 4.2"/></g>,
  shield: <path d="M12 3l7 3v5c0 4.5-3 8-7 10-4-2-7-5.5-7-10V6l7-3Z" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>,
  trash: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 7h16M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2M6 7l1 13h10l1-13M10 11v6M14 11v6"/></g>,
  alert: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 3 2 20h20L12 3Z"/><path d="M12 10v4M12 17h.01"/></g>,
  plug: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M9 3v5M15 3v5M6 8h12v3a6 6 0 0 1-12 0V8ZM12 17v4"/></g>,
  record: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="11" r="4"/><path d="M12 15v4M8 21h8"/></g>,
  expand: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 9V5a1 1 0 0 1 1-1h4M20 9V5a1 1 0 0 0-1-1h-4M4 15v4a1 1 0 0 0 1 1h4M20 15v4a1 1 0 0 1-1 1h-4"/></g>,
  compress: <g fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M9 4v4a1 1 0 0 1-1 1H4M15 4v4a1 1 0 0 0 1 1h4M9 20v-4a1 1 0 0 0-1-1H4M15 20v-4a1 1 0 0 1 1-1h4"/></g>,
};
function Svg({ children, vb = "0 0 24 24" }) { return <svg viewBox={vb}>{children}</svg>; }

/* ---------------- backend url helper ----------------
   後台「後端服務位址」可留空(=同源,本機開發用),也可填正式網址。
   會自動去掉結尾的 /v1 與斜線,再接上 /api/... 路徑。 */
function backendApi(backendUrl, pathname) {
  const base = (backendUrl || "").trim().replace(/\/v1\/?$/i, "").replace(/\/+$/, "");
  return base + pathname;
}

/* ---------------- 時間戳(字幕段落用) ---------------- */
function fmtClock(ts) {
  const d = ts ? new Date(ts) : new Date();
  const p = (n) => String(n).padStart(2, "0");
  return p(d.getHours()) + ":" + p(d.getMinutes()) + ":" + p(d.getSeconds());
}

/* ---------------- status bar ---------------- */
function StatusBar({ dark }) {
  const [t, setT] = useState(() => clock());
  useEffect(() => { const id = setInterval(() => setT(clock()), 20000); return () => clearInterval(id); }, []);
  function clock() { const d = new Date(); return d.getHours() + ":" + String(d.getMinutes()).padStart(2, "0"); }
  return (
    <div className={"statusbar" + (dark ? " on-dark" : "")}>
      <span>{t}</span>
      <span className="sb-right">
        <span className="sb-bars">
          <i style={{ height: "5px" }}></i><i style={{ height: "7px" }}></i><i style={{ height: "9px" }}></i><i style={{ height: "11px" }}></i>
        </span>
        <svg width="16" height="12" viewBox="0 0 16 12" fill="none"><path d="M8 10.5 1 4.2a10 10 0 0 1 14 0L8 10.5Z" stroke="currentColor" strokeWidth="1.4" opacity=".9"/><path d="M8 10.5 4 6.8a6 6 0 0 1 8 0L8 10.5Z" fill="currentColor"/></svg>
        <span className="sb-batt"><i></i></span>
      </span>
    </div>
  );
}

/* ---------------- waveform ---------------- */
function Wave() { return <span className="wave"><i></i><i></i><i></i><i></i><i></i></span>; }


/* ---------------- connection pill + popover ---------------- */
const CONN_STATES = {
  ok:   { label: "已連線", color: "var(--ok)", desc: "穩定" },
  warn: { label: "重新連線中…", color: "var(--warn)", desc: "暫時中斷" },
  lag:  { label: "延遲偏高", color: "var(--warn)", desc: "網路擁塞" },
  err:  { label: "無法使用", color: "var(--err)", desc: "請檢查網路" },
};
function ConnPill({ state, latency, onClick }) {
  const s = CONN_STATES[state];
  const dotClass = state === "ok" ? "dot" : state === "err" ? "dot err" : "dot warn";
  return (
    <button className="conn" onClick={onClick}>
      <span className={dotClass}></span>
      <span>{s.label}</span>
      {state === "ok" && latency ? <span className="lat">{latency}ms</span> : null}
    </button>
  );
}
function ConnPopover({ state, latency, onClose }) {
  const order = ["ok", "lag", "warn", "err"];
  return (
    <>
      <div style={{ position: "absolute", inset: 0, zIndex: 44 }} onClick={onClose}></div>
      <div className="conn-pop">
        {order.map(k => {
          const s = CONN_STATES[k];
          return (
            <div key={k} className={"cp-row" + (k === state ? " cur" : "")}>
              <span className="d" style={{ background: s.color }}></span>
              <span className="cp-t">{s.label}</span>
              <span className="cp-s">{k === state && state === "ok" && latency ? latency + "ms" : s.desc}</span>
            </div>
          );
        })}
      </div>
    </>
  );
}

/* ---------------- language picker sheet ---------------- */
function LangPicker({ title, subtitle, current, exclude, onPick, onClose }) {
  const [q, setQ] = useState("");
  const list = DATA.LANGS.filter(l => {
    if (exclude && l.id === exclude) return false;
    if (!q) return true;
    const s = (l.native + " " + l.en + " " + l.code).toLowerCase();
    return s.includes(q.toLowerCase());
  });
  const common = list.filter(l => l.common);
  const rest = list.filter(l => !l.common);
  const Row = (l) => (
    <button key={l.id} className={"lang-row" + (l.id === current ? " sel" : "")} onClick={() => onPick(l.id)}>
      <span className="lr-code">{l.code}</span>
      <span className="lr-txt">
        <span className="lr-native">{l.native}</span>
        <span className="lr-en">{l.en}</span>
      </span>
      <span className="lr-check"><Svg>{I.check}</Svg></span>
    </button>
  );
  return (
    <div className="sheet-scrim" onClick={onClose}>
      <div className="sheet" onClick={e => e.stopPropagation()}>
        <div className="sheet-grab"></div>
        <div className="sheet-head">
          <h3>{title}</h3>
          <p>{subtitle}</p>
        </div>
        <div className="sheet-search">
          <Svg>{I.search}</Svg>
          <input placeholder="搜尋語言…" value={q} onChange={e => setQ(e.target.value)} autoFocus={false} />
        </div>
        <div className="sheet-list">
          {!q && common.length > 0 && <div className="sheet-sec">常用</div>}
          {(q ? list : common).map(Row)}
          {!q && rest.length > 0 && <div className="sheet-sec">所有語言</div>}
          {!q && rest.map(Row)}
        </div>
      </div>
    </div>
  );
}

/* ---------------- 全螢幕切換 ----------------
   Android Chrome 可隱藏網址列與系統列;iOS iPhone 不支援 Fullscreen API,
   請改用「加入主畫面」以全螢幕網頁 App 開啟(由 manifest 處理)。 */
function useFullscreen() {
  const [fs, setFs] = useState(!!(document.fullscreenElement || document.webkitFullscreenElement));
  useEffect(() => {
    const on = () => setFs(!!(document.fullscreenElement || document.webkitFullscreenElement));
    document.addEventListener("fullscreenchange", on);
    document.addEventListener("webkitfullscreenchange", on);
    return () => { document.removeEventListener("fullscreenchange", on); document.removeEventListener("webkitfullscreenchange", on); };
  }, []);
  const supported = !!(document.documentElement.requestFullscreen || document.documentElement.webkitRequestFullscreen);
  const toggle = () => {
    const el = document.documentElement;
    if (!document.fullscreenElement && !document.webkitFullscreenElement) {
      (el.requestFullscreen || el.webkitRequestFullscreen || function () {}).call(el);
    } else {
      (document.exitFullscreen || document.webkitExitFullscreen || function () {}).call(document);
    }
  };
  return { fs, toggle, supported };
}
function FullscreenBtn({ className }) {
  const { fs, toggle, supported } = useFullscreen();
  if (!supported) return null;   // iPhone Safari:隱藏(改用加入主畫面)
  return (
    <button className={className || "icon-btn"} onClick={toggle} title={fs ? "離開全螢幕" : "全螢幕"}>
      <Svg>{fs ? I.compress : I.expand}</Svg>
    </button>
  );
}

Object.assign(window, { I, Svg, backendApi, fmtClock, useFullscreen, FullscreenBtn, StatusBar, Wave, ConnPill, ConnPopover, LangPicker, CONN_STATES });
