/* global React */
/* 7151 Johnston Road — Estate System Map (interactive).
   A finished, config-driven replacement for the placeholder map section.
   Drives everything from ESTATE_ITEMS: a framed watercolor site plan with
   absolutely-positioned markers that track the image at any width, a linked
   numbered legend, a hover/focus preview card, and a full-gallery lightbox.
   Reusable for another listing by swapping ESTATE_MAP.image + ESTATE_ITEMS. */
const EEm = window.EstablishedEstateDesignSystem_03388c;
const { Eyebrow: EyebrowM, Rule: RuleM } = EEm;
const dspM = window.EEdsp, bdyM = window.EEbdy;
const EE_DISPLAY_3M = window.EE_DISPLAY_3;
const { Reveal: RevealM, Section: SectionM } = window;

/* ============================================================ CONFIG ======
   Asset roots. The illustration + structure photos live in the sibling
   `Assets/estate-map/` folder, so paths are relative to the page HTML.
   TODO(assets): if you generate web-optimized derivatives of the photos,
   point PHOTO_BASE at that folder — nothing else changes.                   */
const ESTATE_MAP = {
  image: '../Assets/estate-map/estate-map-web.jpg',  /* web-optimized illustration */
  /* native px of the illustration — keeps the frame at the true aspect ratio */
  width: 5056,
  height: 3392,
  alt: 'Watercolor site plan of the 7151 Johnston Road estate, showing the residence, guest spaces, barns, arenas, pens, pastures, and arrival.',
};
const PHOTO_BASE = '../Assets/estate-map/photos-web/';  /* web-optimized structure photos */

/* legend order is 1–11; x/y are PERCENTAGES of the map image (0,0 = top-left).
   #10 has six marker positions that all map to the same legend row + gallery. */
const ESTATE_ITEMS = [
  { id: 1,  label: 'Main Residence',         photos: ['01-main-residence.jpg'],      markers: [{ x: 60.5, y: 44.5 }] }, // est.
  { id: 2,  label: 'Guesthouse',             photos: ['02-guesthouse.jpg'],          markers: [{ x: 66.8, y: 39.3 }] },
  { id: 3,  label: 'Covered Arena',          photos: ['03-covered-arena.jpg'],       markers: [{ x: 49.9, y: 36.2 }] },
  { id: 4,  label: 'Upper Barn / Bunkhouse', photos: ['04-upper-barn-bunkhouse.jpg'], markers: [{ x: 37.7, y: 33.4 }] },
  { id: 5,  label: 'Covered Turnout Pen',    photos: ['05-covered-turnout-pen.jpg'], markers: [{ x: 43.3, y: 67.4 }] },
  { id: 6,  label: 'Outdoor Arena',          photos: ['06-outdoor-arena.jpg'],       markers: [{ x: 29.6, y: 60.0 }] },
  { id: 7,  label: 'Lower Barn',             photos: ['07-lower-barn.jpg'],          markers: [{ x: 23.6, y: 48.7 }] },
  { id: 8,  label: 'Hay Barn',               photos: ['08-hay-barn.jpg'],            markers: [{ x: 17.5, y: 46.0 }] }, // est.
  { id: 9,  label: 'Round Pen',              photos: ['09-round-pen.jpg'],           markers: [{ x: 20.5, y: 42.0 }] }, // est.
  { id: 10, label: 'Pastures & Runs',        photos: ['10-pastures-runs.jpg'],
    markers: [{ x: 64.4, y: 29.5 }, { x: 31.6, y: 40.0 }, { x: 45.0, y: 51.4 }, { x: 74.3, y: 53.2 }, { x: 50.4, y: 60.0 }, { x: 56.1, y: 68.5 }] },
  { id: 11, label: 'Views & Arrival',        photos: ['11-views-arrival.jpg'],       markers: [{ x: 56.7, y: 36.7 }] },
];
/* To add more photos to any item later, just add filenames to its photos[]
   array; the preview shows the first, and the lightbox gallery walks them all. */

const photoSrc = (file) => PHOTO_BASE + file;
const num2 = (n) => String(n).padStart(2, '0');

/* Flatten every photo across every item into ONE gallery, in legend order
   1→11, stepping through each item's photos[] in turn. This is what the
   lightbox navigates; each slide knows which legend item it belongs to. */
const GALLERY = (() => {
  const out = [];
  ESTATE_ITEMS.forEach((item) => {
    item.photos.forEach((file, pi) => {
      out.push({
        itemId: item.id,
        label: item.label,
        src: photoSrc(file),
        photoIndex: pi,
        photoCount: item.photos.length,
      });
    });
  });
  return out;
})();
const firstSlideOf = (id) => GALLERY.findIndex((g) => g.itemId === id);
const slideCaption = (s) =>
  `${num2(s.itemId)} · ${s.label}${s.photoCount > 1 ? ` · ${s.photoIndex + 1} / ${s.photoCount}` : ''}`;
const slideAlt = (s) =>
  `${s.label}${s.photoCount > 1 ? `, photo ${s.photoIndex + 1} of ${s.photoCount}` : ''}, 7151 Johnston Road`;

/* ====================================================== PREVIEW CARD ======
   One card at a time. On pointer/keyboard it floats anchored to the trigger,
   flipping above/below to stay on screen; on touch it pins to the bottom.
   `aria-live` lets it be announced for the focused control. */
function PreviewCard({ item, anchor, isTouch }) {
  if (!item) return null;
  const photo = item.photos[0];
  let style;
  if (isTouch || !anchor) {
    style = null; // CSS pins it to the bottom on touch
  } else {
    const r = anchor.getBoundingClientRect();
    const CARD_W = 230, CARD_H = 196, GAP = 12;
    let left = r.left + r.width / 2 - CARD_W / 2;
    left = Math.max(12, Math.min(left, window.innerWidth - CARD_W - 12));
    let top = r.top - CARD_H - GAP;
    if (top < 12) top = r.bottom + GAP; // flip below when there's no room above
    style = { left: `${left}px`, top: `${top}px`, width: `${CARD_W}px` };
  }
  return (
    <div
      id="ee-emap-preview"
      className={`ee-emap-preview${isTouch ? ' ee-emap-preview--touch' : ''}`}
      role="status"
      aria-live="polite"
      style={style}
    >
      <span className="ee-emap-preview__media">
        <img src={photoSrc(photo)} alt="" loading="lazy" />
      </span>
      <span className="ee-emap-preview__meta">
        <span className="ee-emap-preview__num">{num2(item.id)}</span>
        <span className="ee-emap-preview__label">{item.label}</span>
      </span>
    </div>
  );
}

/* ========================================================= LIGHTBOX =======
   Proper modal: role="dialog", aria-modal, labelled by the live caption.
   Traps focus, locks body scroll, restores focus to the trigger on close,
   navigates the whole estate with arrows / ← → keys / swipe, preloads the
   neighbouring images, and shows the large photo crisply.                   */
function EstateLightbox({ index, onClose, onNav, restoreEl }) {
  const dialogRef = React.useRef(null);
  const closeRef = React.useRef(null);
  const touchX = React.useRef(null);
  const slide = GALLERY[index];

  React.useEffect(() => {
    const prevFocus = restoreEl || document.activeElement;
    document.body.style.overflow = 'hidden';
    const id = requestAnimationFrame(() => closeRef.current && closeRef.current.focus());

    const onKey = (e) => {
      if (e.key === 'Escape') { e.preventDefault(); onClose(); return; }
      if (e.key === 'ArrowRight') { e.preventDefault(); onNav(1); return; }
      if (e.key === 'ArrowLeft') { e.preventDefault(); onNav(-1); return; }
      if (e.key === 'Tab') {
        const root = dialogRef.current;
        if (!root) return;
        const f = root.querySelectorAll('button, [href], [tabindex]:not([tabindex="-1"])');
        if (!f.length) return;
        const first = f[0], last = f[f.length - 1];
        if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
        else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
      }
    };
    document.addEventListener('keydown', onKey, true);
    return () => {
      document.removeEventListener('keydown', onKey, true);
      document.body.style.overflow = '';
      cancelAnimationFrame(id);
      if (prevFocus && prevFocus.focus) prevFocus.focus();
    };
  }, [onClose, onNav, restoreEl]);

  /* preload neighbours so prev/next feels instant */
  React.useEffect(() => {
    [1, -1].forEach((d) => {
      const n = (index + d + GALLERY.length) % GALLERY.length;
      const img = new Image();
      img.src = GALLERY[n].src;
    });
  }, [index]);

  const onTouchStart = (e) => { touchX.current = e.changedTouches[0].clientX; };
  const onTouchEnd = (e) => {
    if (touchX.current == null) return;
    const dx = e.changedTouches[0].clientX - touchX.current;
    if (Math.abs(dx) > 44) onNav(dx < 0 ? 1 : -1);
    touchX.current = null;
  };

  return (
    <div className="ee-overlay ee-overlay--deep" onClick={onClose}>
      <div
        ref={dialogRef}
        className="ee-emap-lightbox"
        role="dialog"
        aria-modal="true"
        aria-labelledby="ee-emap-lb-caption"
        onClick={(e) => e.stopPropagation()}
        onTouchStart={onTouchStart}
        onTouchEnd={onTouchEnd}
      >
        <button ref={closeRef} className="ee-overlay__close" onClick={onClose} aria-label="Close gallery">
          <svg width="18" height="18" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.6" fill="none" strokeLinecap="round"><path d="M5 5l14 14M19 5L5 19" /></svg>
        </button>
        <button className="ee-lightbox__nav" style={{ left: 16 }} onClick={() => onNav(-1)} aria-label="Previous photo">
          <svg width="22" height="22" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.6" fill="none" strokeLinecap="round" strokeLinejoin="round"><path d="M15 5l-7 7 7 7" /></svg>
        </button>
        <button className="ee-lightbox__nav" style={{ right: 16 }} onClick={() => onNav(1)} aria-label="Next photo">
          <svg width="22" height="22" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.6" fill="none" strokeLinecap="round" strokeLinejoin="round"><path d="M9 5l7 7-7 7" /></svg>
        </button>
        <figure className="ee-emap-lightbox__figure">
          {/* lightbox image loads eagerly + crisp (no lazy) */}
          <img className="ee-lightbox__img" src={slide.src} alt={slideAlt(slide)} />
          <figcaption id="ee-emap-lb-caption" className="ee-lightbox__caption">
            {slideCaption(slide)}
            <span className="ee-emap-lightbox__count"> &nbsp;·&nbsp; {index + 1} / {GALLERY.length}</span>
          </figcaption>
        </figure>
      </div>
    </div>
  );
}

/* ========================================================= ESTATE MAP =====
   `siteplan` (optional) lets the page-level MEDIA config override the image;
   otherwise the bundled watercolor illustration is used.                    */
function EstateMap({ siteplan }) {
  const mapSrc = (siteplan && siteplan.src) || ESTATE_MAP.image;
  const [active, setActive] = React.useState(null);      // hovered/focused item id
  const [anchor, setAnchor] = React.useState(null);      // trigger element for the card
  const [lightbox, setLightbox] = React.useState(null);  // gallery index | null
  const triggerRef = React.useRef(null);                 // element to restore focus to

  const isTouch = React.useMemo(
    () => typeof window !== 'undefined' &&
      window.matchMedia('(hover: none) and (pointer: coarse)').matches,
    []
  );

  const show = (id, el) => { setActive(id); setAnchor(el); };
  const hide = (id) => setActive((a) => (a === id ? null : a));

  const open = (id, el) => {
    triggerRef.current = el || null;
    setActive(null);
    setLightbox(firstSlideOf(id));
  };
  const navLightbox = React.useCallback((dir) => {
    setLightbox((i) => (i == null ? i : (i + dir + GALLERY.length) % GALLERY.length));
  }, []);

  /* a moving preview would go stale on scroll — dismiss it instead (calm) */
  React.useEffect(() => {
    if (active == null || isTouch) return;
    const onScroll = () => setActive(null);
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, [active, isTouch]);

  const activeItem = active == null ? null : ESTATE_ITEMS.find((i) => i.id === active);

  return (
    <SectionM id="map" tone="alt">
      <div className="ee-split ee-split--map">
        {/* ---------------------------------------------------- the framed map */}
        <RevealM>
          <div
            className="ee-frame ee-emap"
            style={{ aspectRatio: `${ESTATE_MAP.width} / ${ESTATE_MAP.height}` }}
          >
            <img src={mapSrc} alt={ESTATE_MAP.alt} />
            {ESTATE_ITEMS.flatMap((item) =>
              item.markers.map((m, mi) => {
                const many = item.markers.length > 1;
                const aria = many
                  ? `${item.id}. ${item.label}, area ${mi + 1} of ${item.markers.length}. Open photo`
                  : `${item.id}. ${item.label}. Open photo`;
                return (
                  <button
                    key={`${item.id}-${mi}`}
                    type="button"
                    className={`ee-pin${active === item.id ? ' is-active' : ''}`}
                    style={{ left: `${m.x}%`, top: `${m.y}%` }}
                    onMouseEnter={(e) => show(item.id, e.currentTarget)}
                    onMouseLeave={() => hide(item.id)}
                    onFocus={(e) => show(item.id, e.currentTarget)}
                    onBlur={() => hide(item.id)}
                    onClick={(e) => open(item.id, e.currentTarget)}
                    aria-label={aria}
                    aria-describedby={active === item.id ? 'ee-emap-preview' : undefined}
                  >
                    <span className="ee-pin__dot">{item.id}</span>
                  </button>
                );
              })
            )}
          </div>
        </RevealM>

        {/* ------------------------------------------------ eyebrow + legend */}
        <RevealM delay={80}>
          <div>
            <RuleM tone="leather" length="48px" style={{ marginBottom: 'var(--space-5)' }} />
            <EyebrowM style={{ marginBottom: 'var(--space-4)' }}>The Estate Map</EyebrowM>
            <h2 style={dspM(EE_DISPLAY_3M, { marginBottom: 'var(--space-5)' })}>One complete system.</h2>
            <p style={bdyM({ marginBottom: 'var(--space-6)', fontSize: 'var(--type-body-sm)' })}>
              How the residence, guest spaces, and equestrian facilities sit together across the parcel.
              Hover or tap a marker to preview it — click to open the photo.
            </p>
            <ol className="ee-emap-legend" style={{ listStyle: 'none', margin: 0, padding: 0 }}>
              {ESTATE_ITEMS.map((item) => (
                <li key={item.id}>
                  <button
                    type="button"
                    className={`ee-legend-item${active === item.id ? ' is-active' : ''}`}
                    onMouseEnter={(e) => show(item.id, e.currentTarget)}
                    onMouseLeave={() => hide(item.id)}
                    onFocus={(e) => show(item.id, e.currentTarget)}
                    onBlur={() => hide(item.id)}
                    onClick={(e) => open(item.id, e.currentTarget)}
                    aria-label={`${item.id}. ${item.label}. Open photo`}
                    aria-describedby={active === item.id ? 'ee-emap-preview' : undefined}
                  >
                    <span className="ee-legend-item__num">{num2(item.id)}</span>
                    <span className="ee-legend-item__label">{item.label}</span>
                  </button>
                </li>
              ))}
            </ol>
          </div>
        </RevealM>
      </div>

      {/* single floating/bottom preview card, anchored to the active trigger */}
      <PreviewCard item={lightbox == null ? activeItem : null} anchor={anchor} isTouch={isTouch} />

      {lightbox != null ? (
        <EstateLightbox
          index={lightbox}
          onClose={() => setLightbox(null)}
          onNav={navLightbox}
          restoreEl={triggerRef.current}
        />
      ) : null}
    </SectionM>
  );
}

window.EstateMap = EstateMap;
window.ESTATE_ITEMS = ESTATE_ITEMS;
