// ---------- Course carousel ----------
// Pulls categories + courses from the shared WB_* globals so the homepage
// carousel and the All-Courses subpage stay in sync.
//
// Carousel model (rewritten 2024-12): track is a transform-based row, not an
// overflow-scroll container. Pointer drag with setPointerCapture moves the
// row; on release we snap to the nearest card index. This sidesteps the iOS
// Safari quirks around overflow + nested touch handlers that broke swiping.
const courseCategories = window.WB_CATEGORIES;

// Convert "#RRGGBB" to "rgba(r,g,b,a)" so we can build fade gradients in JS.
function hexToRgba(hex, a) {
  const h = String(hex).replace('#', '');
  const n = parseInt(h.length === 3 ? h.split('').map(c => c + c).join('') : h, 16);
  return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${a})`;
}

// Flatten courses with their category tag for the pill band — order interleaved
// so the band reads as a colorful blend, not stacked single-color blocks.
const directCourses = (() => {
  const buckets = courseCategories.map(cat => ({
    cat,
    list: window.WB_COURSES.filter(c => c.cat === cat.id),
  }));
  const max = Math.max(...buckets.map(b => b.list.length));
  const out = [];
  for (let i = 0; i < max; i++) {
    buckets.forEach(({ cat, list }) => {
      if (list[i]) out.push({ label: list[i].title, cat, course: list[i] });
    });
  }
  return out;
})();

const CourseCarousel = ({ onOpenInquiry }) => {
  const stageRef = React.useRef(null);
  const trackRef = React.useRef(null);
  const pillsRef = React.useRef(null);
  const [activeBubble, setActiveBubble] = React.useState(null);
  const [openCourse, setOpenCourse] = React.useState(null);
  const headRef = useReveal();

  // Card geometry — measured at runtime, recomputed on resize.
  const [geom, setGeom] = React.useState({ cardW: 240, gap: 18, stageW: 1200 });

  // Active card index — never wraps, can go negative or > N. We render the row
  // long enough to cover any reasonable index, and modulo into the data array
  // when picking which category a slot shows.
  const N = courseCategories.length;
  const [index, setIndex] = React.useState(0);
  const [drag, setDrag] = React.useState(0); // px, additional translate while dragging

  const dragRef = React.useRef({ active: false, startX: 0, startIndex: 0, moved: false });

  // Measure: card width = clamp(200, 22vw, 260), gap = 18px.
  React.useEffect(() => {
    const measure = () => {
      const stage = stageRef.current;
      if (!stage) return;
      const card = stage.querySelector('.crs-card');
      const cardW = card ? card.offsetWidth : 240;
      setGeom({ cardW, gap: 18, stageW: stage.clientWidth });
    };
    measure();
    window.addEventListener('resize', measure);
    return () => window.removeEventListener('resize', measure);
  }, []);

  const step = geom.cardW + geom.gap;

  // Bounded carousel: how many full cards fit in the visible stage area.
  // The track sits flush with the container's left and right edges so the
  // first card aligns with the heading and the last card aligns with the
  // "Alle Kurse" button. Arrows float over the card edges.
  const SIDE_GUTTER = 56;
  const visibleW = geom.stageW;
  const visibleCount = Math.max(1, Math.floor((visibleW + geom.gap) / step));
  const maxIndex = Math.max(0, N - visibleCount);
  const clampIndex = (i) => Math.max(0, Math.min(maxIndex, i));

  // Left-align: card 0 sits flush with the stage's inner padding (SIDE_GUTTER).
  const baseOffset = -index * step;

  // Rubber-band drag past the edges so the user feels the stop.
  const RUBBER = 0.35;
  const rubberize = (dx) => {
    // dx > 0 means dragging right (toward earlier cards).
    if (dx > 0 && index <= 0) return dx * RUBBER;
    if (dx < 0 && index >= maxIndex) return dx * RUBBER;
    return dx;
  };

  // Pointer-drag to swipe.
  const onPointerDown = (e) => {
    if (e.button !== undefined && e.button !== 0) return;
    // Don't capture if user clicked the side arrow or any nested button
    const t = e.target;
    if (t && t.closest && t.closest('[data-side-arrow]')) return;
    // NOTE: we deliberately do NOT call setPointerCapture here. Capturing on
    // pointerdown re-targets the synthesized click event to the stage div,
    // which swallows clicks on the <a> cards (the browser never sees them as
    // link activations). We only capture once the user has actually moved
    // past the drag threshold — see onPointerMove below.
    dragRef.current = {
      active: true, startX: e.clientX, startIndex: index, moved: false,
      pointerId: e.pointerId, captured: false,
    };
  };
  const onPointerMove = (e) => {
    if (!dragRef.current.active) return;
    const dx = e.clientX - dragRef.current.startX;
    if (!dragRef.current.moved && Math.abs(dx) > 5) {
      dragRef.current.moved = true;
      // Now that we know it's a drag, capture the pointer so we keep getting
      // move/up events even if the cursor leaves the stage.
      if (!dragRef.current.captured) {
        try {
          e.currentTarget.setPointerCapture(e.pointerId);
          dragRef.current.captured = true;
        } catch (_) {}
      }
    }
    if (dragRef.current.moved) {
      setDrag(rubberize(dx));
    }
  };
  const onPointerUp = (e) => {
    if (!dragRef.current.active) return;
    const dx = e.clientX - dragRef.current.startX;
    dragRef.current.active = false;
    setDrag(0);
    // Snap: shift index by however many "steps" the drag covered (rounded),
    // clamped to [0, maxIndex] so we hit a hard stop at the ends.
    const delta = Math.round(-dx / step);
    if (delta !== 0) setIndex(i => clampIndex(i + delta));
    if (dragRef.current.captured) {
      try { e.currentTarget.releasePointerCapture(e.pointerId); } catch (_) {}
    }
    // Swallow the click that follows a drag so cards don't open accidentally
    if (dragRef.current.moved) {
      const swallow = (ev) => { ev.stopPropagation(); ev.preventDefault(); };
      e.currentTarget.addEventListener('click', swallow, { capture: true, once: true });
      // Reset moved flag for next interaction
      setTimeout(() => { dragRef.current.moved = false; }, 0);
    }
  };

  // Re-clamp the active index when the viewport changes (resize might
  // increase visibleCount and lower maxIndex below the current index).
  React.useEffect(() => {
    setIndex(i => clampIndex(i));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [maxIndex]);

  // Render slots: only the real categories — no infinite wrap. Bounds give
  // the slider a clear start and end.
  const slots = courseCategories.map((cat, i) => ({ slot: i, cat }));

  const atStart = index <= 0;
  const atEnd = index >= maxIndex;

  const scrollPillsBy = (dir) => {
    const p = pillsRef.current;
    if (!p) return;
    p.scrollBy({ left: dir * Math.max(220, p.clientWidth * 0.6), behavior: 'smooth' });
  };

  // Track pill scroll position so we can disable the arrows at the ends.
  const [pillBounds, setPillBounds] = React.useState({ atStart: true, atEnd: false });
  React.useEffect(() => {
    const p = pillsRef.current;
    if (!p) return;
    const update = () => {
      const max = p.scrollWidth - p.clientWidth;
      setPillBounds({
        atStart: p.scrollLeft <= 1,
        atEnd: p.scrollLeft >= max - 1,
      });
    };
    update();
    p.addEventListener('scroll', update, { passive: true });
    window.addEventListener('resize', update);
    return () => {
      p.removeEventListener('scroll', update);
      window.removeEventListener('resize', update);
    };
  }, []);

  return (
    <section id="angebot" style={{ padding: 'clamp(56px, 8vw, 96px) 0 clamp(40px, 6vw, 64px)' }}>
      <div className="container">
        <div ref={headRef} className="reveal" style={{
          marginBottom: 48, display: 'flex', alignItems: 'flex-end',
          justifyContent: 'space-between', gap: 24, flexWrap: 'wrap',
        }}>
          <div style={{ maxWidth: 720 }}>
            <div className="eyebrow" style={{ marginBottom: 16 }}>Angebot</div>
            <h2 style={{
              fontSize: 'clamp(36px, 5vw, 56px)', lineHeight: 1.1, letterSpacing: '-0.015em',
              marginBottom: 20,
              fontVariationSettings: '"opsz" 96, "SOFT" 50, "WONK" 1',
            }}>
              Begleitung über alle{' '}
              <em className="bacalisties" style={{ color: 'var(--heather-plum)' }}>Lebensphasen</em>.
            </h2>
            <p className="lead" style={{ maxWidth: 600 }}>
              Vom ersten Spüren bis lange nach der Geburt — und durch alles, was Frausein
              sonst noch ausmacht. Kurse, Einzelbegleitung, Frauenkreise.
            </p>
          </div>
          <a href="Alle Kurse.html" className="btn btn-secondary" style={{
            textDecoration: 'none', whiteSpace: 'nowrap',
          }}>
            Alle Kurse <span style={{ fontSize: 17, marginLeft: 2 }}>→</span>
          </a>
        </div>
      </div>

      {/* Card carousel — transform-based, pointer-drag swipe */}
      <div className="container" style={{ position: 'relative' }}>
        <CrsArrow dir={-1} onClick={() => setIndex(i => clampIndex(i - 1))} gutter={SIDE_GUTTER} disabled={atStart} />
        <CrsArrow dir={1} onClick={() => setIndex(i => clampIndex(i + 1))} gutter={SIDE_GUTTER} disabled={atEnd} />

        <div
          ref={stageRef}
          onPointerDown={onPointerDown}
          onPointerMove={onPointerMove}
          onPointerUp={onPointerUp}
          onPointerCancel={onPointerUp}
          style={{
            position: 'relative',
            overflow: 'hidden',
            padding: '4px 0 24px',
            touchAction: 'pan-y',
            cursor: dragRef.current.active ? 'grabbing' : 'grab',
            userSelect: 'none',
            WebkitUserSelect: 'none',
          }}
        >
          <div
            ref={trackRef}
            style={{
              display: 'flex', gap: geom.gap,
              paddingLeft: 0,
              paddingRight: 0,
              transform: `translate3d(${baseOffset + drag}px, 0, 0)`,
              transition: dragRef.current.active ? 'none' : 'transform 480ms cubic-bezier(0.2, 0.8, 0.2, 1)',
              willChange: 'transform',
            }}
          >
            {slots.map(({ slot, cat: c }) => (
              <a key={slot} className="crs-card"
                href={`Alle Kurse.html#cat=${c.id}`}
                style={{
                  flex: '0 0 clamp(200px, 22vw, 260px)',
                  aspectRatio: '3/4',
                  borderRadius: 20,
                  background: c.tone.bg,
                  color: c.tone.text,
                  textDecoration: 'none',
                  position: 'relative',
                  overflow: 'hidden',
                  cursor: 'pointer',
                  boxShadow: '0 12px 32px rgba(42,34,29,0.18)',
                  transition: 'transform 240ms var(--ease)',
                  display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
                  padding: '22px 22px 26px',
                  WebkitTapHighlightColor: 'transparent',
                }}
                onMouseEnter={(e) => e.currentTarget.style.transform = 'translateY(-4px)'}
                onMouseLeave={(e) => e.currentTarget.style.transform = 'translateY(0)'}
                onClick={(e) => {
                  // Block native navigation only if this was a drag, not a click.
                  if (dragRef.current.moved) {
                    e.preventDefault();
                    e.stopPropagation();
                  }
                }}
                onDragStart={(e) => e.preventDefault()}
              >
                {/* Themed photo backdrop */}
                <div aria-hidden="true" style={{
                  position: 'absolute', top: 0, left: 0, right: 0, height: '62%',
                  overflow: 'hidden',
                  pointerEvents: 'none',
                }}>
                  <img
                    src={`https://picsum.photos/seed/${encodeURIComponent('weiberei-cat-' + c.id)}/520/420`}
                    alt=""
                    loading="lazy"
                    draggable={false}
                    style={{
                      width: '100%', height: '100%', objectFit: 'cover',
                      mixBlendMode: 'multiply',
                      opacity: 0.78,
                      filter: 'saturate(0.85)',
                    }}
                  />
                  <div style={{
                    position: 'absolute', inset: 0,
                    background: c.tone.solid,
                    mixBlendMode: 'color',
                    opacity: 0.55,
                  }} />
                  <div style={{
                    position: 'absolute', left: 0, right: 0, bottom: -1, height: '60%',
                    background: `linear-gradient(180deg, ${hexToRgba(c.tone.solid, 0)} 0%, ${c.tone.solid} 92%)`,
                  }} />
                </div>

                <div style={{
                  display: 'flex', justifyContent: 'space-between',
                  fontSize: 11, letterSpacing: '0.16em', textTransform: 'uppercase',
                  opacity: 0.85, fontWeight: 600,
                  position: 'relative', zIndex: 2,
                }}>
                  <span>{c.eyebrow}</span>
                  <span>↗</span>
                </div>

                <div style={{ position: 'relative', zIndex: 2 }}>
                  <h3 style={{
                    fontFamily: 'Fraunces, serif',
                    fontSize: 'clamp(20px, 2.1vw, 26px)',
                    lineHeight: 1.1, marginBottom: 10,
                    fontVariationSettings: '"opsz" 96, "SOFT" 40',
                    color: c.tone.text,
                  }}>{c.title}</h3>
                  <p style={{
                    fontSize: 13, lineHeight: 1.5,
                    opacity: 0.88, maxWidth: 220,
                  }}>{c.body}</p>
                </div>
              </a>
            ))}
          </div>
        </div>
      </div>

      {/* Single-row pill carousel — colored by parent category. Stays overflow-scroll;
          pills are tiny and don't have iOS issues. */}
      <div className="container" style={{ marginTop: 28, position: 'relative' }}>
        <CrsArrow dir={-1} onClick={() => scrollPillsBy(-1)} gutter={SIDE_GUTTER} small disabled={pillBounds.atStart} />
        <CrsArrow dir={1} onClick={() => scrollPillsBy(1)} gutter={SIDE_GUTTER} small disabled={pillBounds.atEnd} />

        <div ref={pillsRef} className="crs-pills" style={{
          display: 'flex', gap: 8,
          overflowX: 'auto', overflowY: 'hidden',
          padding: `8px ${SIDE_GUTTER}px`,
          scrollbarWidth: 'none',
          WebkitOverflowScrolling: 'touch',
          scrollSnapType: 'x proximity',
        }}>
          <style>{`.crs-pills::-webkit-scrollbar{display:none}`}</style>
          {directCourses.map(({ label, cat, course }, i) => {
            const active = activeBubble === label;
            return (
              <button key={`${cat.id}-${label}-${i}`} type="button"
                title={cat.title}
                onClick={() => { setActiveBubble(label); setOpenCourse(course); }}
                style={{
                  fontSize: 12, fontWeight: 500,
                  padding: '7px 14px', borderRadius: 999,
                  background: active ? cat.tone.bg : cat.pill.bg,
                  color: active ? cat.tone.text : cat.pill.text,
                  border: '1px solid ' + (active ? 'transparent' : cat.pill.border),
                  cursor: 'pointer',
                  transition: 'all 160ms var(--ease)',
                  whiteSpace: 'nowrap',
                  flex: '0 0 auto',
                  fontFamily: 'inherit',
                  scrollSnapAlign: 'start',
                  display: 'inline-flex', alignItems: 'center', gap: 6,
                }}
                onMouseEnter={(e) => {
                  if (!active) {
                    e.currentTarget.style.transform = 'translateY(-1px)';
                    e.currentTarget.style.boxShadow = '0 2px 6px rgba(42,34,29,0.08)';
                  }
                }}
                onMouseLeave={(e) => {
                  if (!active) {
                    e.currentTarget.style.transform = 'translateY(0)';
                    e.currentTarget.style.boxShadow = 'none';
                  }
                }}
              >
                <span style={{
                  width: 6, height: 6, borderRadius: 999,
                  background: cat.pill.text,
                  opacity: active ? 0.85 : 0.7,
                  flexShrink: 0,
                }} />
                {label}
              </button>
            );
          })}
        </div>
      </div>
      {(() => {
        const Detail = window.WBCourseDetail;
        return openCourse && Detail ? (
          <Detail course={openCourse} onClose={() => { setOpenCourse(null); setActiveBubble(null); }} />
        ) : null;
      })()}
    </section>
  );
};

// Floating side arrow — uses the same gutter so card arrows + pill arrows line up vertically.
const CrsArrow = ({ dir, onClick, gutter = 56, small, disabled }) => {
  const size = small ? 32 : 40;
  const offset = Math.max(8, (gutter - size) / 2);
  return (
    <button type="button" onClick={disabled ? undefined : onClick}
      data-side-arrow="true"
      disabled={disabled}
      aria-label={dir < 0 ? 'Vorherige' : 'Nächste'}
      aria-disabled={disabled || undefined}
      style={{
        position: 'absolute',
        top: '50%',
        [dir < 0 ? 'left' : 'right']: offset,
        transform: 'translateY(-50%)',
        zIndex: 5,
        width: size, height: size, borderRadius: 999,
        background: 'rgba(250, 246, 241, 0.9)',
        backdropFilter: 'blur(8px)',
        WebkitBackdropFilter: 'blur(8px)',
        border: '1px solid rgba(196, 169, 143, 0.5)',
        color: 'var(--ink)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        cursor: disabled ? 'not-allowed' : 'pointer',
        fontSize: small ? 13 : 15,
        boxShadow: disabled ? 'none' : '0 4px 12px rgba(42,34,29,0.10)',
        opacity: disabled ? 0.35 : 1,
        transition: 'all 160ms var(--ease)',
      }}
      onMouseEnter={(e) => {
        if (disabled) return;
        e.currentTarget.style.background = 'var(--heather-plum)';
        e.currentTarget.style.color = 'var(--offwhite)';
        e.currentTarget.style.borderColor = 'var(--heather-plum)';
      }}
      onMouseLeave={(e) => {
        if (disabled) return;
        e.currentTarget.style.background = 'rgba(250, 246, 241, 0.9)';
        e.currentTarget.style.color = 'var(--ink)';
        e.currentTarget.style.borderColor = 'rgba(196, 169, 143, 0.5)';
      }}
    >{dir < 0 ? '←' : '→'}</button>
  );
};

window.CourseCarousel = CourseCarousel;
