/**
 * Side-by-side comparison of two series.
 *
 * This is how comparison actually works at a workstation: two viewports, and the
 * reader decides whether scrolling and windowing are yoked together or driven
 * independently.
 *
 * Linking is useful and dangerous in different cases, so it is explicit rather
 * than automatic:
 *   - Same patient, same plane, same slice count (contrast phases, pre/post) —
 *     linking slices compares the same anatomy at two timepoints, which is the
 *     whole point.
 *   - Different patients or different planes — linked indices compare unrelated
 *     levels and actively mislead. Link is therefore offered only when the two
 *     stacks are comparable, and defaults off otherwise.
 *
 * Each pane is an ordinary DicomViewer, so every tool it already has (presets,
 * zoom, HU probe, segmentation overlay) keeps working inside the comparison.
 */

(function () {
'use strict';

const { useState, useCallback } = React;

function paneLabel(entry) {
  const bits = [entry.description];
  if (entry.plane) bits.push({ axial: 'צירי', sagittal: 'סגיטלי', coronal: 'כלילי', volume: 'נפחי' }[entry.plane] || entry.plane);
  return bits.filter(Boolean).join(' · ');
}

function CompareViewer({ leftId, rightId, note, normalVsDisease = false, hideSliceLink = false }) {
  const left = window.Catalog.getSeries(leftId);
  const right = window.Catalog.getSeries(rightId);

  // Slice linking only makes sense when the two stacks index the same anatomy.
  // The plane must be positively declared on both: treating two undeclared
  // planes as "equal" would silently enable linking for a pair nobody checked.
  const candidate =
    left.patientId === right.patientId &&
    Boolean(left.plane) &&
    left.plane === right.plane &&
    left.sliceCount === right.sliceCount;

  const [linkSlices, setLinkSlices] = useState(false);
  const [linkWindow, setLinkWindow] = useState(false);
  const [leftReady, setLeftReady] = useState(false);
  const [rightReady, setRightReady] = useState(false);
  const comparable = candidate && window.Assessment.canLinkStacks(leftReady && leftReady.stack, rightReady && rightReady.stack);

  const [forcedIndex, setForcedIndex] = useState(null);

  /**
   * The echo between panes is stopped by convergence, not by a mutex.
   *
   * Pane A reports 11, both panes are driven to 11, pane B reports 11 back, and
   * setting the same value again is a no-op React bails out of — so it settles
   * after one round with nothing to unlock. An earlier version held a flag
   * released on requestAnimationFrame; that silently broke whenever the tab was
   * not visible, because rAF does not fire in a hidden document and the flag
   * stayed latched, killing synchronisation for the rest of the session.
   *
   * A pane that applied an index from here does not report it back (see
   * `externallySetRef` in the viewer), so a pane still on its shorter preview
   * stack cannot clamp the value and drag its partner backwards.
   */
  const handleIndex = useCallback(
    (index) => {
      if (!linkSlices || !comparable) return;
      setForcedIndex(index);
    },
    [linkSlices, comparable]
  );

  const bothReady = leftReady && rightReady;

  return (
    <div className="compare animate-in">
      <div className="compare__bar">
        <strong style={{ color: 'var(--text-strong)' }}>השוואה בין סריקות</strong>

        {!hideSliceLink && (
          <label className="compare__toggle">
            <input
              type="checkbox"
              checked={linkSlices}
              disabled={!comparable}
              onChange={(e) => setLinkSlices(e.target.checked)}
            />
            סנכרון חתכים
          </label>
        )}

        <label className="compare__toggle">
          <input
            type="checkbox"
            checked={linkWindow}
            onChange={(e) => setLinkWindow(e.target.checked)}
          />
          סנכרון חלון
        </label>

        {!comparable && !hideSliceLink && (
          <span className="chip chip--accent" title="הסנכרון דורש גם מיקום, כיוון ומסגרת ייחוס תואמים בתגיות DICOM של כל החתכים">
            סנכרון חתכים לא זמין
          </span>
        )}

        {!bothReady && <span className="breathe" style={{ fontSize: 'var(--text-xs)' }}>טוען את שתי הסדרות…</span>}
      </div>

      {!comparable && !hideSliceLink && (
        <div className="callout callout--caution" style={{ margin: 0 }}>
          <strong className="callout__title">למה הסנכרון חסום כאן</strong>
          טרם אומתה התאמה של כל החתכים במרחב: נדרשים אותו נבדק, מישור, מיקום,
          כיוון ומסגרת ייחוס, וסדרות מלאות ללא דגימה. מספר חתכים זהה לבדו אינו מספיק.
          אפשר להשוות ולגלול בכל צד בנפרד; ההתאמה האנטומית נעשית ידנית. גם התאמת תגיות אינה מתקנת תנועת מטופל או נשימה.
        </div>
      )}

      {note && <p className="prose" style={{ fontSize: 'var(--text-sm)' }}>{note}</p>}

      <div className="compare__grid">
        {[
          { entry: left, onReady: loaded => setLeftReady(loaded) },
          { entry: right, onReady: loaded => setRightReady(loaded) }
        ].map(({ entry, onReady }, i) => (
          <div className="compare__pane" key={entry.id}>
            <div className="compare__pane-head">
              <span className="compare__pane-index">{i === 0 ? 'א' : 'ב'}</span>
              {normalVsDisease && <strong>{i === 0 ? 'תקין / ביקורת' : 'מקרה פתולוגי'}</strong>}
              <span>{paneLabel(entry)}</span>
              <span className="chip chip--neutral">{entry.modality}</span>
            </div>

            <window.DicomViewer
              entry={entry}
              onReady={onReady}
              onIndexChange={handleIndex}
              externalIndex={linkSlices && comparable ? forcedIndex : null}
              syncWindowTo={linkWindow ? 'compare' : null}
              revealed
            />
          </div>
        ))}
      </div>
    </div>
  );
}

window.CompareViewer = CompareViewer;

/** Source PNGs retain their display settings; they have no raw HU or cine data. */
function UltrasoundCase({ caseId }) {
  const item = window.USCases.cases.find(entry => entry.id === caseId);
  const [revealed, setRevealed] = useState(false);
  const [zoom, setZoom] = useState(1);
  const [failed, setFailed] = useState(false);
  const labels = { Normal: 'תמונה תקינה לפי המקור', Benign: 'נגע שפיר לפי המקור', Malignant: 'נגע ממאיר לפי המקור' };
  return <article className="card us-case">
    <h3>{labels[item.label]} · <span dir="ltr">{item.subject}</span></h3>
    <p>תמונה בודדת · סימון המקור נחשף לפי בחירה</p>
    <div className="us-case__stage">
      {failed ? <p role="alert">התמונה לא נטענה. רענן את הדף ונסה שוב.</p> : <div className="us-case__image" style={{ transform: `scale(${zoom})` }}>
        <img src={item.url} alt={`אולטרסאונד שד, ${labels[item.label]}`} loading="lazy" onError={() => setFailed(true)} />
        {revealed && item.label !== 'Normal' && <img className="us-case__overlay" src={item.overlayUrl} alt="מסכת מומחה: ירוק לשפיר, אדום לממאיר" />}
      </div>}
    </div>
    <label>הגדלה <input type="range" min="1" max="3" step="0.1" value={zoom} onChange={event => setZoom(Number(event.target.value))} /></label>
    {item.label !== 'Normal' && <button type="button" className="btn btn--secondary" aria-pressed={revealed} onClick={() => setRevealed(!revealed)}>{revealed ? 'הסתר סימון מומחה' : 'הצג סימון מומחה'}</button>}
    <p><a href={item.source} target="_blank" rel="noopener noreferrer">BUS-UCLM — מקור התמונה</a> · <a href={window.USCases.licenseUrl} target="_blank" rel="noopener noreferrer">CC BY 4.0</a></p>
    <small>{window.USCases.credit}. התמונה נשמרה ללא שינוי; ברובד הסימון בלבד הרקע השחור הפך לשקוף. אין כאן ניקוד אבחנתי או אישור מקצועי מקומי.</small>
  </article>;
}
window.UltrasoundCase = UltrasoundCase;

function UltrasoundComparison({ leftId, rightId }) {
  return <div className="compare__grid"><UltrasoundCase key={leftId} caseId={leftId} /><UltrasoundCase key={rightId} caseId={rightId} /></div>;
}
window.UltrasoundComparison = UltrasoundComparison;

/** The free workspace is specifically normal/reference versus disease. */
function ComparisonWorkspace() {
  const params = new URLSearchParams(window.location.hash.split('?')[1] || '');
  const initial = params.get('left');
  const groups = window.ComparisonPairs.groups;
  const initialGroup = groups.find(group => group.normal.includes(initial) || group.disease.includes(initial)) || groups[0];
  const [groupId, setGroupId] = useState(initialGroup.id);
  const group = groups.find(item => item.id === groupId);
  const [leftId, setLeftId] = useState(initialGroup.normal.includes(initial) ? initial : initialGroup.normal[0] || '');
  const [rightId, setRightId] = useState(initialGroup.disease.includes(initial) ? initial : initialGroup.disease[0] || '');
  const [pair, setPair] = useState(null);
  const valid = window.ComparisonPairs.validPair(group, leftId, rightId);
  const label = id => {
    if (group.kind === 'ultrasound') {
      const item = window.USCases.cases.find(entry => entry.id === id);
      return `${item.subject} — ${id} · ${{ Normal: 'תקינה לפי המקור', Benign: 'שפיר', Malignant: 'ממאיר' }[item.label]}`;
    }
    const entry = window.Catalog.getSeries(id); return `${entry.patientId} — ${entry.description} (${entry.modality})`;
  };
  function selectGroup(id) {
    const next = groups.find(item => item.id === id);
    setGroupId(id); setLeftId(next.normal[0] || ''); setRightId(next.disease[0] || ''); setPair(null);
  }

  return (
    <section className="comparison-workspace animate-in" aria-labelledby="comparison-heading">
      <header>
        <h1 id="comparison-heading">השוואת תקין מול פתולוגי</h1>
        <p>בחר איבר: צד א הוא נבדק ביקורת או מתנדב בריא לפי מקור הנתונים; צד ב הוא מקרה מחלה של אותו איבר. בכל צד גלילה וחלון עצמאי. השוואות בין רצפים ופאזות נשארות בשלבים המתאימים בשיעורים.</p>
      </header>
      <form className="card comparison-picker" onSubmit={event => { event.preventDefault(); if (valid) setPair([leftId, rightId]); }}>
        <label htmlFor="compare-organ">איבר וסוג בדיקה
          <select id="compare-organ" value={groupId} onChange={event => selectGroup(event.target.value)}>
            {groups.map(item => <option key={item.id} value={item.id}>{item.title}{item.normal.length ? '' : ' — חסרה ביקורת מאומתת'}</option>)}
          </select>
        </label>
        <p>{group.note}</p>
        <div className="comparison-picker__series">
          {[{ id: 'compare-left', name: 'תקין / נבדק ביקורת', ids: group.normal, value: leftId, change: setLeftId }, { id: 'compare-right', name: 'מקרה פתולוגי', ids: group.disease, value: rightId, change: setRightId }].map(field => (
            <label key={field.id} htmlFor={field.id}>{field.name}
              <select id={field.id} value={field.value} disabled={!field.ids.length} onChange={event => field.change(event.target.value)}>
                {!field.ids.length && <option value="">חסרה סדרה מאומתת מתאימה</option>}
                {field.ids.map(id => <option key={id} value={id}>{label(id)}</option>)}
              </select>
              <span className="comparison-picker__note">{field.value ? group.kind === 'ultrasound' ? 'תיוג ברמת התמונה לפי INFO.csv ומסכת המומחה.' : window.Catalog.getSeries(field.value).cohortNote : 'לא יוצג רצף אחר של אותו חולה כתחליף לנבדק בריא.'}</span>
            </label>
          ))}
        </div>
        {!valid && <p role="status">ההשוואה תיפתח רק כשיש ביקורת ומקרה מחלה מתאימים של אותו איבר.</p>}
        <button type="submit" className="btn btn--primary" disabled={!valid}>הצג תקין מול פתולוגי</button>
        {pair && (pair[0] !== leftId || pair[1] !== rightId) && <p role="status">הבחירה השתנתה. לחץ על ״הצג תקין מול פתולוגי״ לעדכון התצוגה.</p>}
      </form>
      {pair && (group.kind === 'ultrasound'
        ? <UltrasoundComparison key={pair.join('|')} leftId={pair[0]} rightId={pair[1]} />
        : <CompareViewer key={pair.join('|')} leftId={pair[0]} rightId={pair[1]} note={group.note} normalVsDisease />)}
    </section>
  );
}

window.ComparisonWorkspace = ComparisonWorkspace;

})();
