// Reviews queue — every task paused at a human-review station for this project.
// Page tier: owns the snapshot poll; composes TaskModal for detail + decisions.
const { useState, useEffect, useMemo } = React;

const REVIEWS_POLL_MS = 5000;

const reviewWaitingSince = (task) => {
  const h = Array.isArray(task.history) ? task.history : [];
  for (let i = h.length - 1; i >= 0; i--) {
    if (h[i].status === 'human-review') return h[i].at;
  }
  return task.updatedAt || null;
};

const reviewAgo = (iso) => {
  if (!iso) return '';
  const mins = Math.max(0, Math.round((Date.now() - new Date(iso).getTime()) / 60000));
  if (mins < 60) return `${mins}m`;
  const hrs = Math.round(mins / 60);
  return hrs < 48 ? `${hrs}h` : `${Math.round(hrs / 24)}d`;
};

const ReviewsPage = ({ slug }) => {
  const [snapshot, setSnapshot] = useState(null);
  const [loadError, setLoadError] = useState(null);
  const [selectedId, setSelectedId] = useState(null);
  const [pendingActionIds, setPendingActionIds] = useState([]);
  const [actionError, setActionError] = useState(null);

  useEffect(() => {
    if (!slug) return undefined;
    let cancelled = false;
    let timer = null;
    const load = async () => {
      try {
        const snap = await window.Api.getProjectSnapshot(slug);
        if (!cancelled) { setSnapshot(snap); setLoadError(null); }
      } catch (err) {
        if (!cancelled) setLoadError(err);
      }
      if (!cancelled) timer = setTimeout(load, REVIEWS_POLL_MS);
    };
    load();
    return () => { cancelled = true; if (timer) clearTimeout(timer); };
  }, [slug]);

  const tasks = useMemo(
    () => (snapshot && window.DataToCanvas ? window.DataToCanvas.tasksFromSnapshot(snapshot) : []),
    [snapshot]
  );
  const stations = useMemo(
    () => (snapshot && window.DataToCanvas ? window.DataToCanvas.stationsFromSnapshot(snapshot) : []),
    [snapshot]
  );
  const queue = useMemo(
    () => tasks
      .filter((t) => t.status === 'human-review')
      .sort((a, b) => String(reviewWaitingSince(a) || '').localeCompare(String(reviewWaitingSince(b) || ''))),
    [tasks]
  );

  // Same decision semantics as the live board (assembly-line.jsx): write the
  // decision, keep the row's spinner until a poll shows the task actually left
  // human-review (the conductor reconciles asynchronously), 30s backstop.
  const decide = async (task, verb, presetNote) => {
    if (pendingActionIds.includes(task.id)) return;
    const preset = typeof presetNote === 'string' ? presetNote.trim() : '';
    let note;
    if (verb === 'approve') {
      // Optional note: use the modal's preset if it left one, otherwise proceed
      // with none — no popup. (The modal's textarea is the only note channel for
      // approve; the pre-existing behavior here was to proceed instantly.)
      note = preset || undefined;
    } else {
      // reject / retry-scratch: note is REQUIRED — it becomes the lead's rework
      // instructions and the station's expertise-journal entry, so a blank note
      // never reaches the API.
      if (preset) {
        note = preset;
      } else {
        const raw = window.prompt(
          verb === 'retry-scratch'
            ? `Feedback (required) — "${task.title}" restarts from a clean checkout with only this text:`
            : `Rework feedback (required) — the lead reworks "${task.title}" from exactly this text:`
        );
        if (raw === null) return; // cancelled — leave it awaiting review
        const trimmed = raw.trim();
        if (!trimmed) {
          window.alert('A send-back needs feedback — it becomes the lead\'s rework instructions and the station\'s learning.');
          return;
        }
        note = trimmed;
      }
    }
    setPendingActionIds((ids) => [...ids, task.id]);
    setActionError(null);
    const safety = setTimeout(() => {
      setPendingActionIds((ids) => ids.filter((id) => id !== task.id));
    }, 30000);
    try {
      const result = await window.Api.postProjectAction(slug, verb, { taskId: task.id, ...(note ? { note } : {}) });
      if (result?.snapshot) setSnapshot(result.snapshot);
    } catch (err) {
      clearTimeout(safety);
      setPendingActionIds((ids) => ids.filter((id) => id !== task.id));
      setActionError(`Could not ${verb} "${task.title}": ${err.message}`);
    }
  };

  useEffect(() => {
    if (!pendingActionIds.length || !tasks.length) return;
    setPendingActionIds((ids) => {
      const next = ids.filter((id) => {
        const t = tasks.find((x) => x.id === id);
        return t && t.status === 'human-review';
      });
      return next.length === ids.length ? ids : next;
    });
  }, [tasks]);

  const selected = selectedId ? queue.find((t) => t.id === selectedId) || tasks.find((t) => t.id === selectedId) : null;
  const stationOf = (task) => stations.find((s) => s.id === task.area) || null;
  const nextStationTitleOf = (task) => {
    const idx = stations.findIndex((s) => s.id === task.area);
    return idx >= 0 && idx + 1 < stations.length ? stations[idx + 1].title : 'Done';
  };

  if (loadError && !snapshot) {
    return <div className="max-w-3xl mx-auto p-10 text-center text-red-600">Could not load the review queue: {loadError.message}</div>;
  }
  if (!snapshot) {
    return <div className="max-w-3xl mx-auto p-10 text-center text-neutral-400">Loading reviews…</div>;
  }

  return (
    <div className="max-w-3xl mx-auto px-6 py-8">
      <div className="flex items-baseline justify-between mb-6">
        <h1 className="text-xl font-bold text-neutral-900">Reviews</h1>
        <span className="text-sm text-neutral-500">{queue.length} waiting</span>
      </div>
      {actionError && (
        <div className="mb-4 px-4 py-2 rounded-xl bg-red-50 border border-red-200 text-sm text-red-700">{actionError}</div>
      )}
      {queue.length === 0 ? (
        <div className="rounded-2xl border border-dashed border-neutral-300 bg-white px-6 py-14 text-center text-neutral-400">
          Nothing is waiting for review.
        </div>
      ) : (
        <ul className="space-y-2">
          {queue.map((task) => {
            const pending = pendingActionIds.includes(task.id);
            return (
              <li key={task.id}>
                <button
                  onClick={() => setSelectedId(task.id)}
                  className="w-full text-left rounded-2xl border border-neutral-200 bg-white px-5 py-4 hover:border-neutral-400 transition-colors flex items-center gap-4"
                >
                  <span className="flex-1 min-w-0">
                    <span className="block font-semibold text-neutral-900 truncate">{task.title}</span>
                    <span className="block text-xs text-neutral-500 mt-0.5">
                      {stationOf(task)?.title || task.area} · waiting {reviewAgo(reviewWaitingSince(task))}
                    </span>
                  </span>
                  {pending
                    ? <span className="text-xs font-semibold text-neutral-400 animate-pulse shrink-0">Sending…</span>
                    : <span className="text-xs font-semibold px-2 py-1 rounded-full bg-orange-100 text-orange-800 border border-orange-300 shrink-0">Human review</span>}
                </button>
              </li>
            );
          })}
        </ul>
      )}
      {/* A reviewer reads the task first, so this queue opens the modal on Details —
          except for a stuck task, whose whole point is the diagnosis on the timeline. */}
      {selected && (
        <window.TaskModal
          task={{ ...selected, actionPending: pendingActionIds.includes(selected.id) }}
          station={stationOf(selected)}
          onClose={() => setSelectedId(null)}
          onApprove={(t, note) => decide(t, 'approve', note)}
          onReject={(t, _station, note) => decide(t, 'reject', note)}
          onRetryScratch={(t, _station, note) => decide(t, 'retry-scratch', note)}
          projectSlug={slug}
          nextStationTitle={nextStationTitleOf(selected)}
          initialTab={selected.status === 'stuck' ? 'history' : 'details'}
        />
      )}
    </div>
  );
};

window.ReviewsPage = ReviewsPage;
