/* 聆譯 — 後台設定 (Settings) */
const { useState: useStateS, useEffect: useEffectS } = React;
const Ds = window.APP_DATA;
const langS = (id) => Ds.LANG_BY[id];

/* 列舉實際音訊裝置(麥克風 / 喇叭);未授權時 label 可能為空,給予預設名稱。 */
function useMediaDevices() {
  const [d, setD] = useStateS({ inputs: [], outputs: [] });
  useEffectS(() => {
    if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) return;
    let alive = true;
    const load = async () => {
      try {
        const list = await navigator.mediaDevices.enumerateDevices();
        if (!alive) return;
        setD({
          inputs: list.filter((x) => x.kind === "audioinput"),
          outputs: list.filter((x) => x.kind === "audiooutput"),
        });
      } catch (e) { /* ignore */ }
    };
    load();
    navigator.mediaDevices.addEventListener && navigator.mediaDevices.addEventListener("devicechange", load);
    return () => {
      alive = false;
      navigator.mediaDevices.removeEventListener && navigator.mediaDevices.removeEventListener("devicechange", load);
    };
  }, []);
  return d;
}

/* ---- small controls (scoped to this file) ---- */
function StGroup({ title, desc, children }) {
  return (
    <section className="st-group">
      <div className="st-group-head">
        <h3>{title}</h3>
        {desc && <p>{desc}</p>}
      </div>
      <div className="st-card">{children}</div>
    </section>
  );
}
function StRow({ icon, label, sub, control, onClick, last }) {
  return (
    <div className={"st-row" + (onClick ? " tap" : "") + (last ? " last" : "")} onClick={onClick}>
      {icon && <span className="st-ic">{icon}</span>}
      <span className="st-rtxt">
        <span className="st-label">{label}</span>
        {sub && <span className="st-sub">{sub}</span>}
      </span>
      <span className="st-ctrl">{control}</span>
    </div>
  );
}
function StToggle({ on, onChange }) {
  return (
    <button className={"st-toggle" + (on ? " on" : "")} onClick={(e) => { e.stopPropagation(); onChange(!on); }}>
      <span className="st-knob"></span>
    </button>
  );
}
function StSlider({ value, min, max, unit, onChange }) {
  return (
    <span className="st-slider">
      <input type="range" min={min} max={max} value={value} onChange={(e) => onChange(+e.target.value)} />
      <span className="st-val">{value}{unit}</span>
    </span>
  );
}
function StSelect({ value, options, onChange }) {
  return (
    <span className="st-select">
      <select value={value} onChange={(e) => onChange(e.target.value)}>
        {options.map((o, i) => <option key={o.v + "-" + i} value={o.v}>{o.l}</option>)}
      </select>
      <span className="st-caret"><Svg>{I.caretDown}</Svg></span>
    </span>
  );
}
function StPill({ children, tone }) { return <span className={"st-pill " + (tone || "")}>{children}</span>; }
/* 分級切換(取代滑桿) */
function StSeg({ value, options, onChange }) {
  return (
    <span className="st-seg2">
      {options.map((o) => (
        <button key={o.v} className={value === o.v ? "on" : ""} onClick={() => onChange(o.v)}>{o.l}</button>
      ))}
    </span>
  );
}
/* 顏色色票 */
function StSwatches({ value, options, onChange }) {
  return (
    <span className="st-swatches">
      {options.map((o) => (
        <button key={o.v} className={"st-sw" + (value === o.v ? " on" : "")} style={{ background: o.c }}
          title={o.l} aria-label={o.l} onClick={() => onChange(o.v)}>
          {value === o.v && <Svg>{I.check}</Svg>}
        </button>
      ))}
    </span>
  );
}
function StField({ value, onChange, placeholder, password }) {
  const [show, setShow] = useStateS(false);
  return (
    <div className="st-field">
      <input type={password && !show ? "password" : "text"} value={value} onChange={(e) => onChange(e.target.value)} placeholder={placeholder} spellCheck="false" autoComplete="off" />
      {password && <button className="st-eye" onClick={() => setShow(v => !v)} title={show ? "隱藏" : "顯示"}><Svg>{show ? I.eyeOff : I.eye}</Svg></button>}
    </div>
  );
}
function StTestConn({ apiKey, backendUrl }) {
  const [st, setSt] = useStateS("idle");
  const [info, setInfo] = useStateS(null); // { latencyMs } | { message }
  const run = async () => {
    if (st === "testing") return;
    setSt("testing"); setInfo(null);
    try {
      // 把後台輸入框的金鑰當「臨時覆寫」送給後端;若空白,後端會改用環境變數金鑰。
      const r = await fetch(backendApi(backendUrl, "/api/test-connection"), {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ overrideKey: (apiKey || "").trim() || undefined }),
      });
      const data = await r.json();
      if (data.ok) { setSt("ok"); setInfo({ latencyMs: data.latencyMs }); }
      else { setSt("fail"); setInfo({ message: data.message || "連線失敗" }); }
    } catch (e) {
      setSt("fail");
      setInfo({ message: "無法連線到後端服務,請確認後端是否已啟動。" });
    }
  };
  return (
    <div className="st-testrow">
      <button className="st-testbtn" onClick={run} disabled={st === "testing"}>
        <Svg>{I.plug}</Svg>{st === "testing" ? "測試中…" : "測試連線"}
      </button>
      {st === "ok" && <span className="st-teststatus ok"><Svg>{I.check}</Svg>連線成功 · 權限正常{info?.latencyMs != null ? ` · ${info.latencyMs}ms` : ""}</span>}
      {st === "fail" && <span className="st-teststatus fail"><Svg>{I.alert}</Svg>{info?.message || "金鑰無效或權限不足"}</span>}
      {st === "testing" && <span className="st-teststatus testing">驗證金鑰與權限中…</span>}
      {st === "idle" && <span className="st-teststatus idle">尚未測試</span>}
    </div>
  );
}
function StClearBtn({ onClear }) {
  const [done, setDone] = useStateS(false);
  return (
    <button className={"st-danger" + (done ? " done" : "")} onClick={() => { if (onClear) onClear(); setDone(true); setTimeout(() => setDone(false), 2200); }}>
      <Svg>{done ? I.check : I.trash}</Svg>{done ? "已清除" : "清除所有紀錄"}
    </button>
  );
}

/* ---- 會議紀錄(真實 localStorage) ---- */
function fmtRecDate(ts) { const d = new Date(ts); return (d.getMonth() + 1) + " 月 " + d.getDate() + " 日"; }
function fmtRecDur(sec) {
  if (!sec || sec < 60) return Math.max(1, Math.round(sec || 0)) + " 秒";
  return Math.round(sec / 60) + " 分鐘";
}
function recLangPair(rec) {
  const f = rec.fromLang && langS(rec.fromLang) ? langS(rec.fromLang).code : "";
  const t = rec.toLang && langS(rec.toLang) ? langS(rec.toLang).code : "";
  return f && t ? f + " ⇄ " + t : (t || "");
}

function SummarySheet({ rec, apiKey, backendUrl, onClose, onSaved }) {
  const [st, setSt] = useStateS(rec.summary ? "done" : "idle");
  const [text, setText] = useStateS(rec.summary || "");
  const [err, setErr] = useStateS("");
  const gen = async () => {
    setSt("loading"); setErr("");
    try {
      const r = await fetch(backendApi(backendUrl, "/api/summary"), {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ transcript: rec.lines, title: rec.title, overrideKey: (apiKey || "").trim() || undefined }),
      });
      const data = await r.json();
      if (data.ok) {
        setText(data.summary); setSt("done");
        if (window.LingyiRecords) LingyiRecords.setSummary(rec.id, data.summary);
        if (onSaved) onSaved();
      } else { setSt("error"); setErr(data.message || "產生摘要失敗。"); }
    } catch (e) { setSt("error"); setErr("無法連線到後端服務,請確認後端是否啟動。"); }
  };
  return (
    <div className="sheet-scrim" onClick={onClose}>
      <div className="sheet" onClick={(e) => e.stopPropagation()}>
        <div className="sheet-grab"></div>
        <div className="sheet-head"><h3>會後摘要</h3><p>{rec.title} · {fmtRecDate(rec.ts)} · {rec.lines ? rec.lines.length : 0} 句</p></div>
        <div className="sheet-list" style={{ padding: "4px 20px 24px" }}>
          {st === "idle" && <button className="st-testbtn" onClick={gen}><Svg>{I.spark}</Svg>產生會後摘要</button>}
          {st === "loading" && <div className="sum-loading"><Svg>{I.spark}</Svg>產生中…請稍候(會後處理,可能需數秒)</div>}
          {st === "error" && <div className="st-note warn"><Svg>{I.alert}</Svg>{err}</div>}
          {st === "done" && <div className="sum-text">{text}</div>}
          {st === "done" && <button className="va-btn" style={{ marginTop: 12 }} onClick={gen}><Svg>{I.spark}</Svg>重新產生</button>}
        </div>
      </div>
    </div>
  );
}

function RecordsList({ apiKey, backendUrl, onChange }) {
  const [open, setOpen] = useStateS(null);
  const recs = window.LingyiRecords ? LingyiRecords.listRecords() : [];
  if (!recs.length) {
    return <div className="st-note"><Svg>{I.doc}</Svg>尚無會議紀錄。開始一次翻譯、按停止後,逐字稿會自動儲存在這裡。</div>;
  }
  return (
    <>
      <div className="rec-list">
        {recs.map((rec) => (
          <div className="rec-row" key={rec.id} onClick={() => setOpen(rec)}>
            <span className="rec-dot"></span>
            <span className="rec-meta">
              <b>{rec.title}</b>
              <span>{fmtRecDate(rec.ts)} · {fmtRecDur(rec.durationSec)}{recLangPair(rec) ? " · " + recLangPair(rec) : ""}{rec.summary ? " · 已摘要" : ""}</span>
            </span>
            <span className="rec-act">摘要<span className="pc"><Svg>{I.chevR}</Svg></span></span>
          </div>
        ))}
      </div>
      {open && <SummarySheet rec={open} apiKey={apiKey} backendUrl={backendUrl} onClose={() => setOpen(null)} onSaved={onChange} />}
    </>
  );
}

/* ---- 用量(依真實會議紀錄估算) ---- */
const TRANSLATE_RATE = 0.034; // 即時翻譯約 US$/分鐘
function fmtRecDateShort(ts) { const d = new Date(ts); return (d.getMonth() + 1) + "/" + d.getDate(); }
function UsageView() {
  const recs = window.LingyiRecords ? LingyiRecords.listRecords() : [];
  const totalSec = recs.reduce((a, r) => a + (r.durationSec || 0), 0);
  const estCost = (totalSec / 60) * TRANSLATE_RATE;
  const fmtTotal = (sec) => Math.floor(sec / 60) + " 分 " + String(Math.round(sec % 60)).padStart(2, "0") + " 秒";
  return (
    <>
      <StGroup title="累計用量" desc={"依已儲存的會議紀錄估算;即時翻譯約 US$" + TRANSLATE_RATE + " / 分鐘。"}>
        <div className="usage-hero">
          <div className="uh-col"><span className="uh-k">累計時長</span><span className="uh-v">{fmtTotal(totalSec)}</span></div>
          <div className="uh-div"></div>
          <div className="uh-col"><span className="uh-k">估計費用</span><span className="uh-v cost">US$ {estCost.toFixed(2)}</span></div>
        </div>
        <StRow icon={<Svg>{I.doc}</Svg>} label="紀錄場次" last control={<span className="st-muted">{recs.length} 場</span>} />
        <div className="st-note"><Svg>{I.info}</Svg>此為估算值,實際費用以 OpenAI 帳單為準。</div>
      </StGroup>
      <StGroup title="各場紀錄">
        {recs.length ? (
          <div className="hist-list">
            {recs.map((r) => (
              <div className="hist-row" key={r.id}>
                <span className="hist-date">{fmtRecDateShort(r.ts)}</span>
                <span className="hist-name">{r.title}</span>
                <span className="hist-min">{fmtRecDur(r.durationSec)}</span>
                <span className="hist-cost">US$ {((r.durationSec / 60) * TRANSLATE_RATE).toFixed(2)}</span>
              </div>
            ))}
          </div>
        ) : (
          <div className="st-note"><Svg>{I.info}</Svg>尚無紀錄。翻譯結束後會自動記錄時長與估計費用。</div>
        )}
      </StGroup>
    </>
  );
}

/* ---- main ---- */
function SettingsView({ s, setS, onClose, embedded }) {
  const [picker, setPicker] = useStateS(null); // field key
  const [tab, setTab] = useStateS("conn");
  const [recRefresh, setRecRefresh] = useStateS(0); // 會議紀錄變更後強制重讀
  const set = (k, v) => setS((prev) => ({ ...prev, [k]: v }));

  const pickField = (key, title, sub, exclude) => ({
    onPick: (id) => { set(key, id); setPicker(null); },
    open: () => setPicker({ key, title, sub, exclude, current: s[key] }),
  });

  const devices = useMediaDevices();
  // 未授權時 deviceId 可能為空,過濾掉(避免與「系統預設」空值重複)
  const inputOpts = [{ v: "", l: "系統預設麥克風" }].concat(
    devices.inputs.filter((dv) => dv.deviceId).map((dv, i) => ({ v: dv.deviceId, l: dv.label || ("麥克風 " + (i + 1)) }))
  );
  const outputOpts = [{ v: "", l: "系統預設輸出" }].concat(
    devices.outputs.filter((dv) => dv.deviceId).map((dv, i) => ({ v: dv.deviceId, l: dv.label || ("輸出裝置 " + (i + 1)) }))
  );
  const regionOpts = [
    { v: "asia-east", l: "亞太 · 東亞 (台北)" },
    { v: "asia-se", l: "亞太 · 東南亞 (新加坡)" },
    { v: "us-west", l: "美洲 · 西岸" },
    { v: "eu", l: "歐洲 (法蘭克福)" },
  ];

  const usagePct = 34;

  return (
    <div className={"settings" + (embedded ? " embedded" : "")}>
      <header className="st-top">
        <button className="icon-btn" onClick={onClose} title="返回">
          <Svg>{embedded ? I.close : I.back}</Svg>
        </button>
        <div className="st-title">
          <h2>設定</h2>
          <p>不常變動的偏好都收在這裡</p>
        </div>
      </header>

      <div className="st-tabs">
        {[{ id: "conn", label: "連線" }, { id: "general", label: "一般" }, { id: "audio", label: "音訊" }, { id: "voice", label: "語音" }, { id: "usage", label: "用量" }].map(tb => (
          <button key={tb.id} className={"st-tab" + (tab === tb.id ? " on" : "")} onClick={() => setTab(tb.id)}>{tb.label}</button>
        ))}
      </div>

      <div className="st-scroll" key={tab}>
        {tab === "conn" && <StGroup title="OPENAI 連線" desc="輸入金鑰後即可實際連上 OpenAI 即時翻譯模型。">
          <div className="st-fieldrow">
            <label className="st-flabel"><span className="fl-ic"><Svg>{I.key}</Svg></span>OpenAI API Key</label>
            <StField value={s.apiKey} onChange={v => set("apiKey", v)} placeholder="sk-…" password />
          </div>
          <StTestConn apiKey={s.apiKey} backendUrl={s.backendUrl} />
        </StGroup>}
        {tab === "conn" && <StGroup title="後端服務">
          <div className="st-fieldrow">
            <label className="st-flabel">後端服務位址</label>
            <StField value={s.backendUrl} onChange={v => set("backendUrl", v)} placeholder="https://…" />
            <span className="fl-hint">本機測試可留空（＝使用同一台後端）。日後上 Zeabur 再填正式網址。</span>
          </div>
          <StRow icon={<Svg>{I.globe}</Svg>} label="連線區域" last control={<StSelect value={s.region} options={regionOpts} onChange={v => set("region", v)} />} />
          <div className="st-note safe"><Svg>{I.shield}</Svg>API Key 僅儲存於後端伺服器,不會傳送或外洩至瀏覽器,請安心使用。</div>
        </StGroup>}
        {tab === "conn" && <StGroup title="連線狀態">
          <div className="st-note"><Svg>{I.info}</Svg>翻譯進行中時,連線狀態與延遲會即時顯示在主畫面右上角。</div>
        </StGroup>}

        {tab === "general" && <StGroup title="介面與隱私">
          <StRow icon={<Svg>{I.globe}</Svg>} label="介面語言" sub="操作介面顯示語言(向外賓 / 長官展示時可切英文)" control={<StSelect value={s.uiLang} options={[{ v: "zh", l: "中文" }, { v: "en", l: "English" }]} onChange={v => set("uiLang", v)} />} />
          <StRow icon={<Svg>{I.trash}</Svg>} label="清除所有翻譯紀錄" sub="客戶對話涉及隱私,可一鍵清除" last control={<StClearBtn onClear={() => { if (window.LingyiRecords) LingyiRecords.clearRecords(); setRecRefresh((x) => x + 1); }} />} />
        </StGroup>}

        {tab === "general" && <StGroup title="字幕顯示" desc="只調整字幕對話框的文字,不影響按鈕與其他介面。">
          <StRow icon={<Svg>{I.doc}</Svg>} label="字型" control={<StSelect value={s.subFont} options={window.SUB_FONT_OPTS} onChange={v => set("subFont", v)} />} />
          <StRow icon={<Svg>{I.search}</Svg>} label="字體大小" control={<StSeg value={s.subFontSize} options={window.SUB_SIZE_OPTS} onChange={v => set("subFontSize", v)} />} />
          <StRow icon={<Svg>{I.spark}</Svg>} label="文字顏色" last control={<StSwatches value={s.subColor} options={window.SUB_COLOR_OPTS} onChange={v => set("subColor", v)} />} />
          <div className="sub-preview">
            <span className="sp-time">12:34:56</span>
            <div className="sp-text">這是字幕預覽 · Subtitle preview</div>
          </div>
        </StGroup>}

        {tab === "general" && <StGroup title="預設語言對" desc="開啟時自動套用的語言;前台仍可隨時切換。">
          <div className="pair-card">
            <div className="pair-col">
              <span className="pair-tag">個人用 · 聆聽</span>
              <button className="pair-lang" onClick={pickField("soloListen").open}>
                <span className="code">{langS(s.soloListen).code}</span>{langS(s.soloListen).native}
                <span className="pc"><Svg>{I.caretDown}</Svg></span>
              </button>
            </div>
            <span className="pair-arrow"><Svg>{I.swap}</Svg></span>
            <div className="pair-col">
              <span className="pair-tag">翻譯為</span>
              <button className="pair-lang out" onClick={pickField("soloTarget").open}>
                <span className="code">{langS(s.soloTarget).code}</span>{langS(s.soloTarget).native}
                <span className="pc"><Svg>{I.caretDown}</Svg></span>
              </button>
            </div>
          </div>
          <div className="pair-card">
            <div className="pair-col">
              <span className="pair-tag">對話用 · 我說</span>
              <button className="pair-lang" onClick={pickField("convFrom").open}>
                <span className="code">{langS(s.convFrom).code}</span>{langS(s.convFrom).native}
                <span className="pc"><Svg>{I.caretDown}</Svg></span>
              </button>
            </div>
            <span className="pair-arrow"><Svg>{I.swap}</Svg></span>
            <div className="pair-col">
              <span className="pair-tag">對方語言</span>
              <button className="pair-lang out" onClick={pickField("convTo").open}>
                <span className="code">{langS(s.convTo).code}</span>{langS(s.convTo).native}
                <span className="pc"><Svg>{I.caretDown}</Svg></span>
              </button>
            </div>
          </div>
          <div className="st-note"><Svg>{I.globe}</Svg>共 {Ds.LANGS.length} 種語言 · 雲端翻譯,免下載語言包、不佔空間。</div>
        </StGroup>}

        {tab === "general" && <StGroup title="各視窗預設發聲" desc="開始翻譯時,語音是否自動播出。">
          <StRow icon={<Svg>{I.user}</Svg>} label="個人用" sub="聽影片 / 簡報" control={<StToggle on={s.voiceSolo} onChange={v => set("voiceSolo", v)} />} />
          <StRow icon={<Svg>{I.speaker}</Svg>} label="對話用 · 我說的話" sub="翻給對方聽" control={<StToggle on={s.voiceMe} onChange={v => set("voiceMe", v)} />} />
          <StRow icon={<Svg>{I.chat}</Svg>} label="對話用 · 對方說的話" sub="翻給我聽" last control={<StToggle on={s.voiceThem} onChange={v => set("voiceThem", v)} />} />
        </StGroup>}

        {tab === "audio" && <StGroup title="音訊裝置">
          <StRow icon={<Svg>{I.mic}</Svg>} label="輸入裝置" control={<StSelect value={s.inputDevice} options={inputOpts} onChange={v => set("inputDevice", v)} />} />
          <StRow icon={<Svg>{I.headphone}</Svg>} label="輸出裝置" last control={<StSelect value={s.outputDevice} options={outputOpts} onChange={v => set("outputDevice", v)} />} />
          {!devices.inputs.some(dv => dv.label) && <div className="st-note"><Svg>{I.info}</Svg>裝置名稱需先授權麥克風後才會完整顯示;未選擇時使用系統預設。</div>}
        </StGroup>}

        {tab === "voice" && <StGroup title="語音設定">
          <StRow icon={<Svg>{I.spark}</Svg>} label="自動模仿我的音色" sub="翻譯成對方語言時,沿用你的聲音,免設定。" control={<StToggle on={s.mimic} onChange={v => set("mimic", v)} />} />
          <div className="voice-adv">
            <div className="va-head">
              <span className="va-title">自訂語音<StPill tone="soon">進階 · 即將推出</StPill></span>
              <span className="va-desc">上傳聲音樣本,建立專屬語音模型。</span>
            </div>
            <div className="dropzone" aria-disabled="true">
              <span className="dz-ic"><Svg>{I.upload}</Svg></span>
              <span className="dz-t">拖曳或點擊上傳聲音樣本</span>
              <span className="dz-s">WAV / MP3 · 建議 30 秒以上清晰人聲</span>
            </div>
            <button className="va-btn" disabled><Svg>{I.record}</Svg>改用麥克風錄製樣本</button>
            <div className="va-note"><Svg>{I.info}</Svg>需另行開通 OpenAI Custom Voices 權限;目前無法套用於即時翻譯模型。</div>
          </div>
        </StGroup>}

        {tab === "audio" && <StGroup title="原音處理" desc="翻譯語音播放時,原音壓低而非完全靜音,保留講者語氣。">
          <StRow icon={<Svg>{I.ear}</Svg>} label="原音音量" control={<StSlider value={s.origVol} min={0} max={100} unit="%" onChange={v => set("origVol", v)} />} />
          <StRow icon={<Svg>{I.speaker}</Svg>} label="翻譯語音音量" last control={<StSlider value={s.transVol} min={0} max={100} unit="%" onChange={v => set("transVol", v)} />} />
          <div className="st-note warn"><Svg>{I.alert}</Svg>單機雙向並使用外放時可能產生迴授,建議配戴耳機以獲得最佳品質。</div>
        </StGroup>}

        {tab === "general" && <StGroup title="會議紀錄" desc="加值功能,於會議結束後處理,不影響即時翻譯。">
          <StRow icon={<Svg>{I.doc}</Svg>} label="自動儲存逐字稿" control={<StToggle on={s.saveTranscript} onChange={v => set("saveTranscript", v)} />} />
          <StRow icon={<Svg>{I.spark}</Svg>} label="會後自動摘要" sub="會議結束後產生重點摘要" last control={<StToggle on={s.autoSummary} onChange={v => set("autoSummary", v)} />} />
          <RecordsList apiKey={s.apiKey} backendUrl={s.backendUrl} refresh={recRefresh} onChange={() => setRecRefresh((x) => x + 1)} />
        </StGroup>}

        {tab === "usage" && <UsageView refresh={recRefresh} />}

        <div className="st-end">聆譯 · Internet of happiness</div>
      </div>

      {picker && (
        <LangPicker title={picker.title || "選擇語言"} subtitle={picker.sub || ""} current={picker.current}
          onPick={pickField(picker.key).onPick} onClose={() => setPicker(null)} />
      )}
    </div>
  );
}

window.SettingsView = SettingsView;
