const AdminSchedule = () => {
  const data = window.useAdminData();
  const [scope,         setScope]         = React.useState('today');     // 'yesterday' | 'today' | 'tomorrow' | 'week' | 'lastweek' | 'nextweek' | 'month' | 'upcoming' | 'past' | 'all' | 'custom'
  const [customFrom,    setCustomFrom]    = React.useState('');           // 'YYYY-MM-DD' — inclusive lower bound when scope==='custom'
  const [customTo,      setCustomTo]      = React.useState('');           // 'YYYY-MM-DD' — inclusive upper bound when scope==='custom'
  const [instructorId,  setInstructorId]  = React.useState('');
  const [studentId,     setStudentId]     = React.useState('');
  const [statusFilter,  setStatusFilter]  = React.useState('all');       // all | confirmed | completed | cancelled | reschedule
  const [paidFilter,    setPaidFilter]    = React.useState('all');       // all | paid | unpaid
  const [typeFilter,    setTypeFilter]    = React.useState('all');       // all | onetime | recurring
  const [busyId,        setBusyId]        = React.useState(null);
  const [seriesTarget,  setSeriesTarget]  = React.useState(null);        // booking row for Series… modal
  const [deleteTarget,  setDeleteTarget]  = React.useState(null);        // booking row for Delete modal
  const [transcriptTarget, setTranscriptTarget] = React.useState(null);  // booking row for Transcript modal
  const [detailTarget,  setDetailTarget]  = React.useState(null);        // booking row for the click-to-open detail drawer
  const [rescheduleTarget, setRescheduleTarget] = React.useState(null);  // booking row for the Reschedule modal

  const allBookings = (data.bookings || []);
  const instructors = (data.instructors || []);
  const students    = (data.students || []);
  // The instructor picker lists only real instructors — hide the seeded
  // marketplace sample profiles so it matches the Instructors roster (which
  // also filters `!isSample`). Row name resolution below keeps the full
  // `instructors` list, so any booking still shows its teacher's name.
  const instructorOptions = instructors.filter(i => !i.isSample);

  // Admin TZ — same source the rest of the admin dashboard already uses
  // for time labels (browser TZ, surfaced on every row + in the header).
  // Bug fix 2026-05-21: the day-group key below used to slice the raw
  // UTC ISO string (`scheduledAt.slice(0,10)`), which buckets evening
  // lessons into the wrong day for any admin in a negative-UTC zone.
  // Project to admin TZ first.
  const adminTz = window.adminSchTz;
  const ymdInAdminTz = (iso) => {
    if (!iso) return '';
    try { return window.Calendar.time.dateKey(new Date(iso), adminTz); }
    catch (e) { return (iso || '').slice(0, 10); }
  };

  const today = new Date(); today.setHours(0, 0, 0, 0);
  const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1);  // start of "yesterday" (today-1)
  const tomorrow = new Date(today); tomorrow.setDate(tomorrow.getDate() + 1);
  const dayAfter = new Date(today); dayAfter.setDate(dayAfter.getDate() + 2);   // end of "tomorrow" (exclusive)
  // Monday-anchored calendar weeks (Mon 00:00 → next Mon 00:00) so "This week"
  // is the whole Mon–Sun week containing today, not a rolling today+7 window.
  // getDay(): 0=Sun..6=Sat, so (getDay()+6)%7 is the day offset back to Monday.
  const weekStart = new Date(today); weekStart.setDate(weekStart.getDate() - ((today.getDay() + 6) % 7));  // Monday of this week
  const weekEnd   = new Date(weekStart); weekEnd.setDate(weekEnd.getDate() + 7);    // next Monday (exclusive) == start of "next week"
  const lastWeekStart = new Date(weekStart); lastWeekStart.setDate(lastWeekStart.getDate() - 7);  // Monday of last week
  const nextWeekEnd   = new Date(weekEnd);   nextWeekEnd.setDate(nextWeekEnd.getDate() + 7);      // end of "next week" (exclusive)
  const monthStart = new Date(today.getFullYear(), today.getMonth(),     1);  // first day of this calendar month
  const monthEnd   = new Date(today.getFullYear(), today.getMonth() + 1, 1);  // first day of next month (exclusive)
  const now = new Date();

  // Custom range boundaries. Both inputs are 'YYYY-MM-DD' in the admin's
  // browser TZ; parse to local midnight to match how `today` is built above.
  // `customEndExcl` is the To-day + 1 day so the comparison stays a strict
  // `<` while still including the whole To day (a 23:00 class on the To date
  // still counts). Either side may be blank → that side is unbounded.
  const customStart = customFrom ? new Date(`${customFrom}T00:00:00`) : null;
  const customEndExcl = (() => {
    if (!customTo) return null;
    const d = new Date(`${customTo}T00:00:00`);
    d.setDate(d.getDate() + 1);
    return d;
  })();
  const todayYmdAdmin    = window.Calendar.time.dateKey(today,    adminTz);
  const tomorrowYmdAdmin = window.Calendar.time.dateKey(tomorrow, adminTz);

  const filtered = allBookings.filter(b => {
    const at = new Date(b.scheduledAt);
    if (scope === 'yesterday'&& !(at >= yesterday && at < today)) return false;
    if (scope === 'today'    && !(at >= today && at < tomorrow)) return false;
    if (scope === 'tomorrow' && !(at >= tomorrow && at < dayAfter)) return false;
    if (scope === 'week'     && !(at >= weekStart && at < weekEnd))  return false;
    if (scope === 'lastweek' && !(at >= lastWeekStart && at < weekStart))  return false;
    if (scope === 'nextweek' && !(at >= weekEnd && at < nextWeekEnd)) return false;
    if (scope === 'month'    && !(at >= monthStart && at < monthEnd)) return false;
    if (scope === 'upcoming' && !(at >= now))                    return false;
    if (scope === 'past'     && !(at <  now))                    return false;
    if (scope === 'custom') {
      if (customStart   && at <  customStart)   return false;
      if (customEndExcl && at >= customEndExcl) return false;
    }
    if (instructorId && b.instructorId !== instructorId)         return false;
    if (studentId    && b.studentId !== studentId)               return false;
    if (statusFilter === 'reschedule' && !b.rescheduleRequestedAt) return false;
    if (statusFilter !== 'all' && statusFilter !== 'reschedule' && b.status !== statusFilter) return false;
    if (paidFilter === 'paid'   && !b.paid) return false;
    if (paidFilter === 'unpaid' &&  b.paid) return false;
    // One-time vs part of a recurring series. Series creation always sets
    // both series_id and is_recurring, but treat either as "recurring" so a
    // legacy row with only one set never mislabels as one-time.
    const recurring = !!(b.seriesId || b.isRecurring);
    if (typeFilter === 'recurring' && !recurring) return false;
    if (typeFilter === 'onetime'   &&  recurring) return false;
    return true;
  });

  // Sort: upcoming/today scopes → soonest first; past → most-recent first.
  const sorted = filtered.slice().sort((a, b) => {
    const da = new Date(a.scheduledAt), db = new Date(b.scheduledAt);
    // Historical scopes read best most-recent-first; everything else
    // (incl. a possibly forward-looking custom range) reads soonest-first.
    const recentFirst = scope === 'past' || scope === 'lastweek';
    return recentFirst ? db - da : da - db;
  });

  // Poll Daily.co presence for any booking in its live window (start - 15min
  // through end + 60min). Hook is a no-op when none of `filtered` is live.
  // admin-schedule-live.jsx is guaranteed loaded before this file per
  // the script tag order in index.html, so the hook call is unconditional.
  // Hook ticks at 30s (membership granularity); per-row relative-time copy
  // ticks inside each RowStatus, so AdminSchedule does NOT re-render every
  // second under it.
  const live = window.AdminScheduleLive.useLivePresence(filtered);

  // Group by YYYY-MM-DD so the list reads as "Monday May 19", "Tuesday May 20", etc.
  const groups = (() => {
    const m = new Map();
    for (const b of sorted) {
      const k = ymdInAdminTz(b.scheduledAt);
      if (!m.has(k)) m.set(k, []);
      m.get(k).push(b);
    }
    return Array.from(m.entries()); // [[YYYY-MM-DD, [bookings...]], ...]
  })();

  const dayLabel = (ymd) => {
    if (!ymd) return '';
    const [y, mo, d] = ymd.split('-').map(Number);
    // Construct a Date that represents the same wall-clock day in admin TZ
    // for weekday/month rendering. Using local-midnight is fine for the
    // *label*; the day-bucket math above already lives in admin-TZ space.
    const dt = new Date(y, mo - 1, d);
    const dayName = dt.toLocaleDateString(undefined, { weekday: 'long' });
    const monthDay = dt.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
    if (ymd === todayYmdAdmin)    return `Today · ${dayName} ${monthDay}`;
    if (ymd === tomorrowYmdAdmin) return `Tomorrow · ${dayName} ${monthDay}`;
    return `${dayName}, ${monthDay}`;
  };

  const fld ={ padding:'9px 11px', borderRadius:8, border:'1.5px solid oklch(88% 0.015 265)', fontSize:13, fontFamily:"'Plus Jakarta Sans', sans-serif", color:'oklch(22% 0.06 265)', background:'#fff', outline:'none', boxSizing:'border-box' };
  const scopeChip = (id, label) => ({
    padding:'7px 14px', borderRadius:20, fontSize:12, fontWeight:700, cursor:'pointer',
    fontFamily:"'Plus Jakarta Sans', sans-serif",
    border: scope === id ? '1.5px solid oklch(22% 0.06 265)' : '1.5px solid oklch(88% 0.02 265)',
    background: scope === id ? 'oklch(22% 0.06 265)' : '#fff',
    color: scope === id ? '#fff' : 'oklch(36% 0.04 265)',
  });
  const dateLbl = { display:'block', fontSize:10, fontWeight:700, textTransform:'uppercase', letterSpacing:'0.06em', color:'oklch(56% 0.03 265)', marginBottom:4 };
  // Tiny per-row pill flagging one-time vs part of a recurring series.
  const typeTagBase = { flexShrink:0, fontSize:10, fontWeight:700, letterSpacing:'0.02em', padding:'2px 8px', borderRadius:20, whiteSpace:'nowrap' };
  const typeTagRecurring = { ...typeTagBase, background:'oklch(96% 0.05 75)',   color:'oklch(40% 0.13 75)',  border:'1px solid oklch(88% 0.08 75)' };
  const typeTagOnetime   = { ...typeTagBase, background:'oklch(96.5% 0.008 265)', color:'oklch(52% 0.03 265)', border:'1px solid oklch(91% 0.012 265)' };
  // Default a custom range to the last 30 days the first time it's opened, so
  // the picker starts on a sensible window instead of an unbounded "everything".
  const selectCustom = () => {
    setScope('custom');
    if (!customFrom && !customTo) {
      const from = new Date(today); from.setDate(from.getDate() - 30);
      setCustomFrom(window.Calendar.time.dateKey(from,  adminTz));
      setCustomTo(  window.Calendar.time.dateKey(today, adminTz));
    }
  };

  const approveResched = async (id) => {
    setBusyId(id);
    try { await data.approveReschedule(id); }
    catch (e) { alert(e.message); }
    finally { setBusyId(null); }
  };
  const declineResched = async (id) => {
    setBusyId(id);
    try { await data.declineReschedule(id); }
    catch (e) { alert(e.message); }
    finally { setBusyId(null); }
  };

  return (
    <div style={{ fontFamily:"'Plus Jakarta Sans', sans-serif" }}>
      {/* Timezone notice — every time on this page is rendered in this zone. */}
      <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:12, fontSize:12, color:'oklch(40% 0.04 265)' }} title={window.adminSchTz}>
        <span style={{ background:'oklch(96% 0.02 265)', border:'1px solid oklch(88% 0.02 265)', borderRadius:20, padding:'3px 10px', fontWeight:700, color:'oklch(28% 0.06 265)' }}>
          {window.adminSchTzAbbrev()} · {window.adminSchTz}
        </span>
        <span>All times below are in your browser's timezone.</span>
      </div>

      {/* Filter bar */}
      <div style={{ background:'#fff', border:'1px solid oklch(91% 0.01 265)', borderRadius:14, padding:'14px 16px', marginBottom:20 }}>
        <div style={{ display:'flex', gap:7, flexWrap:'wrap', marginBottom:12 }}>
          <button onClick={() => setScope('yesterday')} style={scopeChip('yesterday', 'Yesterday')}>Yesterday</button>
          <button onClick={() => setScope('today')}     style={scopeChip('today',     'Today')}>Today</button>
          <button onClick={() => setScope('tomorrow')}  style={scopeChip('tomorrow',  'Tomorrow')}>Tomorrow</button>
          <button onClick={() => setScope('week')}      style={scopeChip('week',      'This week')}>This week</button>
          <button onClick={() => setScope('lastweek')}  style={scopeChip('lastweek',  'Last week')}>Last week</button>
          <button onClick={() => setScope('nextweek')}  style={scopeChip('nextweek',  'Next week')}>Next week</button>
          <button onClick={() => setScope('month')}     style={scopeChip('month',     'This month')}>This month</button>
          <button onClick={() => setScope('upcoming')}  style={scopeChip('upcoming',  'Upcoming')}>Upcoming</button>
          <button onClick={() => setScope('past')}      style={scopeChip('past',      'Past')}>Past</button>
          <button onClick={() => setScope('all')}       style={scopeChip('all',       'All')}>All</button>
          <button onClick={selectCustom}                style={scopeChip('custom',    'Custom')}>Custom…</button>
        </div>

        {/* Custom from–to date range — revealed only when the Custom chip is active.
            Either side may be left blank to leave that end unbounded. */}
        {scope === 'custom' && (
          <div style={{ display:'flex', gap:10, flexWrap:'wrap', alignItems:'flex-end', marginBottom:12 }}>
            <div>
              <label style={dateLbl}>From</label>
              <input type="date" value={customFrom} max={customTo || undefined}
                onChange={e => setCustomFrom(e.target.value)} style={fld} />
            </div>
            <div>
              <label style={dateLbl}>To</label>
              <input type="date" value={customTo} min={customFrom || undefined}
                onChange={e => setCustomTo(e.target.value)} style={fld} />
            </div>
            {(customFrom || customTo) && (
              <button onClick={() => { setCustomFrom(''); setCustomTo(''); }}
                style={{ ...fld, cursor:'pointer', color:'oklch(40% 0.04 265)' }}>
                Clear dates
              </button>
            )}
          </div>
        )}
        <div style={{ display:'flex', gap:10, flexWrap:'wrap' }}>
          <select value={instructorId} onChange={e => setInstructorId(e.target.value)} style={fld}>
            <option value="">All instructors</option>
            {instructorOptions.map(i => <option key={i.id} value={i.id}>{i.name}</option>)}
          </select>
          <select value={studentId} onChange={e => setStudentId(e.target.value)} style={fld}>
            <option value="">All students</option>
            {students.map(s => <option key={s.id} value={s.id}>{s.name}</option>)}
          </select>
          <select value={statusFilter} onChange={e => setStatusFilter(e.target.value)} style={fld}>
            <option value="all">Any status</option>
            <option value="confirmed">Confirmed</option>
            <option value="completed">Completed</option>
            <option value="cancelled">Cancelled</option>
            <option value="reschedule">Reschedule pending</option>
          </select>
          <select value={paidFilter} onChange={e => setPaidFilter(e.target.value)} style={fld}>
            <option value="all">Any payment</option>
            <option value="paid">Paid</option>
            <option value="unpaid">Unpaid</option>
          </select>
          <select value={typeFilter} onChange={e => setTypeFilter(e.target.value)} style={fld}>
            <option value="all">Any type</option>
            <option value="onetime">One-time</option>
            <option value="recurring">Recurring</option>
          </select>
          {(instructorId || studentId || statusFilter !== 'all' || paidFilter !== 'all' || typeFilter !== 'all') && (
            <button onClick={() => { setInstructorId(''); setStudentId(''); setStatusFilter('all'); setPaidFilter('all'); setTypeFilter('all'); }}
              style={{ ...fld, cursor:'pointer', color:'oklch(40% 0.04 265)' }}>
              Clear filters
            </button>
          )}
        </div>
      </div>

      {/* Grouped list */}
      {groups.length === 0 ? (
        <div style={{ background:'#fff', border:'1px solid oklch(91% 0.01 265)', borderRadius:14, padding:'48px 24px', textAlign:'center', color:'oklch(58% 0.03 265)', fontSize:14 }}>
          No classes match these filters.
        </div>
      ) : (
        <div style={{ display:'flex', flexDirection:'column', gap:18 }}>
          {groups.map(([ymd, rows]) => (
            <div key={ymd}>
              <div style={{ fontSize:11, fontWeight:700, letterSpacing:'0.08em', color:'oklch(56% 0.03 265)', textTransform:'uppercase', marginBottom:9, padding:'0 4px' }}>
                {dayLabel(ymd)} · {rows.length} class{rows.length === 1 ? '' : 'es'}
              </div>
              <div style={{ background:'#fff', border:'1px solid oklch(91% 0.01 265)', borderRadius:12, overflow:'hidden' }}>
                {/* Minimal row: just "student with teacher". Click (or
                    Enter/Space) opens the detail drawer with the time,
                    duration, status, recurring flag, and every action. */}
                {rows.map((b, idx) => {
                  const teacher = instructors.find(i => i.id === b.instructorId);
                  const student = students.find(s => s.id === b.studentId);
                  const cancelled = b.status === 'cancelled';
                  const recurring = !!(b.seriesId || b.isRecurring);
                  const open = () => setDetailTarget(b);
                  return (
                    <div key={b.id} role="button" tabIndex={0}
                      onClick={open}
                      onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); open(); } }}
                      style={{
                        padding:'14px 16px',
                        borderTop: idx === 0 ? 'none' : '1px solid oklch(95% 0.005 60)',
                        display:'flex', alignItems:'center', justifyContent:'space-between', gap:12,
                        cursor:'pointer', opacity: cancelled ? 0.5 : 1,
                      }}>
                      <div style={{ minWidth:0, fontSize:14, fontWeight:700, color:'oklch(22% 0.06 265)', textDecoration: cancelled ? 'line-through' : 'none', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
                        {student?.name || '?'} <span style={{ color:'oklch(60% 0.03 265)', fontWeight:500 }}>with</span> {teacher?.name || '?'}
                      </div>
                      <div style={{ display:'flex', alignItems:'center', gap:10, flexShrink:0 }}>
                        <span title={recurring ? 'Part of a recurring series' : 'One-time class'}
                          style={recurring ? typeTagRecurring : typeTagOnetime}>
                          {recurring ? '↻ Recurring' : 'One-time'}
                        </span>
                        <span aria-hidden="true" style={{ color:'oklch(70% 0.03 265)', fontSize:20, lineHeight:1 }}>›</span>
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          ))}
        </div>
      )}

      {/* Detail drawer — opens when a row is clicked. Resolves the freshest
          copy of the booking by id so it reflects live data updates while
          open; the action callbacks close it and hand off to the existing
          series / delete / transcript modals or the reschedule helpers. */}
      {detailTarget && (() => {
        const b = allBookings.find(x => x.id === detailTarget.id) || detailTarget;
        const teacher = instructors.find(i => i.id === b.instructorId);
        const student = students.find(s => s.id === b.studentId);
        return (
          <window.AdminScheduleDetailDrawer
            booking={b}
            studentName={student?.name || '?'}
            teacherName={teacher?.name || '?'}
            liveEl={<window.AdminScheduleLive.RowStatus booking={b} presence={live.presence[b.id]} />}
            busy={busyId === b.id}
            onClose={() => setDetailTarget(null)}
            onReschedule={() => { setRescheduleTarget(b); setDetailTarget(null); }}
            onSeries={() => { setSeriesTarget(b); setDetailTarget(null); }}
            onDelete={() => { setDeleteTarget(b); setDetailTarget(null); }}
            onTranscript={() => { setTranscriptTarget(b); setDetailTarget(null); }}
            onApproveResched={async () => { await approveResched(b.id); setDetailTarget(null); }}
            onDeclineResched={async () => { await declineResched(b.id); setDetailTarget(null); }}
          />
        );
      })()}

      {seriesTarget && (
        <window.AdminSeriesActionsModal
          booking={seriesTarget}
          onClose={() => setSeriesTarget(null)}
        />
      )}
      {deleteTarget && (
        <window.AdminDeleteBookingModal
          booking={deleteTarget}
          onClose={() => setDeleteTarget(null)}
        />
      )}
      {transcriptTarget && (
        <window.AdminTranscriptModal
          booking={transcriptTarget}
          onClose={() => setTranscriptTarget(null)}
        />
      )}
      {rescheduleTarget && (
        <window.AdminRescheduleBookingModal
          booking={rescheduleTarget}
          onClose={() => setRescheduleTarget(null)}
        />
      )}
    </div>
  );
};

Object.assign(window, { AdminSchedule });
