// Review & score dashboard. Shows real trajectory submissions from two
// sources, unioned by id:
//   1. localStorage["caa_submitted_trajectories"] — what this annotator has submitted on this device.
//   2. GET /api/trajectories — the D1-backed server store, if the Pages
//      Functions backend is deployed. Silently skipped otherwise.
function ReviewDash({ setRoute, setActive }) {
  const { t: tr } = useLang();
  const [local, setLocal] = React.useState([]);
  const [server, setServer] = React.useState([]);
  const [serverStatus, setServerStatus] = React.useState("checking"); // "checking" | "ok" | "unavailable"
  const [selected, setSelected] = React.useState(null); // full trajectory for detail modal
  const [query, setQuery] = React.useState("");
  const [onlyMine, setOnlyMine] = React.useState(false);

  const user = React.useMemo(() => {
    try { return JSON.parse(localStorage.getItem("caa_user") || "null"); }
    catch { return null; }
  }, []);

  // Load local submissions.
  const loadLocal = React.useCallback(() => {
    try {
      const arr = JSON.parse(localStorage.getItem("caa_submitted_trajectories") || "[]");
      setLocal(Array.isArray(arr) ? arr : []);
    } catch { setLocal([]); }
  }, []);
  React.useEffect(() => { loadLocal(); }, [loadLocal]);

  // Attempt to load server submissions. Treat 404/501/non-JSON as "backend not deployed".
  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const res = await fetch("/api/trajectories?limit=500");
        const ct = res.headers.get("content-type") || "";
        if (!res.ok || !ct.includes("application/json")) {
          if (!cancelled) setServerStatus("unavailable");
          return;
        }
        const data = await res.json();
        if (!cancelled) {
          setServer(Array.isArray(data.items) ? data.items : []);
          setServerStatus("ok");
        }
      } catch {
        if (!cancelled) setServerStatus("unavailable");
      }
    })();
    return () => { cancelled = true; };
  }, []);

  // Merge by id — local wins for messages since server "list" is a summary.
  const merged = React.useMemo(() => {
    const byId = new Map();
    for (const s of server) byId.set(s.id, { ...s, _source: "server" });
    for (const l of local) {
      const prior = byId.get(l.id);
      byId.set(l.id, { ...(prior || {}), ...l, _source: prior ? "both" : "local" });
    }
    return Array.from(byId.values()).sort((a, b) => (b.submitted_at || "").localeCompare(a.submitted_at || ""));
  }, [local, server]);

  const filtered = React.useMemo(() => {
    const q = query.trim().toLowerCase();
    return merged.filter(s => {
      if (onlyMine && user?.email && s.annotator_email !== user.email) return false;
      if (!q) return true;
      return (
        (s.task_id || "").toLowerCase().includes(q) ||
        (s.annotator_email || "").toLowerCase().includes(q) ||
        (s.annotator_name || "").toLowerCase().includes(q) ||
        (s.model || "").toLowerCase().includes(q)
      );
    });
  }, [merged, query, onlyMine, user]);

  // Stats from real data.
  const stats = React.useMemo(() => {
    const total = merged.length;
    const completed = merged.filter(s => s.completed).length;
    const rewards = merged
      .map(s => s.reward_summary?.reward_sum)
      .filter(v => typeof v === "number");
    const meanReward = rewards.length
      ? (rewards.reduce((a, b) => a + b, 0) / rewards.length).toFixed(2)
      : "—";
    const rollupVals = merged
      .flatMap(s => s.rollup ? Object.values(s.rollup) : [])
      .filter(v => typeof v === "number");
    const meanRollup = rollupVals.length
      ? (rollupVals.reduce((a, b) => a + b, 0) / rollupVals.length).toFixed(2)
      : "—";
    return { total, completed, meanReward, meanRollup };
  }, [merged]);

  const openInSimulator = (s) => {
    // Hand the trajectory (or just its id) to the simulator so it can load it
    // on mount. The simulator consumes and clears this hint.
    window.__caaLoadTrajectory = Array.isArray(s.messages)
      ? { task_id: s.task_id, trajectory: s }
      : { task_id: s.task_id, id: s.id };
    setActive(s.task_id);
    setRoute("simulate");
  };

  const openDetail = async (s) => {
    // If we only have the server summary (no messages), fetch full record.
    if (!s.messages && s.id && s._source === "server") {
      try {
        const res = await fetch(`/api/trajectories/${s.id}`);
        if (res.ok) {
          const full = await res.json();
          setSelected(full);
          return;
        }
      } catch {}
    }
    setSelected(s);
  };

  const deleteLocal = (id) => {
    if (!confirm("Delete this submission from local storage? (Server copy, if any, is unaffected.)")) return;
    try {
      const arr = JSON.parse(localStorage.getItem("caa_submitted_trajectories") || "[]");
      const next = arr.filter(s => s.id !== id);
      localStorage.setItem("caa_submitted_trajectories", JSON.stringify(next));
      loadLocal();
    } catch {}
  };

  const downloadOne = (s) => {
    downloadBlob(`trajectory_${s.task_id}_${s.id || Date.now()}.json`, JSON.stringify(s, null, 2));
  };

  return (
    <div style={{padding:"24px 32px", overflow:"auto", height:"100%"}}>
      <div style={{display:"flex", alignItems:"flex-end", marginBottom:20, gap:14}}>
        <div style={{flex:1}}>
          <h1 style={{margin:"0 0 4px", fontSize:22, fontWeight:600, letterSpacing:"-0.01em"}}>{tr("rev_title") || "Review & score"}</h1>
          <p style={{margin:0, color:"var(--ink-dim)", fontSize:13}}>
            Submitted trajectories — per-turn rewards, rollup scores, and tool traces from every annotator run.
          </p>
        </div>
        <div style={{fontSize:11.5, fontFamily:"var(--font-mono)", color:"var(--ink-mute)"}}>
          {serverStatus === "checking" && "server: checking…"}
          {serverStatus === "ok" && `server: ${server.length} records (D1)`}
          {serverStatus === "unavailable" && "server: offline (local only)"}
        </div>
      </div>

      <div style={{display:"grid", gridTemplateColumns:"repeat(4, 1fr)", gap:14, marginBottom:24}}>
        {[
          { l: "Submissions",    v: stats.total.toLocaleString(),     sub: `${local.length} local · ${server.length} server` },
          { l: "Completed runs", v: stats.completed.toLocaleString(), sub: "record_diagnosis fired" },
          { l: "Mean reward",    v: stats.meanReward,                 sub: "∑ per-turn scores" },
          { l: "Mean rollup",    v: stats.meanRollup,                 sub: "/ 5 across dimensions" },
        ].map(s => (
          <div key={s.l} style={{border:"1px solid var(--line)", borderRadius:6, padding:"14px 16px", background:"var(--bg-panel)"}}>
            <div style={{fontSize:11.5, color:"var(--ink-mute)", fontFamily:"var(--font-mono)", textTransform:"uppercase", letterSpacing:"0.05em"}}>{s.l}</div>
            <div style={{fontSize:26, fontWeight:600, letterSpacing:"-0.02em", marginTop:4, fontFamily:"var(--font-mono)"}}>{s.v}</div>
            <div style={{fontSize:11.5, color:"var(--ink-dim)"}}>{s.sub}</div>
          </div>
        ))}
      </div>

      <div style={{display:"flex", gap:10, marginBottom:12, alignItems:"center"}}>
        <input
          value={query}
          onChange={e => setQuery(e.target.value)}
          placeholder="Filter by task id, annotator, or model…"
          style={{flex:1, padding:"6px 10px", border:"1px solid var(--line)", borderRadius:4, fontSize:12.5}}
        />
        {user?.email && (
          <label style={{fontSize:12, color:"var(--ink-dim)", display:"flex", alignItems:"center", gap:6}}>
            <input type="checkbox" checked={onlyMine} onChange={e => setOnlyMine(e.target.checked)}/>
            only mine ({user.email})
          </label>
        )}
        <button className="btn ghost" onClick={loadLocal} style={{padding:"4px 10px", fontSize:11.5}}>refresh</button>
      </div>

      <div style={{border:"1px solid var(--line)", borderRadius:6, background:"var(--bg-panel)"}}>
        <div style={{padding:"10px 14px", borderBottom:"1px solid var(--line)", display:"grid", gridTemplateColumns:"160px 1fr 150px 110px 90px 90px 140px", gap:14, fontSize:11, fontFamily:"var(--font-mono)", color:"var(--ink-mute)", textTransform:"uppercase", letterSpacing:"0.03em"}}>
          <span>submitted</span>
          <span>task · diagnosis</span>
          <span>annotator</span>
          <span>model</span>
          <span style={{textAlign:"right"}}>reward</span>
          <span style={{textAlign:"center"}}>status</span>
          <span style={{textAlign:"right"}}>actions</span>
        </div>
        {filtered.length === 0 ? (
          <div style={{padding:28, textAlign:"center", color:"var(--ink-mute)", fontSize:13}}>
            No trajectories{query || onlyMine ? " match the filter" : " submitted yet"}. Run a simulation and click <b>Submit trajectory to RL dataset</b>.
          </div>
        ) : filtered.map(s => (
          <div key={s.id || `${s.task_id}_${s.submitted_at}`}
            style={{display:"grid", gridTemplateColumns:"160px 1fr 150px 110px 90px 90px 140px", gap:14, padding:"10px 14px", borderBottom:"1px solid var(--line)", alignItems:"center", fontSize:12.5}}>
            <span style={{fontFamily:"var(--font-mono)", fontSize:11.5, color:"var(--ink-dim)"}}>
              {(s.submitted_at || "").slice(0,19).replace("T"," ")}
            </span>
            <span style={{minWidth:0, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap"}}>
              <code style={{fontFamily:"var(--font-mono)", fontSize:11.5}}>{s.task_id}</code>
              {s.reference_diagnosis && (
                <span style={{color:"var(--ink-mute)", marginLeft:8}}>· {s.reference_diagnosis}</span>
              )}
            </span>
            <span style={{color:"var(--ink-dim)", fontSize:11.5, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap"}}>
              {s.annotator_name || s.annotator_email || "—"}
            </span>
            <span style={{fontFamily:"var(--font-mono)", fontSize:11.5, color:"var(--ink-dim)", overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap"}}>
              {s.model || "—"}
            </span>
            <span style={{fontFamily:"var(--font-mono)", fontSize:12, textAlign:"right"}}>
              {s.reward_summary?.reward_sum != null
                ? `${s.reward_summary.reward_sum} (${s.reward_summary.turns_scored}t)`
                : "—"}
            </span>
            <span style={{textAlign:"center"}}>
              {s.completed
                ? <span className="tag green">✓ dx</span>
                : <span className="tag amber">partial</span>}
            </span>
            <span style={{textAlign:"right", display:"flex", gap:4, justifyContent:"flex-end"}}>
              <button className="btn ghost" onClick={() => openDetail(s)} style={{padding:"2px 6px", fontSize:11}}>view</button>
              <button className="btn ghost" onClick={() => openInSimulator(s)} style={{padding:"2px 6px", fontSize:11}} title="Load this task in the simulator">sim</button>
              <button className="btn ghost" onClick={() => downloadOne(s)} style={{padding:"2px 6px", fontSize:11}}>⇓</button>
              {s._source !== "server" && (
                <button className="btn ghost" onClick={() => deleteLocal(s.id)}
                  style={{padding:"2px 6px", fontSize:11, color:"var(--red, #b91c1c)"}}>×</button>
              )}
            </span>
          </div>
        ))}
      </div>

      {selected && (
        <TrajectoryDetailModal s={selected} onClose={() => setSelected(null)}/>
      )}
    </div>
  );
}

function TrajectoryDetailModal({ s, onClose }) {
  return (
    <div onClick={onClose} style={{position:"fixed", inset:0, background:"rgba(0,0,0,.4)", zIndex:50, display:"flex", alignItems:"center", justifyContent:"center"}}>
      <div onClick={e => e.stopPropagation()} style={{background:"var(--bg, #fff)", border:"1px solid var(--line)", borderRadius:6, width:860, maxWidth:"94vw", maxHeight:"86vh", padding:20, fontSize:13, display:"flex", flexDirection:"column"}}>
        <div style={{display:"flex", alignItems:"flex-start", marginBottom:12, gap:10}}>
          <div style={{flex:1}}>
            <h3 style={{margin:"0 0 2px", fontSize:15}}>{s.task_id}</h3>
            <div style={{fontSize:11.5, color:"var(--ink-mute)", fontFamily:"var(--font-mono)"}}>
              {s.annotator_email || s.annotator_name || "anonymous"} · {(s.submitted_at||"").slice(0,19).replace("T"," ")} · {s.model}
            </div>
          </div>
          <button className="btn ghost" onClick={onClose} style={{padding:"2px 10px"}}>×</button>
        </div>
        <div style={{display:"grid", gridTemplateColumns:"repeat(3, 1fr)", gap:10, marginBottom:12, fontSize:11.5}}>
          <div style={{border:"1px solid var(--line)", borderRadius:4, padding:"6px 10px"}}>
            <div style={{color:"var(--ink-mute)", fontFamily:"var(--font-mono)"}}>reference dx</div>
            <div>{s.reference_diagnosis || "—"}</div>
          </div>
          <div style={{border:"1px solid var(--line)", borderRadius:4, padding:"6px 10px"}}>
            <div style={{color:"var(--ink-mute)", fontFamily:"var(--font-mono)"}}>recorded dx</div>
            <div>{s.recorded_diagnosis?.diagnosis || (s.completed ? "—" : "not recorded")}</div>
          </div>
          <div style={{border:"1px solid var(--line)", borderRadius:4, padding:"6px 10px"}}>
            <div style={{color:"var(--ink-mute)", fontFamily:"var(--font-mono)"}}>reward</div>
            <div>
              {s.reward_summary?.reward_sum != null
                ? `${s.reward_summary.reward_sum} over ${s.reward_summary.turns_scored} turn${s.reward_summary.turns_scored===1?"":"s"}`
                : "—"}
            </div>
          </div>
        </div>
        {s.rollup && Object.values(s.rollup).some(v => v != null) && (
          <div style={{fontSize:11.5, color:"var(--ink-dim)", fontFamily:"var(--font-mono)", marginBottom:10}}>
            rollup — {["clinical_accuracy","dialogue_fluency","safety_empathy"].map(k => `${k}: ${s.rollup[k] ?? "—"}`).join(" · ")}
          </div>
        )}
        {s.notes && (
          <div style={{fontSize:12, color:"var(--ink-dim)", marginBottom:10, padding:"8px 10px", borderLeft:"3px solid var(--line)", background:"var(--bg-sunken)"}}>
            <div style={{fontSize:10.5, fontFamily:"var(--font-mono)", color:"var(--ink-mute)", marginBottom:4}}>ANNOTATOR NOTE</div>
            {s.notes}
          </div>
        )}
        <div style={{flex:1, overflowY:"auto", border:"1px solid var(--line)", borderRadius:4, padding:"8px 12px", background:"var(--bg-sunken)"}}>
          {(s.messages || []).map((m, i) => (
            <div key={i} style={{padding:"6px 0", borderBottom: i<(s.messages.length-1) ? "1px dashed var(--line)":"none"}}>
              <div style={{fontSize:10.5, color:"var(--ink-mute)", fontFamily:"var(--font-mono)"}}>
                #{String(m.idx ?? i).padStart(2,'0')} · <b style={{color:"var(--ink-dim)"}}>{m.role}</b>
                {m.role==='tool' && <span> · {m.name}</span>}
                {s.scores && s.scores[m.idx ?? i] != null && (
                  <span style={{marginLeft:8, color:"var(--ink)"}}>reward {s.scores[m.idx ?? i]}</span>
                )}
              </div>
              {m.role === "tool" ? (
                <div style={{fontFamily:"var(--font-mono)", fontSize:11.5, color:"var(--ink-dim)", marginTop:2}}>
                  args={JSON.stringify(m.args)} → {JSON.stringify(m.returns)}
                </div>
              ) : (
                <div style={{fontSize:13, lineHeight:1.4, marginTop:2}}>{m.text}</div>
              )}
            </div>
          ))}
        </div>
        <div style={{display:"flex", justifyContent:"flex-end", gap:8, marginTop:12}}>
          <button className="btn" onClick={() => downloadBlob(`trajectory_${s.task_id}_${s.id || Date.now()}.json`, JSON.stringify(s, null, 2))}>
            ⇓ Download JSON
          </button>
          <button className="btn ghost" onClick={onClose}>Close</button>
        </div>
      </div>
    </div>
  );
}

window.ReviewDash = ReviewDash;
