// AKC Course Booking — Screens
// Three screens: Search → Intake Cards (combined as 'browse'), Registration, Confirmation.
// All UI is original — no recreation of branded UIs.

const { useState, useMemo, useEffect, useRef } = React;

// ───────────────────────────────────────────────────────────────────────────
// Atoms
// ───────────────────────────────────────────────────────────────────────────

function Field({ label, hint, error, children, span }) {
  return (
    <div className="akc-field" style={span ? { gridColumn: `span ${span}` } : null}>
      {label && <label className="akc-field-label">{label}</label>}
      {children}
      {error ? <div className="akc-error">{error}</div> : hint ? <div className="akc-field-hint">{hint}</div> : null}
    </div>
  );
}

function Stepper({ step }) {
  // step: 'browse' | 'register' | 'done'
  const order = ['browse', 'register', 'done'];
  const idx = order.indexOf(step);
  const items = [
    { k: 'browse', label: 'Find your class' },
    { k: 'register', label: 'Register' },
    { k: 'done', label: 'Confirmation' },
  ];
  return (
    <div className="akc-stepper" role="list">
      {items.map((it, i) => (
        <React.Fragment key={it.k}>
          <div className={
            'step ' + (i < idx ? 'is-done' : i === idx ? 'is-current' : '')
          }>
            <span className="n">{i < idx ? '✓' : i + 1}</span>
            <span>{it.label}</span>
          </div>
          {i < items.length - 1 && <span className="bar" />}
        </React.Fragment>
      ))}
    </div>
  );
}

// ───────────────────────────────────────────────────────────────────────────
// 1. Browse — search + filters + intake cards
// ───────────────────────────────────────────────────────────────────────────

function BrowseScreen({ device, onSelectIntake }) {
  // Read URL params once on mount to support deep-linking
  const _p = useMemo(() => new URLSearchParams(window.location.search), []);
  const deepLinkedCourse = useMemo(() => _p.get('course') || '', [_p]);

  const [query, setQuery] = useState(deepLinkedCourse || _p.get('q') || '');
  const [language, setLanguage] = useState(_p.get('language') || '');
  const [location, setLocation] = useState(_p.get('location') || '');
  const [dateFrom, setDateFrom] = useState(_p.get('from') || '');
  const [dateTo, setDateTo] = useState(_p.get('to') || '');
  const [selectedMainCategory, setSelectedMainCategory] = useState(_p.get('category') || '');
  const [selectedSubCategory, setSelectedSubCategory] = useState(_p.get('sub') || '');

  // Consume prefetch data fired during the splash (if it arrived in time)
  const _pre = window.AKC_PREFETCH || null;
  if (_pre) delete window.AKC_PREFETCH;
  const skipFirstFetch = useRef(!!_pre);

  const [courses, setCourses] = useState(_pre ? _pre.courses : []);
  const [filterOptions, setFilterOptions] = useState(_pre ? {
    languages: mergeSelected(_pre.options.languages, _p.get('language') || ''),
    locations: mergeSelected(_pre.options.locations, _p.get('location') || ''),
  } : {
    languages: _p.get('language') ? [_p.get('language')] : [],
    locations: _p.get('location') ? [_p.get('location')] : [],
  });
  const [expanded, setExpanded] = useState(null);
  const [loading, setLoading] = useState(!_pre); // no skeleton if prefetch is ready
  const [error, setError] = useState('');

  useEffect(() => {
    // Skip the very first fetch if prefetch data already populated the state
    if (skipFirstFetch.current) { skipFirstFetch.current = false; return; }

    let alive = true;
    const timer = setTimeout(async () => {
      setLoading(true);
      setError('');
      try {
        const filters = { course_query: query, language, location, date_from: dateFrom, date_to: dateTo };
        const browseData = await window.fetchBrowseData(filters);
        const nextCourses = browseData.courses;
        const nextOptions = browseData.options;
        if (!alive) return;
        setCourses(nextCourses);
        setFilterOptions({
          languages: mergeSelected(nextOptions.languages, language),
          locations: mergeSelected(nextOptions.locations, location),
        });
      } catch (err) {
        if (!alive) return;
        setCourses([]);
        setError(backendErrorMessage(err));
      } finally {
        if (alive) setLoading(false);
      }
    }, 250);

    return () => {
      alive = false;
      clearTimeout(timer);
    };
  }, [query, language, location, dateFrom, dateTo]);

  // Keep URL in sync so the current filter state is always shareable
  useEffect(() => {
    const params = new URLSearchParams();
    if (query)                params.set('q',        query);
    if (language)             params.set('language', language);
    if (location)             params.set('location', location);
    if (dateFrom)             params.set('from',     dateFrom);
    if (dateTo)               params.set('to',       dateTo);
    if (selectedMainCategory) params.set('category', selectedMainCategory);
    if (selectedSubCategory)  params.set('sub',      selectedSubCategory);
    const qs = params.toString();
    history.replaceState(null, '', window.location.pathname + (qs ? '?' + qs : ''));
  }, [query, language, location, dateFrom, dateTo, selectedMainCategory, selectedSubCategory]);

  const isMobile = device === 'mobile';
  const categoryStats = useMemo(() => buildCategoryStats(courses), [courses]);
  const visibleCourses = useMemo(() => {
    if (selectedSubCategory) return courses.filter(course => course.category === selectedSubCategory);
    if (selectedMainCategory) return courses.filter(course => course.main_category === selectedMainCategory);
    return courses;
  }, [courses, selectedMainCategory, selectedSubCategory]);
  const intakeCount = visibleCourses.reduce((sum, course) => sum + course.intakes.length, 0);
  const activeCategoryLabel = selectedSubCategory || selectedMainCategory;

  useEffect(() => {
    // Wait until data has loaded before validating/clearing category state.
    // Without this guard, URL-initialised categories would be wiped on first
    // render when categoryStats is still empty.
    if (categoryStats.length === 0) return;

    if (!selectedMainCategory) {
      if (selectedSubCategory) setSelectedSubCategory('');
      return;
    }

    const selectedGroup = categoryStats.find(group => group.category === selectedMainCategory);
    if (!selectedGroup) {
      setSelectedMainCategory('');
      setSelectedSubCategory('');
      return;
    }

    if (selectedSubCategory && !selectedGroup.subcategories.some(item => item.category === selectedSubCategory)) {
      setSelectedSubCategory('');
    }
  }, [categoryStats, selectedMainCategory, selectedSubCategory]);

  const handleMainCategorySelect = (category) => {
    setSelectedMainCategory(category);
    setSelectedSubCategory('');
  };

  useEffect(() => {
    setExpanded(prev => {
      if (prev && visibleCourses.some(course => courseKey(course) === prev)) return prev;
      if (deepLinkedCourse) {
        const target = findDeepLinkedCourse(visibleCourses, deepLinkedCourse);
        if (target) return courseKey(target);
      }
      if (!visibleCourses[0] || visibleCourses[0].intakes.length > 8) return null;
      return courseKey(visibleCourses[0]);
    });
  }, [visibleCourses, deepLinkedCourse]);

  return (
    <div className="akc-stack-lg" style={{ padding: isMobile ? '20px 16px 32px' : '28px 32px 40px' }}>
      <header className="akc-stack" style={{ gap: 6 }}>
        <div className="akc-eyebrow">Step 1 of 3</div>
        <h1 className="akc-h1">Find a class that fits.</h1>
      </header>

      {/* search bar */}
      <div className="akc-card" style={{ padding: 14, display: 'flex', gap: 10, flexDirection: isMobile ? 'column' : 'row', alignItems: isMobile ? 'stretch' : 'center' }}>
        <div style={{ position: 'relative', flex: 2, minWidth: 0 }}>
          <SearchIcon style={{ position: 'absolute', left: 12, top: '50%', transform: 'translateY(-50%)', color: 'var(--muted)' }} />
          <input
            className="akc-input"
            placeholder="Search by course name, code or topic…"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            style={{ paddingLeft: 38 }}
          />
        </div>
        <DateRangePicker
          from={dateFrom}
          to={dateTo}
          onChange={(a, b) => { setDateFrom(a); setDateTo(b); }}
          isMobile={isMobile}
        />
        <select className="akc-select" value={language} onChange={(e) => setLanguage(e.target.value)} style={{ flex: 1 }}>
          <option value="">All languages</option>
          {filterOptions.languages.map(l => <option key={l} value={l}>{l}</option>)}
        </select>
        <select className="akc-select" value={location} onChange={(e) => setLocation(e.target.value)} style={{ flex: 1 }}>
          <option value="">All locations</option>
          {filterOptions.locations.map(l => <option key={l} value={l}>{l}</option>)}
        </select>
      </div>

      <CategoryFinder
        selectedMain={selectedMainCategory}
        selectedSub={selectedSubCategory}
        stats={categoryStats}
        onSelectMain={handleMainCategorySelect}
        onSelectSub={setSelectedSubCategory}
        onReset={() => {
          setSelectedMainCategory('');
          setSelectedSubCategory('');
        }}
        isMobile={isMobile}
        loading={loading && courses.length === 0}
      />

      {/* results count + clear */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', fontSize: 13, color: 'var(--ink-2)', minHeight: 24 }}>
        {loading ? (
          <span style={{ color: 'var(--muted)', display: 'flex', alignItems: 'center', gap: 8 }}>
            <span style={{
              display: 'inline-block', width: 12, height: 12, borderRadius: '50%',
              border: '2px solid var(--primary-100)', borderTopColor: 'var(--primary)',
              animation: 'akc-spin 0.7s linear infinite', flexShrink: 0,
            }} />
            Searching…
          </span>
        ) : (
          <span>
            <strong style={{ color: 'var(--ink)' }}>{intakeCount}</strong> upcoming intakes across <strong style={{ color: 'var(--ink)' }}>{visibleCourses.length}</strong> courses
            {activeCategoryLabel ? <span> in <strong style={{ color: 'var(--ink)' }}>{activeCategoryLabel}</strong></span> : null}
          </span>
        )}
        {!loading && (query || language || location || dateFrom || dateTo || selectedMainCategory || selectedSubCategory) && (
          <button className="akc-btn akc-btn--link" onClick={() => { setQuery(''); setLanguage(''); setLocation(''); setDateFrom(''); setDateTo(''); setSelectedMainCategory(''); setSelectedSubCategory(''); }}>
            Clear filters
          </button>
        )}
      </div>

      {/* course list */}
      <div className="akc-stack" style={{ gap: 14 }}>
        {loading && (
          <>
            {[
              { title: '75%', desc: '50%', tag1: 90, tag2: 110 },
              { title: '55%', desc: '40%', tag1: 70, tag2: 95  },
              { title: '65%', desc: '45%', tag1: 85, tag2: 80  },
              { title: '60%', desc: '35%', tag1: 75, tag2: 105 },
            ].map((s, i) => (
              <div key={i} className="akc-skeleton-card" style={{ animationDelay: `${i * 0.1}s` }}>
                <div className="akc-skeleton-card-left">
                  {/* pill + code */}
                  <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
                    <div className="akc-skeleton" style={{ height: 22, width: 110, borderRadius: 99 }} />
                    <div className="akc-skeleton" style={{ height: 14, width: 64 }} />
                    <div className="akc-skeleton" style={{ height: 14, width: 44 }} />
                  </div>
                  {/* title */}
                  <div className="akc-skeleton" style={{ height: 22, width: s.title }} />
                  {/* tags */}
                  <div style={{ display: 'flex', gap: 8 }}>
                    <div className="akc-skeleton" style={{ height: 20, width: s.tag1, borderRadius: 99 }} />
                    <div className="akc-skeleton" style={{ height: 20, width: s.tag2, borderRadius: 99 }} />
                  </div>
                </div>
                <div className="akc-skeleton-card-right">
                  <div className="akc-skeleton" style={{ height: 11, width: 32 }} />
                  <div className="akc-skeleton" style={{ height: 30, width: 80 }} />
                  <div className="akc-skeleton" style={{ height: 12, width: 110, borderRadius: 99 }} />
                  <div className="akc-skeleton" style={{ height: 14, width: 90, marginTop: 4 }} />
                </div>
              </div>
            ))}
          </>
        )}
        {!loading && error && (
          <div className="akc-card" style={{ textAlign: 'center', padding: 40, color: 'var(--danger)' }}>
            {error}
          </div>
        )}
        {!loading && !error && visibleCourses.map(course => (
          <CourseGroup
            key={courseKey(course)}
            course={course}
            expanded={expanded === courseKey(course)}
            onToggle={() => setExpanded(expanded === courseKey(course) ? null : courseKey(course))}
            onSelectIntake={onSelectIntake}
            isMobile={isMobile}
          />
        ))}
        {!loading && !error && visibleCourses.length === 0 && (
          <div className="akc-card" style={{ textAlign: 'center', padding: 40, color: 'var(--muted)' }}>
            No courses match these filters. Try clearing them.
          </div>
        )}
      </div>
    </div>
  );
}

function buildCategoryStats(courses) {
  const groups = window.SMART_CATEGORY_GROUPS || [];
  const subToMain = new Map();
  groups.forEach(group => {
    group.subcategories.forEach(subcategory => subToMain.set(subcategory, group.label));
  });

  const groupCounts = new Map();
  const subCounts = new Map();
  courses.forEach((course) => {
    const subcategory = course.sub_category || course.category || 'Other';
    const mainCategory = course.main_category || subToMain.get(subcategory) || (window.smartMainCategoryFor ? window.smartMainCategoryFor(subcategory) : 'General Courses');
    const currentGroup = groupCounts.get(mainCategory) || { courses: 0, intakes: 0 };
    const currentSub = subCounts.get(subcategory) || { courses: 0, intakes: 0 };
    groupCounts.set(mainCategory, {
      courses: currentGroup.courses + 1,
      intakes: currentGroup.intakes + course.intakes.length,
    });
    subCounts.set(subcategory, {
      courses: currentSub.courses + 1,
      intakes: currentSub.intakes + course.intakes.length,
    });
  });

  return groups
    .map(group => ({
      category: group.label,
      courses: (groupCounts.get(group.label) || { courses: 0 }).courses,
      intakes: (groupCounts.get(group.label) || { intakes: 0 }).intakes,
      subcategories: group.subcategories
        .filter(subcategory => subCounts.has(subcategory))
        .map(subcategory => ({ category: subcategory, ...subCounts.get(subcategory) })),
    }))
    .filter(group => group.courses > 0);
}

function CategoryTile({ label, count, isActive, color, colorLight, variant, onClick }) {
  const [hovered, setHovered] = useState(false);
  let style = {};
  if (isActive && color) {
    style = { borderColor: color, background: color, color: '#fff', boxShadow: '0 8px 18px rgba(0,0,0,0.16)' };
  } else if (hovered && color) {
    style = { borderColor: color, background: colorLight };
  }
  return (
    <button
      className={`akc-category-tile akc-category-tile--${variant}` + (isActive ? ' is-active' : '')}
      type="button"
      style={style}
      onClick={onClick}
      onMouseEnter={() => setHovered(true)}
      onMouseLeave={() => setHovered(false)}
    >
      <span>{label}</span>
      <strong>{count}</strong>
    </button>
  );
}

function CategoryFinder({ selectedMain, selectedSub, stats, onSelectMain, onSelectSub, onReset, isMobile, loading }) {
  const totalCourses = stats.reduce((sum, item) => sum + item.courses, 0);
  const totalIntakes = stats.reduce((sum, item) => sum + item.intakes, 0);
  const selectedGroup = selectedMain ? stats.find(item => item.category === selectedMain) : null;

  const colorMap = useMemo(() => {
    const map = new Map();
    (window.SMART_CATEGORY_GROUPS || []).forEach(group => {
      if (group.color) map.set(group.label, { color: group.color, colorLight: group.colorLight });
    });
    return map;
  }, []);

  const getColors = (category) => colorMap.get(category) || {};
  const activeColors = selectedMain ? getColors(selectedMain) : {};

  return (
    <section className="akc-card akc-category-finder" aria-label="Find courses by category">
      <div className="akc-category-finder__head">
        <div>
          <div className="akc-eyebrow">Smart course finder</div>
          <h2 className="akc-h2" style={{ fontSize: isMobile ? 18 : 20 }}>Browse by course category</h2>
        </div>
        {!loading && (
          <span className="akc-category-total">{totalCourses} courses · {totalIntakes} intakes</span>
        )}
      </div>
      {loading ? (
        <div className="akc-category-grid akc-category-grid--main" style={{ marginTop: 4 }}>
          {[1,2,3,4,5,6].map(i => <div key={i} className="akc-skeleton-tile" />)}
        </div>
      ) : (
        <>
          <div className="akc-category-grid akc-category-grid--main">
            <CategoryTile
              label="All courses"
              count={totalCourses}
              isActive={!selectedMain}
              color={null}
              colorLight={null}
              variant="main"
              onClick={onReset}
            />
            {stats.map(item => {
              const c = getColors(item.category);
              return (
                <CategoryTile
                  key={item.category}
                  label={item.category}
                  count={item.courses}
                  isActive={selectedMain === item.category}
                  color={c.color}
                  colorLight={c.colorLight}
                  variant="main"
                  onClick={() => onSelectMain(item.category)}
                />
              );
            })}
          </div>

          {selectedGroup && (
            <div className="akc-subcategory-panel">
              <div className="akc-subcategory-panel__head">
                <span>Course types in {selectedGroup.category}</span>
                <strong>{selectedGroup.courses} courses - {selectedGroup.intakes} intakes</strong>
              </div>
              <div className="akc-category-grid akc-category-grid--sub">
                <CategoryTile
                  label={`All ${selectedGroup.category}`}
                  count={selectedGroup.courses}
                  isActive={!selectedSub}
                  color={activeColors.color}
                  colorLight={activeColors.colorLight}
                  variant="sub"
                  onClick={() => onSelectSub('')}
                />
                {selectedGroup.subcategories.map(item => (
                  <CategoryTile
                    key={item.category}
                    label={item.category}
                    count={item.courses}
                    isActive={selectedSub === item.category}
                    color={activeColors.color}
                    colorLight={activeColors.colorLight}
                    variant="sub"
                    onClick={() => onSelectSub(item.category)}
                  />
                ))}
              </div>
            </div>
          )}
        </>
      )}
    </section>
  );
}

function mergeSelected(options, selected) {
  const values = Array.isArray(options) ? options.filter(Boolean) : [];
  if (selected && !values.includes(selected)) values.unshift(selected);
  return values;
}

function courseKey(course) {
  return course.group_key || course.code || course.name;
}

function findDeepLinkedCourse(courses, requestedCourse) {
  const target = String(requestedCourse || '').trim().toLowerCase();
  if (!target) return null;
  return courses.find(course => [
    course.name,
    course.code,
    course.group_key,
  ].some(value => String(value || '').trim().toLowerCase() === target)) || courses[0] || null;
}

function backendErrorMessage(err) {
  const message = String(err && err.message ? err.message : '');
  if (/failed to fetch|load failed|network/i.test(message)) {
    return `Live AKC backend is not running at ${window.AKC_API_BASE}. Start the Postgres API server, then refresh this page.`;
  }
  return message || 'Unable to load live AKC intakes.';
}

function CourseGroup({ course, expanded, onToggle, onSelectIntake, isMobile }) {
  const displayPrice = Number(course.from_price || course.full_fee || 0);
  const hasDisplayPrice = displayPrice > 0;

  return (
    <article className="akc-card" style={{ padding: 0, overflow: 'hidden' }}>
      <button
        onClick={onToggle}
        style={{
          appearance: 'none', border: 0, background: 'transparent', width: '100%',
          padding: 'var(--pad)', textAlign: 'left', cursor: 'pointer',
          display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr auto', gap: 14, alignItems: 'center',
        }}
      >
        <div className="akc-stack" style={{ gap: 6 }}>
          <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' }}>
            <span className="akc-pill akc-pill--primary">{course.category}</span>
            <span style={{ fontSize: 12, color: 'var(--muted)', fontWeight: 600 }}>{course.code}</span>
            <span style={{ fontSize: 12, color: 'var(--muted)' }}>·</span>
            <span style={{ fontSize: 12, color: 'var(--muted)' }}>{course.duration}</span>
          </div>
          <h3 className="akc-h2" style={{ fontSize: 18 }}>{course.name}</h3>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginTop: 4 }}>
            {course.tags.map(t => <span key={t} className="akc-pill">{t}</span>)}
          </div>
        </div>

        <div style={{ display: 'flex', flexDirection: 'column', alignItems: isMobile ? 'flex-start' : 'flex-end', gap: 6, marginTop: isMobile ? 4 : 0 }}>
          {hasDisplayPrice ? (
            <>
              <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.06em', textTransform: 'uppercase' }}>From</div>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
            <span style={{ fontFamily: "'Inter', sans-serif", fontSize: 24, fontWeight: 600, color: 'var(--primary-700)' }} className="akc-tabular">
                  {window.money(displayPrice)}
            </span>
                {Number(course.full_fee || 0) > displayPrice && (
                  <span className="akc-strike akc-tabular" style={{ fontSize: 13 }}>{window.money(course.full_fee)}</span>
                )}
          </div>
              <span style={{ fontSize: 11, color: course.funding_eligible ? 'var(--good)' : 'var(--muted)', fontWeight: 600 }}>
                {course.funding_eligible ? 'up to 70% off after eligible subsidy' : 'Eligible learners may use SkillsFuture Credit'}
              </span>
            </>
          ) : (
            <span style={{ fontSize: 12, color: 'var(--muted)', fontWeight: 700 }}>Fee to be confirmed</span>
          )}
          <span style={{ fontSize: 12, color: 'var(--primary)', fontWeight: 600, marginTop: 4 }}>
            {expanded ? 'Hide' : 'View'} {course.intakes.length} intake{course.intakes.length === 1 ? '' : 's'} ↓
          </span>
        </div>
      </button>

      {expanded && (
        <div style={{ borderTop: '1px solid var(--line)', padding: 'var(--pad)', background: 'var(--surface-2)', display: 'flex', flexDirection: 'column', gap: 10 }}>
          {course.intakes.map(intake => (
            <IntakeRow key={intake.id} intake={intake} onSelect={() => onSelectIntake(course, intake)} isMobile={isMobile} />
          ))}
        </div>
      )}
    </article>
  );
}

function IntakeRow({ intake, onSelect, isMobile }) {
  const startDate = new Date(intake.start + 'T00:00:00');
  const day = startDate.getDate();
  const month = startDate.toLocaleDateString('en-SG', { month: 'short' });
  const weekday = startDate.toLocaleDateString('en-SG', { weekday: 'short' });
  const hasLessonDates = Boolean(String(intake.date_display || '').trim());
  const lessonDateMatch = String(intake.date_display || '').match(/^(.*?)(\s*\([^)]*CLASS\))?\s*$/i);
  const lessonDates = lessonDateMatch?.[1]?.trim() || intake.date_display;
  const dateText = hasLessonDates ? lessonDates : window.fmtDateRange(intake.start, intake.end);
  const hasDetailedSchedule = Boolean(String(intake.schedule_display || '').trim());
  const scheduleText = intake.schedule_display || `${intake.start_time}–${intake.end_time}`;

  return (
    <div className="akc-intake" style={isMobile ? { gridTemplateColumns: 'auto 1fr', gap: 12 } : null}>
      <div className="akc-intake__date">
        <span className="d akc-tabular">{day}</span>
        <span className="m">{month}</span>
        <span className="w">{weekday}</span>
      </div>
      <div className="akc-stack" style={{ gap: 4 }}>
        <div style={{ display: 'flex', gap: 8, alignItems: 'baseline', flexWrap: 'wrap' }}>
          <span style={{ fontWeight: 700, fontSize: 14 }}>
            {hasLessonDates ? `Class dates: ${dateText}` : dateText}
          </span>
        </div>
        {hasDetailedSchedule && (
          <details style={{ fontSize: 12, color: 'var(--ink-2)' }}>
            <summary style={{ cursor: 'pointer', color: 'var(--primary)', fontWeight: 600, width: 'fit-content' }}>
              View daily timings
            </summary>
            <div style={{ whiteSpace: 'pre-line', lineHeight: 1.5, marginTop: 5 }}>
              {scheduleText}
            </div>
          </details>
        )}
        <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', fontSize: 12, color: 'var(--ink-2)' }}>
          {!hasDetailedSchedule && <span>🕘 {scheduleText}</span>}
          <span>📍 {intake.venue}</span>
          <span>🗣 {intake.language}</span>
          {intake.backend?.mom_reference_no && <span>TGS Code: {intake.backend.mom_reference_no}</span>}
        </div>
      </div>
      {!isMobile && (
        <button className="akc-btn akc-btn--primary" onClick={onSelect}>
          Register →
        </button>
      )}
      {isMobile && (
        <button className="akc-btn akc-btn--primary akc-btn--block" onClick={onSelect} style={{ gridColumn: '1 / -1' }}>
          Register →
        </button>
      )}
    </div>
  );
}

// ───────────────────────────────────────────────────────────────────────────
// Date range picker (Agoda-style)
// ───────────────────────────────────────────────────────────────────────────

function DateRangePicker({ from, to, onChange, isMobile }) {
  const [open, setOpen] = useState(false);
  const [picking, setPicking] = useState(null); // ISO of first click while awaiting second
  const [hover, setHover] = useState(null);     // ISO of hovered cell during range preview
  const wrapRef = useRef(null);

  const nowDate = new Date();
  const todayISO = `${nowDate.getFullYear()}-${String(nowDate.getMonth() + 1).padStart(2, '0')}-${String(nowDate.getDate()).padStart(2, '0')}`;

  const [viewYear, setViewYear] = useState(nowDate.getFullYear());
  const [viewMonth, setViewMonth] = useState(nowDate.getMonth()); // 0-indexed

  // Close on outside click
  useEffect(() => {
    if (!open) return;
    const handler = (e) => {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) {
        setOpen(false);
        setPicking(null);
        setHover(null);
      }
    };
    document.addEventListener('mousedown', handler);
    return () => document.removeEventListener('mousedown', handler);
  }, [open]);

  // When picker opens, jump to the 'from' month (or today)
  useEffect(() => {
    if (open) {
      const d = from ? new Date(from + 'T00:00:00') : nowDate;
      setViewYear(d.getFullYear());
      setViewMonth(d.getMonth());
      setPicking(null);
      setHover(null);
    }
  }, [open]);

  // Build flat array: ISO strings or null (padding cells)
  function getMonthGrid(year, month) {
    const first = new Date(year, month, 1);
    const last = new Date(year, month + 1, 0);
    const startDow = (first.getDay() + 6) % 7; // Monday = 0
    const days = [];
    for (let i = 0; i < startDow; i++) days.push(null);
    for (let d = 1; d <= last.getDate(); d++) {
      days.push(`${year}-${String(month + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`);
    }
    while (days.length % 7 !== 0) days.push(null);
    return days;
  }

  function prevMonth() {
    if (viewMonth === 0) { setViewYear(y => y - 1); setViewMonth(11); }
    else setViewMonth(m => m - 1);
  }

  function nextMonth() {
    if (viewMonth === 11) { setViewYear(y => y + 1); setViewMonth(0); }
    else setViewMonth(m => m + 1);
  }

  function handleDayClick(iso) {
    if (iso < todayISO) return; // ignore past dates
    if (!picking) {
      setPicking(iso);
    } else {
      const [a, b] = iso < picking ? [iso, picking] : [picking, iso];
      onChange(a, b);
      setPicking(null);
      setHover(null);
      setOpen(false);
    }
  }

  function handleDayHover(iso) {
    if (picking) setHover(iso);
  }

  // Effective range for highlight — swap if hover is before picking anchor
  const anchor = picking || from || null;
  const tail   = picking ? (hover || picking) : (to || from || null);
  const [effStart, effEnd] = anchor && tail
    ? (anchor <= tail ? [anchor, tail] : [tail, anchor])
    : [anchor, null];

  function getCellClass(iso) {
    if (!iso) return 'akc-cal-cell is-empty';
    const past     = iso < todayISO;
    const isStart  = iso === effStart;
    const isEnd    = effEnd && iso === effEnd && effEnd !== effStart;
    const inRange  = effStart && effEnd && iso > effStart && iso < effEnd;
    return [
      'akc-cal-cell',
      past     ? 'is-past'                   : '',
      iso === todayISO ? 'is-today'          : '',
      isStart  ? 'is-range-start is-selected': '',
      isEnd    ? 'is-range-end   is-selected': '',
      inRange  ? 'is-in-range'               : '',
      (!isStart && !isEnd && (iso === from || iso === to)) ? 'is-selected' : '',
    ].filter(Boolean).join(' ');
  }

  function fmtShort(iso) {
    if (!iso) return '';
    const d = new Date(iso + 'T00:00:00');
    return d.toLocaleDateString('en-SG', { day: 'numeric', month: 'short' });
  }

  function handleClear(e) {
    if (e) e.stopPropagation();
    onChange('', '');
    setPicking(null);
    setHover(null);
  }

  const hasValue = from || to;
  const triggerLabel = from && to
    ? `${fmtShort(from)} – ${fmtShort(to)}`
    : from
    ? `From ${fmtShort(from)}`
    : 'Any date';

  const monthTitle = new Date(viewYear, viewMonth, 1)
    .toLocaleDateString('en-SG', { month: 'long', year: 'numeric' });

  const flatGrid = getMonthGrid(viewYear, viewMonth);
  const weeks = [];
  for (let i = 0; i < flatGrid.length; i += 7) weeks.push(flatGrid.slice(i, i + 7));

  const hintText = picking
    ? (hover
        ? `${fmtShort(effStart)} – ${fmtShort(effEnd)}`
        : 'Now pick an end date')
    : '';

  return (
    <div className="akc-datepicker-wrap" ref={wrapRef}>
      <button
        type="button"
        className={`akc-datepicker-trigger${open ? ' is-open' : ''}${hasValue ? ' has-value' : ''}`}
        onClick={() => setOpen(o => !o)}
        aria-label="Filter by date range"
      >
        {/* calendar icon */}
        <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
          <rect x="1" y="3" width="14" height="12" rx="2" stroke="currentColor" strokeWidth="1.4"/>
          <path d="M1 7h14" stroke="currentColor" strokeWidth="1.4"/>
          <path d="M5 1v3M11 1v3" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round"/>
        </svg>
        <span>{triggerLabel}</span>
        {hasValue && (
          <span className="akc-datepicker-clear" onClick={handleClear} role="button" aria-label="Clear dates">✕</span>
        )}
      </button>

      {open && (
        <div className="akc-cal-dropdown">
          {/* Month navigation */}
          <div className="akc-cal-header">
            <button type="button" className="akc-cal-arrow" onClick={prevMonth}>‹</button>
            <div className="akc-cal-header-months">{monthTitle}</div>
            <button type="button" className="akc-cal-arrow" onClick={nextMonth}>›</button>
          </div>

          {/* Hint while picking second date */}
          {hintText && <div className="akc-cal-hint">{hintText}</div>}

          {/* Calendar grid */}
          <div className="akc-cal-months" onMouseLeave={() => { if (picking) setHover(null); }}>
            <div className="akc-cal-month">
              <div className="akc-cal-dow">
                {['Mo','Tu','We','Th','Fr','Sa','Su'].map(d => <span key={d}>{d}</span>)}
              </div>
              {weeks.map((week, wi) => (
                <div key={wi} className="akc-cal-week">
                  {week.map((iso, di) => {
                    const clickable = iso && iso >= todayISO;
                    return (
                      <div
                        key={di}
                        className={getCellClass(iso)}
                        onClick={clickable ? () => handleDayClick(iso) : undefined}
                        onMouseEnter={clickable ? () => handleDayHover(iso) : undefined}
                      >
                        {iso && <span className="akc-cal-day-num">{parseInt(iso.slice(8), 10)}</span>}
                      </div>
                    );
                  })}
                </div>
              ))}
            </div>
          </div>

          {/* Footer */}
          <div className="akc-cal-footer">
            <button
              type="button"
              className="akc-btn akc-btn--sm"
              onClick={() => { handleClear(); setOpen(false); }}
            >
              Clear
            </button>
            <button
              type="button"
              className="akc-btn akc-btn--sm akc-btn--primary"
              onClick={() => { if (!picking) setOpen(false); }}
            >
              {picking ? 'Select end date…' : 'Done'}
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

function SearchIcon({ style }) {
  return (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none" style={style}>
      <circle cx="7" cy="7" r="5" stroke="currentColor" strokeWidth="1.5" />
      <path d="M11 11l3 3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
    </svg>
  );
}

Object.assign(window, { BrowseScreen, Field, Stepper });
