/* app.jsx — root: connects to the server (REST + SSE), drives the phase machine
 * (home → detail → map), theme & tweaks. All app state is live from the backend.
 * 只做「添加 App + 展示」：地图来自后端已采集的数据，前端不再有任何自动探索流程。 */

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "layout": "rail-left",
  "edge": "elbow",
  "focusDim": true,
  "labels": true
}/*EDITMODE-END*/;

function App() {
  const [library, setLibrary] = useState([]);
  const [aiOk, setAiOk] = useState(null);
  const [readOnly, setReadOnly] = useState(false);   // static(线上)模式：隐藏所有写入口
  const [phase, setPhase] = useState("home");        // home | detail | map
  const [tab, setTab] = useState("map");
  const [selected, setSelected] = useState(null);
  const [detailId, setDetailId] = useState(null);
  const [detail, setDetail] = useState(null);
  const [detailFrom, setDetailFrom] = useState("gallery"); // import | gallery | map
  const [theme, setTheme] = useState("dark");
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  useEffect(() => { document.documentElement.dataset.theme = theme; }, [theme]);
  const toggleTheme = () => setTheme(x => (x === "dark" ? "light" : "dark"));

  const detailIdRef = useRef(null);
  detailIdRef.current = detailId;

  const refreshState = useCallback(async () => {
    try { const s = await CARTO.getState(); setLibrary(s.apps); setAiOk(s.aiOk); setReadOnly(CARTO.isReadOnly()); } catch (e) {}
  }, []);

  const refreshDetail = useCallback(async (id) => {
    if (!id) return null;
    try {
      const d = await CARTO.getDetail(id);
      if (detailIdRef.current === id) setDetail(d);
      return d;
    } catch (e) { return null; }
  }, []);

  // initial load
  useEffect(() => { refreshState(); }, [refreshState]);

  // live updates over SSE
  useEffect(() => {
    let stateTimer = null;
    const unsub = CARTO.subscribe((ev) => {
      if (ev.type !== "app" && ev.type !== "removed") return;
      if (stateTimer) clearTimeout(stateTimer);
      stateTimer = setTimeout(refreshState, 120);   // debounce library refresh

      const id = detailIdRef.current;
      if (!id || ev.id !== id) return;
      if (ev.type === "removed") { setPhase("home"); setDetailId(null); setDetail(null); return; }
      if (ev.app) {
        const a = ev.app;
        // light status/ai merge (no full refetch on every tick)
        setDetail(d => (d && d.id === id) ? { ...d, status: a.status, aiStatus: a.aiStatus, trans: a.trans || d.trans } : d);
        // refetch full detail when AI positioning lands
        if (a.aiStatus === "done" || a.aiStatus === "error") refreshDetail(id);
      }
    });
    return () => { if (stateTimer) clearTimeout(stateTimer); unsub(); };
  }, [refreshState, refreshDetail]);

  // load detail bundle when target changes
  useEffect(() => {
    if (!detailId) { setDetail(null); return; }
    setDetail(null);
    refreshDetail(detailId);
  }, [detailId, refreshDetail]);

  /* -------- navigation -------- */
  const openDetail = (id, from) => { setDetailFrom(from); setSelected(null); setDetailId(id); setPhase("detail"); };
  const goHome = () => { setPhase("home"); setSelected(null); setDetailId(null); setDetail(null); };
  const goMap = () => { setPhase("map"); setTab("map"); };

  const startAnalyze = async (id) => {
    try { await CARTO.analyze(id); } catch (e) {}
    await refreshDetail(id);   // pick up queued/running status; SSE pushes the result
  };

  /* -------- render -------- */
  let body;
  if (phase === "home" || !detail) {
    if (phase !== "home" && !detail) {
      body = <LoadingScreen toggleTheme={toggleTheme} onHome={goHome} />;
    } else {
      body = <GalleryScreen library={library} aiOk={aiOk} readOnly={readOnly} toggleTheme={toggleTheme}
        onOpenDetail={(app) => openDetail(app.id, "gallery")}
        onStart={(id) => { openDetail(id, "import"); refreshState(); }} />;
    }
  } else if (phase === "detail") {
    body = <ProductDetail detail={detail} from={detailFrom} theme={theme} readOnly={readOnly} toggleTheme={toggleTheme}
      onHome={goHome}
      onAnalyze={() => startAnalyze(detail.id)}
      onOpenMap={goMap} />;
  } else {
    const mapData = detail.map ? { app: detail.app, journeys: detail.map.journeys, nodes: detail.map.nodes, edges: detail.map.edges, stats: detail.map.stats } : null;
    body = (
      <div style={{ height: "100vh", display: "flex", flexDirection: "column" }}>
        <TopBar app={detail.app} stats={detail.stats} tab={tab} setTab={setTab}
          onHome={goHome}
          onOpenDetail={() => setPhase("detail")}
          theme={theme} toggleTheme={toggleTheme} />
        {mapData
          ? <MapScreen data={mapData} tab={tab} selected={selected} onSelect={setSelected} tw={t} />
          : <div style={{ flex: 1, display: "grid", placeItems: "center", color: "var(--t-dim)" }}>该 App 暂无页面地图。</div>}
      </div>
    );
  }

  const showTweaks = phase === "map" && !!detail;

  return (
    <React.Fragment>
      <div key={phase} className="fade-in" style={{ height: "100vh" }}>{body}</div>

      {showTweaks && (
        <TweaksPanel title="Tweaks">
          <TweakSection label="布局结构" />
          <TweakRadio label="旅程栏" value={t.layout} options={["rail-left", "no-rail", "rail-right"]} onChange={(v) => setTweak("layout", v)} />
          <TweakToggle label="节点名称标签" value={t.labels} onChange={(v) => setTweak("labels", v)} />
          <TweakSection label="Map 可视化" />
          <TweakRadio label="连线风格" value={t.edge} options={["elbow", "curve", "straight"]} onChange={(v) => setTweak("edge", v)} />
          <TweakToggle label="选中聚焦（淡化无关）" value={t.focusDim} onChange={(v) => setTweak("focusDim", v)} />
          <TweakSection label="主题" />
          <TweakRadio label="明暗" value={theme} options={["dark", "light"]} onChange={setTheme} />
        </TweaksPanel>
      )}
    </React.Fragment>
  );
}

function LoadingScreen({ toggleTheme, onHome }) {
  return (
    <div style={{ height: "100vh", display: "flex", flexDirection: "column" }}>
      <MiniTop onHome={onHome} right={<div className="icon-btn" onClick={toggleTheme}><Icon name="sun" s={15} /></div>} />
      <div style={{ flex: 1, display: "grid", placeItems: "center", color: "var(--t-dim)" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <span className="spin" style={{ width: 16, height: 16, border: "2px solid var(--acc-soft)", borderTopColor: "var(--acc)", borderRadius: "50%" }} />
          加载中…
        </div>
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
