// ============ Workflow view (real Google Workflows) ============
const STAGE_TO_KEY  = { setup: "s_setup", ingest: "s_ingest", enhance: "s_enhance", vector: "s_vector", confirm: "s_confirm" };
const STAGE_TO_ICON = { setup: "check",   ingest: "database", enhance: "sparkle",   vector: "cube",     confirm: "rocket"   };

// Workflow timestamps arrive as UTC ISO strings from the server; render them in
// America/Mexico_City so operators see local time regardless of browser tz.
const MX_TZ = "America/Mexico_City";
const fmtMx = (iso) => {
  if (!iso) return "—";
  const d = new Date(iso);
  if (Number.isNaN(d.getTime())) return "—";
  return d.toLocaleString("sv-SE", { timeZone: MX_TZ }).slice(0, 19);
};

// Push (fullpush) control: state chip + "Run now". Only "terminado" can start a
// new push; read-only users see the state but not the button.
const PUSH_TONE = { terminado: "success", iniciado: "warn", procesando: "running" };
// Unknown states (not in i18n) are shown raw.
const pushLabel = (t, s) => { const k = "push_" + s; const v = t(k); return v === k ? s : v; };
const PushControl = ({ t, readOnly }) => {
  const [state, setState] = React.useState(null);
  const [error, setError] = React.useState(null);
  const [confirming, setConfirming] = React.useState(false);
  const [busy, setBusy] = React.useState(false);

  const load = React.useCallback(async () => {
    try {
      const s = await apiAuth("/api/push/status");
      setState(s.state);
      setError(null);
    } catch (e) { setError(e.message); }
  }, []);

  React.useEffect(() => { load(); }, [load]);

  // Poll while a push is queued or processing
  const running = state != null && state !== "terminado";
  React.useEffect(() => {
    if (!running) return;
    const iv = setInterval(load, 15000);
    return () => clearInterval(iv);
  }, [running, load]);

  const start = async () => {
    setBusy(true);
    try {
      const r = await apiAuth("/api/push", { method: "POST" });
      setState(r.state);
      setError(null);
    } catch (e) {
      setError(e.message);
      load();
    } finally {
      setBusy(false);
      setConfirming(false);
    }
  };

  return (
    <>
      {state && <Chip tone={PUSH_TONE[state] || "default"} dot>{t("push")}: {pushLabel(t, state)}</Chip>}
      {error && <Chip tone="danger" dot>{error}</Chip>}
      {!readOnly && (
        <Button size="sm" variant="primary" icon="play" disabled={state !== "terminado" || busy}
          title={running ? t("push_running_hint") : undefined}
          onClick={() => setConfirming(true)}>{t("run_now")}</Button>
      )}
      <Modal open={confirming} onClose={() => !busy && setConfirming(false)} title={t("push_confirm_title")}
        footer={<>
          <Button variant="ghost" onClick={() => setConfirming(false)} disabled={busy}>{t("cancel")}</Button>
          <Button variant="primary" icon="play" onClick={start} disabled={busy}>{busy ? t("loading") : t("run_now")}</Button>
        </>}>
        <div style={{ fontSize: 13, color: "var(--text-dim)" }}>{t("push_confirm_body")}</div>
      </Modal>
    </>
  );
};

const ViewWorkflow = ({ t, readOnly }) => {
  const [runs, setRuns] = React.useState([]);
  const [selectedId, setSelectedId] = React.useState(null);
  const [detail, setDetail] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [detailLoading, setDetailLoading] = React.useState(false);
  const [error, setError] = React.useState(null);
  const [page, setPage] = React.useState(1);
  const perPage = 8;

  const loadRuns = React.useCallback(async () => {
    try {
      const data = await apiAuth("/api/workflow/runs?limit=30");
      setRuns(data.runs || []);
      setError(null);
      if (!selectedId && data.runs?.length) setSelectedId(data.runs[0].id);
    } catch (e) {
      setError(e.message);
    } finally {
      setLoading(false);
    }
  }, [selectedId]);

  const loadDetail = React.useCallback(async (id) => {
    if (!id) return;
    setDetailLoading(true);
    try {
      const data = await apiAuth(`/api/workflow/run/${encodeURIComponent(id)}`);
      setDetail(data);
    } catch (e) {
      setError(e.message);
    } finally {
      setDetailLoading(false);
    }
  }, []);

  React.useEffect(() => { loadRuns(); }, []); // mount
  React.useEffect(() => { loadDetail(selectedId); }, [selectedId, loadDetail]);

  // Live poll when selected run or any run is ACTIVE
  const anyActive = runs.some(r => r.status === "running") || detail?.status === "running";
  React.useEffect(() => {
    if (!anyActive) return;
    const iv = setInterval(() => {
      loadRuns();
      if (selectedId) loadDetail(selectedId);
    }, 5000);
    return () => clearInterval(iv);
  }, [anyActive, selectedId, loadRuns, loadDetail]);

  const selected = detail || runs.find(r => r.id === selectedId) || runs[0];

  // pagination
  const pages = Math.max(1, Math.ceil(runs.length / perPage));
  const rows = runs.slice((page - 1) * perPage, page * perPage);

  return (
    <div className="view">
      {error && <ApiError t={t} message={error}/>}

      <Card title={<SectionTitle icon="workflow" tip={t("tip_workflow_graph")}>{t("workflow_graph")}</SectionTitle>}
        right={
          <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
            {selected && <Chip tone={selected.status === "running" ? "running" : selected.status === "success" ? "success" : "danger"} dot>
              {t(selected.status)}
            </Chip>}
            {selected && <span className="mono" style={{ fontSize: 12, color: "var(--text-dim)" }}>{selected.id?.slice(0,12)}</span>}
            {anyActive && <Chip tone="running" dot>{t("live")}</Chip>}
            <PushControl t={t} readOnly={readOnly}/>
            <Button size="sm" variant="ghost" icon="refresh" onClick={() => { loadRuns(); if (selectedId) loadDetail(selectedId); }}>{t("refresh")}</Button>
          </div>
        }
        style={{ marginBottom: 16 }}>
        {loading && !selected ? <LoadingBlock t={t}/> : !selected ? <EmptyBlock t={t}/> : (
          <div className="dag-wrap">
            <div className="dag-row">
              {selected.stages.map((st, i) => (
                <React.Fragment key={st.key}>
                  <div className={`dag-node ${st.state}`}>
                    <div className="node-icon"><Icon name={STAGE_TO_ICON[st.key] || "check"} size={14}/></div>
                    <div className="node-title">{t(STAGE_TO_KEY[st.key] || st.key)}</div>
                    <div className="node-sub">{t((STAGE_TO_KEY[st.key] || st.key) + "_sub")}</div>
                    <div style={{ marginTop: 4 }}>
                      {st.state === "success" && <Chip tone="success" dot>{t("success")}</Chip>}
                      {st.state === "running" && <Chip tone="running" dot>{t("running")}</Chip>}
                      {st.state === "failed"  && <Chip tone="danger"  dot>{t("failed")}</Chip>}
                      {st.state === "pending" && <Chip dot>{t("pending")}</Chip>}
                    </div>
                    {st.duration != null && (
                      <div className="mono" style={{ fontSize: 10, color: "var(--text-faint)", marginTop: 4 }}>{st.duration}s</div>
                    )}
                  </div>
                  {i < selected.stages.length - 1 && (
                    <div className={`dag-edge ${st.state === "success" ? "done" : st.state === "running" ? "active" : ""}`}>
                      {st.state === "running" && <div className="pulse"/>}
                    </div>
                  )}
                </React.Fragment>
              ))}
            </div>

            <StageMetrics selected={selected} t={t}/>

            <div className="dag-meta">
              {selected.records != null && <Chip><Icon name="database" size={12}/> {selected.records.toLocaleString()} {t("records_count")}</Chip>}
              {selected.duration != null && <Chip>{Math.floor(selected.duration/60)}m {selected.duration%60}s</Chip>}
            </div>

            {selected.error && (
              <div style={{ marginTop: 16, padding: 12, borderRadius: 10, background: "rgba(248,113,113,.08)", border: "1px solid rgba(248,113,113,.35)", color: "var(--danger)", fontSize: 12 }}>
                <div style={{ fontWeight: 600, marginBottom: 4 }}>{t("failed")}</div>
                <div className="mono" style={{ whiteSpace: "pre-wrap", opacity: .85 }}>{selected.error}</div>
              </div>
            )}
          </div>
        )}
      </Card>

      <Card title={<SectionTitle icon="clock" tip={t("tip_exec_history")}>{t("exec_history")}</SectionTitle>}
        right={<Button size="sm" variant="ghost" icon="refresh" onClick={loadRuns}>{t("refresh")}</Button>}>
        {loading && runs.length === 0 ? <LoadingBlock t={t}/> : runs.length === 0 ? <EmptyBlock t={t}/> : (
          <>
            <table className="table">
              <thead>
                <tr>
                  <th>{t("run_id")}</th>
                  <th>{t("started")}</th>
                  <th className="num">{t("duration")}</th>
                  <th className="num">{t("records")}</th>
                  <th>{t("status")}</th>
                  <th></th>
                </tr>
              </thead>
              <tbody>
                {rows.map(r => (
                  <tr key={r.id}
                    onClick={() => setSelectedId(r.id)}
                    style={{ cursor: "pointer", background: r.id === selectedId ? "rgba(34,211,238,.06)" : undefined }}>
                    <td className="mono" style={{ fontSize: 12 }}>{r.id.slice(0, 14)}…</td>
                    <td style={{ fontSize: 13, color: "var(--text-dim)" }}>{fmtMx(r.started)}</td>
                    <td className="num">{r.duration != null ? `${Math.floor(r.duration/60)}m ${r.duration%60}s` : "—"}</td>
                    <td className="num">{r.records != null ? r.records.toLocaleString() : "—"}</td>
                    <td>
                      <Chip tone={r.status === "success" ? "success" : r.status === "running" ? "running" : "danger"} dot>
                        {t(r.status)}
                      </Chip>
                    </td>
                    <td><Button size="sm" variant="ghost" icon="chevron"/></td>
                  </tr>
                ))}
              </tbody>
            </table>
            <div className="pager">
              <div style={{ flex: 1, fontSize: 12, color: "var(--text-faint)" }}>
                {(page-1)*perPage + 1}–{Math.min(page*perPage, runs.length)} {t("of")} {runs.length}
              </div>
              <button className="pg-btn" onClick={() => setPage(p => Math.max(1, p-1))} disabled={page===1}>‹</button>
              {Array.from({ length: pages }, (_, i) => (
                <button key={i} className={"pg-btn" + (page === i+1 ? " active" : "")} onClick={() => setPage(i+1)}>{i+1}</button>
              ))}
              <button className="pg-btn" onClick={() => setPage(p => Math.min(pages, p+1))} disabled={page===pages}>›</button>
            </div>
          </>
        )}
      </Card>
    </div>
  );
};

const StageMetrics = ({ selected, t }) => {
  const stagesWithMetrics = selected.stages.filter(s => s.metrics);
  if (stagesWithMetrics.length === 0) return null;

  // Business-user view: only records, throughput, and time per stage.
  const BUSINESS_KEYS = ["total_processed", "total_rows", "total_enhanced", "products_per_second", "elapsed_time"];

  const metricLabel = (k) => {
    const map = {
      total_processed: t("records"),
      total_rows: t("records"),
      total_enhanced: t("records"),
      elapsed_time: t("elapsed"),
      products_per_second: t("records_per_sec"),
    };
    return map[k] || k;
  };

  const fmtVal = (k, v) => {
    if (typeof v !== "number") return String(v);
    if (k === "elapsed_time") {
      if (v < 60) return `${v.toFixed(1)}s`;
      const m = Math.floor(v / 60), s = Math.round(v % 60);
      return `${m}m ${s}s`;
    }
    if (k === "products_per_second") return v.toFixed(1);
    if (Number.isInteger(v)) return v.toLocaleString();
    return v.toFixed(2);
  };

  return (
    <div style={{ marginTop: 30, display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))", gap: 12 }}>
      {stagesWithMetrics.map(s => {
        // Deduplicate by label: total_processed / total_rows / total_enhanced
        // all render as "Registros"; keep only the first match in priority order.
        const seenLabels = new Set();
        const keys = [];
        for (const k of BUSINESS_KEYS) {
          if (typeof s.metrics[k] !== "number" || s.metrics[k] <= 0) continue;
          const label = metricLabel(k);
          if (seenLabels.has(label)) continue;
          seenLabels.add(label);
          keys.push(k);
        }
        return (
          <div key={s.key} style={{ padding: 12, borderRadius: 10, background: "var(--surface-2)", border: "1px solid var(--line)" }}>
            <div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 8 }}>
              <span style={{ color: "var(--accent)" }}><Icon name={STAGE_TO_ICON[s.key]} size={12}/></span>
              <div style={{ fontSize: 11, letterSpacing: 1, textTransform: "uppercase", color: "var(--text-dim)" }}>
                {t(STAGE_TO_KEY[s.key])}
              </div>
            </div>
            {keys.length === 0
              ? <div style={{ fontSize: 11, color: "var(--text-faint)" }}>—</div>
              : (
                <div style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: "4px 10px" }}>
                  {keys.map(k => (
                    <React.Fragment key={k}>
                      <div style={{ fontSize: 11, color: "var(--text-faint)" }}>{metricLabel(k)}</div>
                      <div className="num-mono" style={{ fontSize: 12, color: "var(--text)", textAlign: "right" }}>
                        {fmtVal(k, s.metrics[k])}
                      </div>
                    </React.Fragment>
                  ))}
                </div>
              )
            }
          </div>
        );
      })}
    </div>
  );
};

Object.assign(window, { ViewWorkflow, StageMetrics, PushControl });
