/* SkyOps — Processing page: shared pieces used across all three views */

const { useState: useStateP, useEffect: useEffectP, useRef: useRefP, useMemo: useMemoP } = React;

function fmtGB(bytes) {
  if (!bytes) return "—";
  return (bytes / 1e9).toFixed(1) + " GB";
}
function streamLabel(s) {
  return s === "both" ? "RGB + Thermal" : s === "rgb" ? "RGB" : "Thermal";
}
function statusTone(s) {
  return s === "completed" ? "ok"
       : s === "processing" ? "active"
       : s === "queued" ? "warn"
       : s === "failed" ? "crit"
       : s === "uploading" ? "active"
       : "";
}

/* Small building blocks used everywhere in Processing */

function StreamPill({ stream, size = "sm" }) {
  const icon = stream === "thermal" ? "thermometer" : stream === "rgb" ? "camera" : "split";
  return (
    <span className={`pp-stream pp-stream-${stream}`} data-size={size}>
      <Icon name={icon} size={10}/>
      {streamLabel(stream)}
    </span>
  );
}

function StatusDot({ status }) {
  const tone = statusTone(status);
  return (
    <span className={`pp-dot pp-dot-${tone}`} title={status}>
      <span className="pp-dot-core"/>
      {(status === "processing" || status === "uploading") && <span className="pp-dot-halo"/>}
    </span>
  );
}

function ProcTopbarActions({ activeView, setActiveView, streamFilter, setStreamFilter }) {
  return (
    <div className="pp-top-actions">
      <div className="pp-view-tabs">
        <button className={`pp-tab ${activeView === "ops" ? "is-on" : ""}`} onClick={() => setActiveView("ops")}>
          <Icon name="activity" size={12}/> Pipeline ops
        </button>
        <button className={`pp-tab ${activeView === "studio" ? "is-on" : ""}`} onClick={() => setActiveView("studio")}>
          <Icon name="cube" size={12}/> Processing studio
        </button>
        <button className={`pp-tab ${activeView === "compare" ? "is-on" : ""}`} onClick={() => setActiveView("compare")}>
          <Icon name="split" size={12}/> RGB ‖ Thermal compare
        </button>
      </div>
    </div>
  );
}

function JobCardSmall({ job, selected, onClick }) {
  return (
    <button className={`pp-jobcard ${selected ? "is-sel" : ""}`} onClick={onClick}>
      <div className="pp-jobcard-hd">
        <StatusDot status={job.status}/>
        <span className="pp-mono">{job.id}</span>
        <span className="pp-grow"/>
        <StreamPill stream={job.stream}/>
      </div>
      <div className="pp-jobcard-name">{job.sortie}</div>
      <div className="pp-jobcard-sub">{job.siteName} · {job.mic}</div>
      <div className="pp-jobcard-meta">
        <span><b>{job.images.toLocaleString()}</b> imgs</span>
        <span>{fmtGB(job.bytes)}</span>
        <span>{job.area_ha} ha</span>
        {job.status === "processing" && <span className="pp-eta">ETA {job.eta}</span>}
      </div>
      {(job.status === "processing" || job.status === "uploading") && (
        <div className="pp-progbar">
          <div className="pp-progbar-fill" style={{ width: `${Math.round(job.progress * 100)}%` }}/>
        </div>
      )}
      {job.status === "failed" && (
        <div className="pp-err-inline"><Icon name="alert" size={10}/>{job.error}</div>
      )}
    </button>
  );
}

function KeyStat({ label, value, mono, tone }) {
  return (
    <div className="pp-kstat">
      <div className="pp-kstat-l">{label}</div>
      <div className={`pp-kstat-v ${mono ? "pp-mono" : ""} ${tone ? "pp-kstat-"+tone : ""}`}>{value}</div>
    </div>
  );
}

/* Coverage / quality mini-chart — fake sparkline bars */
function MiniSpark({ points = 24, seed = 7, tone = "ok" }) {
  const bars = useMemoP(() => {
    let s = seed;
    return Array.from({ length: points }, (_, i) => {
      s = (s * 9301 + 49297) % 233280;
      const v = 0.35 + (s / 233280) * 0.65;
      return v;
    });
  }, [points, seed]);
  return (
    <div className={`pp-spark pp-spark-${tone}`}>
      {bars.map((v, i) => (
        <span key={i} style={{ height: `${v * 100}%` }}/>
      ))}
    </div>
  );
}

/* Small output chip used in sidebars */
function OutputChip({ output, disabled }) {
  return (
    <button className={`pp-outchip ${disabled ? "is-dis" : ""}`} disabled={disabled}>
      <span className="pp-outchip-tag">{output.tag}</span>
      <span className="pp-outchip-label">{output.label}</span>
      <span className="pp-outchip-size pp-mono">{output.size}</span>
      {!disabled && <Icon name="download" size={11}/>}
    </button>
  );
}

/* Stage cell (horizontal or vertical). Stage index determines completed/active/pending state. */
function StageCell({ stage, idx, activeIdx, streamFilter, compact }) {
  let state = "pending";
  if (idx < activeIdx) state = "done";
  else if (idx === activeIdx) state = "active";

  const dim = streamFilter !== "both" && stage.stream !== streamFilter && stage.stream !== "both";

  return (
    <div className={`pp-stage pp-stage-${state} ${dim ? "is-dim" : ""} ${compact ? "is-compact" : ""}`}>
      <div className="pp-stage-head">
        <span className="pp-stage-num pp-mono">{String(idx + 1).padStart(2, "0")}</span>
        <span className="pp-stage-name">{stage.name}</span>
        <span className="pp-grow"/>
        {state === "done" && <Icon name="check" size={11} className="pp-stage-check"/>}
        {state === "active" && <span className="pp-stage-pulse"/>}
      </div>
      {!compact && <div className="pp-stage-desc">{stage.desc}</div>}
      <div className="pp-stage-foot">
        <span className="pp-stage-dur pp-mono">{stage.dur}</span>
        {stage.stream !== "both" && <StreamPill stream={stage.stream} size="xs"/>}
      </div>
    </div>
  );
}

/* Live log — filterable, stage-tagged, follow/pause */
function LiveLog({ compact, job }) {
  const [lines, setLines] = useStateP(() => PROC_LOGS.slice());
  const [lvl, setLvl] = useStateP("all");
  const [stage, setStage] = useStateP("all");
  const [q, setQ] = useStateP("");
  const [follow, setFollow] = useStateP(true);
  const wrapRef = useRefP(null);
  const live = job && job.status === "processing";

  useEffectP(() => {
    if (!live || !follow) return;
    const id = setInterval(() => {
      setLines(prev => {
        const t = [
          ["I","[anomaly] tile 84/144 · 24 new detections"],
          ["D","[anomaly] GPU mem 9.6/16.0 GB · batch 24 · 128 fps"],
          ["I","[post] COG overview 4x · webp q=85"],
          ["W","[anomaly] border tile 9.E · thermal gain recalibrated"],
          ["S","[anomaly] ✔ tile batch 12 complete"],
        ][Math.floor(Math.random()*5)];
        const d = new Date();
        const ts = [d.getHours(),d.getMinutes(),d.getSeconds()].map(n => String(n).padStart(2,"0")).join(":");
        const next = [...prev, [ts, t[0], t[1]]];
        return next.length > 200 ? next.slice(-200) : next;
      });
    }, 1800);
    return () => clearInterval(id);
  }, [live, follow]);

  useEffectP(() => {
    if (follow && wrapRef.current) wrapRef.current.scrollTop = wrapRef.current.scrollHeight;
  }, [lines.length, follow, lvl, stage, q]);

  const parse = ([t, l, m]) => {
    const mm = m.match(/^\[([a-z]+)\]\s*(.*)$/i);
    return { t, l, stg: mm ? mm[1] : "sys", msg: mm ? mm[2] : m };
  };
  const all = lines.map(parse);
  const stages = Array.from(new Set(all.map(x => x.stg)));
  const counts = { all: all.length, E: all.filter(x => x.l === "E").length, W: all.filter(x => x.l === "W").length, I: all.filter(x => x.l === "I" || x.l === "S").length, D: all.filter(x => x.l === "D").length };
  const vis = all.filter(x =>
    (lvl === "all" || (lvl === "I" ? (x.l === "I" || x.l === "S") : x.l === lvl)) &&
    (stage === "all" || x.stg === stage) &&
    (!q || x.msg.toLowerCase().includes(q.toLowerCase())));
  const LV = { I: "info", S: "ok", W: "warn", E: "error", D: "debug" };

  return (
    <div className="lg">
      <div className="lg-hd">
        <div className="lg-title"><Icon name="terminal" size={12}/>Processing log
          {live ? <span className={`lg-live ${follow ? "" : "is-paused"}`}><i/>{follow ? "live" : "paused"}</span> : <span className="lg-live is-off">archived</span>}
        </div>
        <div className="lg-hd-r">
          {live && <button className="lg-btn" onClick={() => setFollow(v => !v)} title={follow ? "Pause tail" : "Resume tail"}><Icon name={follow ? "pause" : "play"} size={11}/></button>}
          <button className="lg-btn" title="Download log"><Icon name="download" size={11}/></button>
        </div>
      </div>
      <div className="lg-tools">
        <div className="lg-lvls">
          {[["all","All"],["E","Errors"],["W","Warnings"],["I","Info"],["D","Debug"]].map(([k,l]) => (
            <button key={k} className={`lg-lvl lg-lvl-${k} ${lvl === k ? "is-on" : ""}`} onClick={() => setLvl(k)}>{l}<em>{counts[k]}</em></button>
          ))}
        </div>
        <div className="lg-row2">
          <div className="lg-search"><Icon name="search" size={11}/><input value={q} onChange={e => setQ(e.target.value)} placeholder="Filter messages"/></div>
          <select className="lg-stage" value={stage} onChange={e => setStage(e.target.value)}>
            <option value="all">All stages</option>
            {stages.map(s => <option key={s} value={s}>{s}</option>)}
          </select>
        </div>
      </div>
      <div className="lg-body" ref={wrapRef} onScroll={(e) => { const el = e.currentTarget; if (el.scrollHeight - el.scrollTop - el.clientHeight > 40 && follow) setFollow(false); }}>
        {vis.map((x, i) => (
          <div key={i} className={`lg-ln lg-${LV[x.l]}`}>
            <span className="lg-t">{x.t}</span>
            <span className="lg-l">{LV[x.l]}</span>
            <span className="lg-s">{x.stg}</span>
            <span className="lg-m">{x.msg}</span>
          </div>
        ))}
        {vis.length === 0 && <div className="lg-empty">No lines match the current filters.</div>}
      </div>
      <div className="lg-foot mono">
        <span>{vis.length} / {all.length} lines</span>
        {counts.W > 0 && <button className="lg-jump" onClick={() => setLvl("W")}>{counts.W} warnings</button>}
        {counts.E > 0 && <button className="lg-jump is-err" onClick={() => setLvl("E")}>{counts.E} errors</button>}
        {!follow && live && <button className="lg-jump is-follow" onClick={() => setFollow(true)}>Jump to latest ↓</button>}
      </div>
    </div>
  );
}

Object.assign(window, {
  fmtGB, streamLabel, statusTone,
  StreamPill, StatusDot, ProcTopbarActions,
  JobCardSmall, KeyStat, MiniSpark, OutputChip,
  StageCell, LiveLog,
});
