/* SkyOps — Processing View A: Pipeline Ops
   Ops-center layout: left rail of jobs, center stage tracker + stage detail, right logs + outputs.
   Top hero: active MIC upload bundle + progress. */

const { useState: useStateVA, useMemo: useMemoVA } = React;

function UploadHero({ job, streamFilter }) {
  if (!job || (job.status !== "processing" && job.status !== "uploading" && job.status !== "queued")) return null;

  const rgbProgress = Math.min(1, job.progress + 0.12);
  const thProgress = Math.max(0, job.progress - 0.08);
  const bandwidth = "284 MB/s";
  const queuedFiles = Math.round(job.images * (1 - job.progress));

  return (
    <div className="pp-hero">
      <div className="pp-hero-l">
        <div className="pp-hero-kicker">
          <Icon name="upload" size={11}/>
          Active bundle · {job.mic} · {job.sortie}
        </div>
        <div className="pp-hero-title">
          <span className="pp-mono pp-hero-id">{job.id}</span>
          <span className="pp-hero-sep">·</span>
          <span>{job.siteName}</span>
        </div>
        <div className="pp-hero-stats">
          <KeyStat label="Images" value={job.images.toLocaleString()} mono/>
          <KeyStat label="Thermal pairs" value={job.thermalImages.toLocaleString()} mono/>
          <KeyStat label="Bundle size" value={fmtGB(job.bytes)} mono/>
          <KeyStat label="Pilot" value={job.pilot}/>
          <KeyStat label="Airframe" value={job.drone}/>
          <KeyStat label="Engine" value={job.engine} mono/>
        </div>
        <div className="pp-hero-bw">
          <span className="pp-hero-bw-ring">
            <svg viewBox="0 0 36 36" width="44" height="44">
              <circle cx="18" cy="18" r="15" fill="none" stroke="var(--border-base)" strokeWidth="2.5"/>
              <circle cx="18" cy="18" r="15" fill="none" stroke="var(--accent-primary)" strokeWidth="2.5"
                strokeDasharray={`${job.progress * 94.2} 100`} strokeLinecap="round" transform="rotate(-90 18 18)"/>
            </svg>
            <span className="pp-mono">{Math.round(job.progress * 100)}<em>%</em></span>
          </span>
          <div className="pp-hero-bw-body">
            <div className="pp-hero-bw-row">
              <span className="pp-caps">RGB stream</span>
              <span className="pp-mono pp-muted">{Math.round(rgbProgress * 100)}%</span>
            </div>
            <div className="pp-progbar"><div className="pp-progbar-fill pp-progbar-rgb" style={{ width: `${rgbProgress * 100}%` }}/></div>
            <div className="pp-hero-bw-row" style={{ marginTop: 6 }}>
              <span className="pp-caps">Thermal stream</span>
              <span className="pp-mono pp-muted">{Math.round(thProgress * 100)}%</span>
            </div>
            <div className="pp-progbar"><div className="pp-progbar-fill pp-progbar-thermal" style={{ width: `${thProgress * 100}%` }}/></div>
            <div className="pp-hero-bw-foot">
              <span><Icon name="wifi" size={10}/> {bandwidth}</span>
              <span className="pp-mono pp-muted">ETA {job.eta}</span>
              <span className="pp-mono pp-muted">{queuedFiles.toLocaleString()} queued</span>
            </div>
          </div>
        </div>
      </div>

      <div className="pp-hero-note"><Icon name="upload" size={14}/><div><b>Ingest from Capture</b><span>Frames are uploaded in stage 3 · Capture. This view tracks processing only.</span></div></div>
    </div>
  );
}

function PipelineTrack({ job, streamFilter }) {
  const activeIdx = job?.activeStage ?? 0;
  return (
    <div className="pp-track">
      <div className="pp-track-hd">
        <div>
          <div className="pp-caps">Pipeline stages</div>
          <div className="pp-track-title">ODM → OpenSplat · SkyOps anomaly stack v3.1</div>
        </div>
        <div className="pp-track-legend">
          <span><i className="pp-leg-dot pp-leg-done"/>Done</span>
          <span><i className="pp-leg-dot pp-leg-active"/>Active</span>
          <span><i className="pp-leg-dot pp-leg-pending"/>Pending</span>
        </div>
      </div>
      <div className="pp-track-grid">
        {PROC_STAGES.map((s, i) => (
          <StageCell key={s.key} stage={s} idx={i} activeIdx={activeIdx} streamFilter={streamFilter}/>
        ))}
      </div>
    </div>
  );
}

function JobList({ jobs, sel, onSel }) {
  const [filter, setFilter] = useStateVA("all");
  const filtered = useMemoVA(() => {
    if (filter === "all") return jobs;
    return jobs.filter(j => j.status === filter);
  }, [jobs, filter]);

  return (
    <div className="pp-joblist">
      <div className="pp-joblist-hd">
        <div className="pp-caps">Job queue</div>
        <div className="pp-grow"/>
        <div className="pp-filter-chips">
          {[["all","All",jobs.length],
            ["processing","Active", jobs.filter(j=>j.status==="processing").length],
            ["queued","Queued", jobs.filter(j=>j.status==="queued").length],
            ["completed","Done", jobs.filter(j=>j.status==="completed").length],
            ["failed","Failed", jobs.filter(j=>j.status==="failed").length]].map(([k,l,n]) => (
              <button key={k} className={`pp-chip ${filter===k?"is-on":""}`} onClick={() => setFilter(k)}>
                {l} <em>{n}</em>
              </button>
          ))}
        </div>
      </div>
      <div className="pp-joblist-body">
        {filtered.map(j => (
          <JobCardSmall key={j.id} job={j} selected={sel === j.id} onClick={() => onSel(j.id)}/>
        ))}
        {filtered.length === 0 && (
          <div className="pp-empty"><Icon name="check_circle" size={14}/> No jobs match this filter</div>
        )}
      </div>
    </div>
  );
}

function JobDetailPanel({ job, streamFilter }) {
  if (!job) return <div className="pp-empty">Select a job</div>;
  const st = job.status;
  const pct = Math.round((job.progress || 0) * 100);
  const stages = PROC_STAGES;
  const cur = stages[Math.min(job.activeStage, stages.length - 1)];
  const done = st === "completed";
  const failed = st === "failed";
  const stageOf = { ortho: 4, dsm: 4, dtm: 4, pc: 3, mesh: 3, thermal: 5, ndvi: 4, kmz: 7, report: 7 };
  const outs = PROC_OUTPUTS.filter(o => streamFilter === "both" || o.stream === streamFilter || o.stream === "both");
  const outState = (o) => done ? "ready" : failed ? "blocked" : (job.activeStage > (stageOf[o.key] ?? 7)) ? "ready" : (job.activeStage === (stageOf[o.key] ?? 7)) ? "building" : "queued";
  const groups = [
    ["Rasters", ["ortho","thermal","dsm","dtm","ndvi"]],
    ["3D", ["pc","mesh"]],
    ["Delivery", ["kmz","report"]],
  ];
  const fmtN = (n) => n == null ? "—" : n.toLocaleString();
  const qc = [
    { l: "Coverage", v: job.coverage ? job.coverage : null, u: "%", t: 95, dir: 1, max: 100 },
    { l: "GSD", v: job.gsd, u: " cm/px", t: 1.5, dir: -1, max: 3 },
    { l: "Reprojection error", v: job.coverage ? 0.62 : null, u: " px", t: 1.0, dir: -1, max: 2 },
    { l: "Thermal ↔ RGB alignment", v: job.coverage ? 0.94 : null, u: " px", t: 1.0, dir: -1, max: 2 },
  ];
  const qcState = (q) => q.v == null ? "na" : (q.dir > 0 ? q.v >= q.t : q.v <= q.t) ? "pass" : "warn";
  const densityHa = job.coverage ? (job.anomaliesDetected / job.area_ha).toFixed(1) : null;

  return (
    <div className="jd">
      <div className="jd-hd">
        <div className="jd-hd-l">
          <span className={`pp-badge pp-badge-${statusTone(st)}`}><StatusDot status={st}/>{st}</span>
          <div>
            <div className="jd-id mono">{job.id}</div>
            <div className="jd-sub">{job.sortie} · {job.siteName} · {job.engine}</div>
          </div>
        </div>
        <div className="jd-hd-r">
          {st === "processing" && <><button className="btn btn-ghost btn-xs"><Icon name="pause" size={11}/>Pause</button><button className="btn btn-ghost btn-xs"><Icon name="x" size={11}/>Cancel</button></>}
          {failed && <button className="btn btn-primary btn-xs"><Icon name="refresh" size={11}/>Retry from {cur.name}</button>}
          {done && <button className="btn btn-primary btn-xs"><Icon name="arrow_right" size={11}/>Review {fmtN(job.anomaliesDetected)} detections</button>}
        </div>
      </div>

      {failed ? (
        <div className="jd-fail">
          <Icon name="alert" size={16}/>
          <div className="jd-fail-b">
            <div className="jd-fail-t">Failed at stage {job.activeStage + 1} · {cur.name}</div>
            <div className="jd-fail-m">{job.error}</div>
            <div className="jd-fail-fix"><span className="caps">Suggested fix</span> Re-run SfM with GCP constraints, or re-fly sector 4 with RTK fix verified. Outputs from stages 1–{job.activeStage} are kept.</div>
          </div>
        </div>
      ) : (
        <div className="jd-prog">
          <div className="jd-prog-row">
            <span className="jd-prog-stage">{done ? "All stages complete" : <>Stage {job.activeStage + 1}/{stages.length} · <b>{cur.name}</b> — {cur.desc}</>}</span>
            <span className="mono jd-prog-pct">{pct}%</span>
          </div>
          <div className="jd-prog-bar">
            {stages.map((s, i) => (
              <span key={s.key} className={`jd-seg ${i < job.activeStage || done ? "is-done" : i === job.activeStage ? "is-cur" : ""}`} title={s.name}/>
            ))}
          </div>
          <div className="jd-prog-meta mono">
            <span>Started {job.startedAt}</span>
            {st === "processing" && <span>ETA {job.eta}</span>}
            <span>{fmtGB(job.bytes)} in</span>
          </div>
        </div>
      )}

      <div className="jd-facts">
        <div className="jd-fact-g">
          <div className="jd-fact-h"><Icon name="camera" size={11}/>Capture</div>
          <div className="jd-kv"><span>RGB frames</span><b className="mono">{fmtN(job.images)}</b></div>
          <div className="jd-kv"><span>Thermal frames</span><b className="mono">{fmtN(job.thermalImages)}</b></div>
          <div className="jd-kv"><span>Area</span><b className="mono">{job.area_ha} ha</b></div>
          <div className="jd-kv"><span>Bundle</span><b className="mono">{fmtGB(job.bytes)}</b></div>
        </div>
        <div className="jd-fact-g">
          <div className="jd-fact-h"><Icon name="cube" size={11}/>Reconstruction</div>
          <div className="jd-kv"><span>Tie points</span><b className="mono">{fmtN(job.sfmPoints)}</b></div>
          <div className="jd-kv"><span>Dense points</span><b className="mono">{job.densePoints ? (job.densePoints/1e6).toFixed(1) + "M" : "—"}</b></div>
          <div className="jd-kv"><span>GSD</span><b className="mono">{job.gsd ? job.gsd + " cm/px" : "—"}</b></div>
          <div className="jd-kv"><span>Coverage</span><b className="mono">{job.coverage ? job.coverage + "%" : "—"}</b></div>
        </div>
        <div className="jd-fact-g">
          <div className="jd-fact-h"><Icon name="thermometer" size={11}/>Intelligence</div>
          <div className="jd-kv"><span>Detections</span><b className="mono">{fmtN(job.anomaliesDetected)}</b></div>
          <div className="jd-kv"><span>Per hectare</span><b className="mono">{densityHa ?? "—"}</b></div>
          <div className="jd-kv"><span>Model</span><b className="mono">yolov11n v3.1</b></div>
          <div className="jd-kv"><span>Staged to review</span><b className="mono">{done ? "yes" : "on completion"}</b></div>
        </div>
      </div>

      <div className="jd-sec">
        <div className="jd-sec-h">Quality gates<span className="sub">thresholds from MIC · {job.mic}</span></div>
        <div className="jd-qc">
          {qc.map(q => {
            const s = qcState(q);
            const w = q.v == null ? 0 : Math.min(100, (q.v / q.max) * 100);
            const tw = (q.t / q.max) * 100;
            return (
              <div key={q.l} className={`jd-qc-row is-${s}`}>
                <span className="jd-qc-l">{q.l}</span>
                <span className="jd-qc-bar"><span className="jd-qc-fill" style={{ width: w + "%" }}/><span className="jd-qc-th" style={{ left: tw + "%" }}/></span>
                <span className="mono jd-qc-v">{q.v == null ? "—" : q.v + q.u}</span>
                <span className="jd-qc-s">{s === "pass" ? <><Icon name="check" size={10}/>pass</> : s === "warn" ? <><Icon name="alert" size={10}/>below</> : "pending"}</span>
              </div>
            );
          })}
        </div>
      </div>

      <div className="jd-sec">
        <div className="jd-sec-h">Outputs<button className="jd-files" onClick={() => { localStorage.setItem("skyops_ws_stage","files"); window.dispatchEvent(new CustomEvent("skyops:stage",{detail:"files"})); }}>Open in Files →</button><span className="sub">{outs.filter(o => outState(o) === "ready").length} of {outs.length} ready</span></div>
        <div className="jd-outs">
          {groups.map(([g, keys]) => {
            const items = outs.filter(o => keys.includes(o.key));
            if (!items.length) return null;
            return (
              <div key={g} className="jd-out-g">
                <div className="caps">{g}</div>
                {items.map(o => {
                  const s = outState(o);
                  return (
                    <div key={o.key} className={`jd-out is-${s}`}>
                      <span className="jd-out-tag mono">{o.tag}</span>
                      <span className="jd-out-l">{o.label}</span>
                      <span className="mono jd-out-sz">{s === "ready" ? o.size : ""}</span>
                      <span className="jd-out-s">{s === "ready" ? <button className="fx-dl"><Icon name="download" size={11}/></button> : s === "building" ? "building…" : s === "blocked" ? "blocked" : "queued"}</span>
                    </div>
                  );
                })}
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

function ViewOps({ jobs, selJob, selJobId, setSelJobId, streamFilter }) {
  return (
    <div className="pp-view-ops">
      <UploadHero job={selJob} streamFilter={streamFilter}/>
      <div className="pp-ops-row pp-ops-row-main">
        <div className="pp-ops-rail">
          <JobList jobs={jobs} sel={selJobId} onSel={setSelJobId}/>
        </div>
        <div className="pp-ops-center">
          <PipelineTrack job={selJob} streamFilter={streamFilter}/>
          <JobDetailPanel job={selJob} streamFilter={streamFilter}/>
        </div>
      </div>
      <div className="pp-ops-row pp-ops-row-obs">
        <LiveLog job={selJob}/>
        <OpsInfraPanel/>
      </div>
    </div>
  );
}

function OpsInfraPanel() {
  const nodes = [
    { name: "GPU-04", kind: "RTX 4090 · 24 GB", status: "busy", util: 0.78, vram: [18.4, 24], temp: 71, job: "JOB-2026-04-033.A", stage: "Anomaly detection", eta: "19m" },
    { name: "CPU-A",  kind: "Xeon 32C · 128 GB", status: "busy", util: 0.45, vram: [58, 128], temp: 54, job: "JOB-2026-04-033.B", stage: "Post-process · COG", eta: "6m", ram: true },
    { name: "GPU-02", kind: "RTX 4090 · 24 GB", status: "idle", util: 0.02, vram: [0.6, 24], temp: 38, next: "JOB-2026-04-028.A" },
    { name: "GPU-01", kind: "RTX 3090 · 24 GB", status: "offline", util: 0, vram: [0, 24], temp: null, note: "Driver update · back 14:00" },
  ];
  const busy = nodes.filter(n => n.status === "busy").length;
  const avail = nodes.filter(n => n.status !== "offline").length;
  const queue = [
    { id: "JOB-2026-04-028.A", site: "Zaragoza North", imgs: "2,210", est: "≈ 1h 40m", on: "GPU-02" },
    { id: "JOB-2026-04-025.A", site: "Albacete Solar I", imgs: "3,102", est: "retry pending", on: "—" },
  ];
  return (
    <div className="fl">
      <div className="fl-hd">
        <div className="fl-title"><Icon name="cpu" size={12}/>Shared compute fleet</div>
        <span className="mono fl-sum">{busy}/{avail} busy · {queue.length} queued</span>
      </div>
      <div className="fl-cap">
        <div className="fl-cap-it"><span className="caps">GPU util</span><b className="mono">{Math.round(nodes.filter(n => n.name.startsWith("GPU") && n.status !== "offline").reduce((a,n) => a + n.util, 0) / 2 * 100)}%</b></div>
        <div className="fl-cap-it"><span className="caps">Throughput</span><b className="mono">128 fps</b></div>
        <div className="fl-cap-it"><span className="caps">Cost today</span><b className="mono">€18.40</b></div>
      </div>
      <div className="fl-nodes">
        {nodes.map(n => (
          <div key={n.name} className={`fl-node is-${n.status}`}>
            <div className="fl-node-hd">
              <span className="fl-dot"/>
              <span className="mono fl-name">{n.name}</span>
              <span className="fl-kind">{n.kind}</span>
              <span className={`fl-st fl-st-${n.status}`}>{n.status}</span>
            </div>
            {n.status !== "offline" && (
              <div className="fl-meters">
                <div className="fl-m"><span>{n.ram ? "CPU" : "GPU"}</span><span className="fl-bar"><i style={{ width: n.util*100 + "%" }} className={n.util > 0.9 ? "hot" : ""}/></span><b className="mono">{Math.round(n.util*100)}%</b></div>
                <div className="fl-m"><span>{n.ram ? "RAM" : "VRAM"}</span><span className="fl-bar"><i style={{ width: (n.vram[0]/n.vram[1])*100 + "%" }}/></span><b className="mono">{n.vram[0]}/{n.vram[1]} GB</b></div>
              </div>
            )}
            <div className="fl-node-ft">
              {n.status === "busy" && <><span className="mono fl-job">{n.job}</span><span className="fl-stage">{n.stage}</span><span className="mono fl-eta">ETA {n.eta}</span></>}
              {n.status === "idle" && <><span className="fl-stage">Idle · next</span><span className="mono fl-job">{n.next}</span></>}
              {n.status === "offline" && <span className="fl-stage">{n.note}</span>}
              {n.temp != null && <span className={`mono fl-temp ${n.temp > 80 ? "hot" : ""}`}>{n.temp}°C</span>}
            </div>
          </div>
        ))}
      </div>
      <div className="fl-q">
        <div className="caps fl-q-h">Queue</div>
        {queue.map((j, i) => (
          <div key={j.id} className="fl-q-row">
            <span className="mono fl-q-n">{i+1}</span>
            <div className="fl-q-b"><div className="mono fl-q-id">{j.id}</div><div className="fl-q-s">{j.site} · {j.imgs} imgs</div></div>
            <div className="fl-q-r"><div className="mono">{j.est}</div><div className="fl-q-s">→ {j.on}</div></div>
          </div>
        ))}
      </div>
    </div>
  );
}

window.ViewOps = ViewOps;
