// src/features/instructor/ui/instructor-schedule-calendar.jsx
//
// Weekly calendar view of the instructor's bookings + availability.
// One of the two "view" modes mounted by InstructorPortal (the other
// being InstructorScheduleList).
//
// Public: window.InstructorScheduleCalendar.
//
// Depends on (via window.*): AddLessonModal, AvailabilityModal,
// LessonDetailPopup (all in instructor-dashboard-modals.jsx).

const InstructorScheduleCalendar = ({ onBack }) => {
  const data = window.useDashboardData();
  const auth = window.useAuth();
  const [weekOffset,    setWeekOffset]    = React.useState(0);
  const [showAdd,       setShowAdd]       = React.useState(false);
  const [addDef,        setAddDef]        = React.useState({ date:'', hour:9 });
  const [showAvail,     setShowAvail]     = React.useState(false);
  const [showBlock,     setShowBlock]     = React.useState(false);
  const [activeBooking, setActiveBooking] = React.useState(null);
  const [toast,         setToast]         = React.useState('');

  const instructor = data.teacherProfile;
  const earnings   = data.earnings() || { earnedThisMonth: 0, earnedThisMonthUnsettled: 0, owedToPlatform: 0, pendingPayout: 0 };
  const availMap   = React.useMemo(() => availabilityToMockMap(data.teacherAvailability), [data.teacherAvailability]);
  const weekDates  = instWeekDates(weekOffset);
  const isToday    = (d) => sameLocalDay(d, INST_TODAY);

  // v88: load this instructor's block-time entries (availability_exceptions)
  // so the grid can render imported Google events as visible blocks. The
  // booking page (instructor-booking-page.jsx) already reads this table for
  // its slot-filter purpose; the calendar grid was previously blind to it.
  const [blocks, setBlocks] = React.useState([]);
  React.useEffect(() => {
    if (!instructor || !instructor.id) return;
    let cancelled = false;
    const load = async () => {
      try {
        const rows = await window.Calendar.db.loadExceptions({ instructorId: instructor.id });
        if (!cancelled) setBlocks(rows || []);
      } catch (_) {
        if (!cancelled) setBlocks([]);
      }
    };
    load();
    // Subscribe to realtime so cron-imported blocks appear without a refresh.
    let channel = null;
    if (window.supa && window.supa.channel) {
      channel = window.supa
        .channel('gcal-blocks-' + instructor.id)
        .on('postgres_changes', { event: '*', schema: 'public', table: 'availability_exceptions', filter: 'instructor_id=eq.' + instructor.id }, () => load())
        .subscribe();
    }
    return () => {
      cancelled = true;
      if (channel && window.supa && window.supa.removeChannel) {
        try { window.supa.removeChannel(channel); } catch (_) {}
      }
    };
  }, [instructor?.id]);

  // Find the block (if any) covering a given hour-cell. Hour-cells are
  // {date, h} pairs in the browser's local timezone; we intersect the
  // [start_at, end_at) range against the [cellStart, cellEnd) hour.
  const blockAt = React.useCallback((cellDate, h) => {
    if (!blocks || !blocks.length) return null;
    const cellStart = new Date(cellDate);
    cellStart.setHours(h, 0, 0, 0);
    const cellEnd = new Date(cellStart.getTime() + 60 * 60 * 1000);
    for (const b of blocks) {
      const s = new Date(b.start_at);
      const e = new Date(b.end_at);
      if (s < cellEnd && e > cellStart) return b;
    }
    return null;
  }, [blocks]);
  // Dynamic visible-hour range so teachers with night availability see those rows.
  const visibleHours = React.useMemo(
    () => computeVisibleHours(availMap, data.teacherBookings),
    [availMap, data.teacherBookings]
  );

  const showToast = msg => { setToast(msg); setTimeout(() => setToast(''), 2500); };

  const addLesson = async (form) => {
    await data.createBookingAsTeacher(form);
  };
  const addSeries = async (form) => {
    await window.supa.rpc('instructor_create_series', {
      p_student_id:  form.studentId,
      p_first_at:    form.firstAt,
      p_duration:    form.durationMinutes,
      p_weeks:       form.weeks,
      p_message:     form.message,
      p_infinite:    !!form.infinite,
    });
    await data.refresh();
  };

  const updateBooking = async (id, patch) => {
    const { data: row, error } = await window.supa.from('bookings').update(patch).eq('id', id)
      .select('*, student:profiles(*)').single();
    if (error) { alert(error.message); return; }
    // Refresh local cache
    await data.refresh();
    setActiveBooking(null);
  };

  // Find booking at a specific cell.
  const bookingAt = (cellDate, h) => data.teacherBookings.find(b => {
    if (b.status === 'cancelled') return false;
    const d = new Date(b.scheduledAt);
    return sameLocalDay(d, cellDate) && d.getHours() === h;
  });
  const isAvail = (di, h) => (availMap[di] || []).includes(h);

  const weekLabel = (() => {
    const s = weekDates[0], e = weekDates[6];
    return `${INST_MONTHS[s.getMonth()]} ${s.getDate()}–${s.getMonth() !== e.getMonth() ? INST_MONTHS[e.getMonth()]+' ' : ''}${e.getDate()}, ${s.getFullYear()}`;
  })();

  const btnStyle = (primary) => ({
    background: primary ? 'oklch(22% 0.06 265)' : 'oklch(96.5% 0.007 265)',
    color:      primary ? '#fff'                 : 'oklch(38% 0.04 265)',
    border:     primary ? 'none'                 : '1px solid oklch(90% 0.01 265)',
    borderRadius:8, padding:'7px 14px', fontSize:12, fontWeight: primary ? 600 : 500,
    cursor:'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif", whiteSpace:'nowrap',
  });

  return (
    <div style={{ display:'flex', flexDirection:'column', height:'100vh', fontFamily:"'Plus Jakarta Sans', sans-serif", background:'#fff', overflow:'hidden' }}>

      {/* ── Header ── */}
      <div style={{ display:'flex', alignItems:'center', gap:16, padding:'0 22px', height:54, borderBottom:'1px solid oklch(92% 0.01 265)', flexShrink:0 }}>
        <div style={{ fontFamily:"'Cormorant Garamond', serif", fontSize:17, fontWeight:700, letterSpacing:'0.12em', color:'oklch(22% 0.06 265)', flexShrink:0 }}>COACHING</div>
        <div style={{ flex:1 }} />

        <div style={{ display:'flex', alignItems:'center', gap:8 }}>
          <button onClick={() => setWeekOffset(w => w-1)} style={{ width:26, height:26, borderRadius:6, border:'1px solid oklch(90% 0.01 265)', background:'#fff', cursor:'pointer', fontSize:13, color:'oklch(44% 0.04 265)', display:'flex', alignItems:'center', justifyContent:'center' }}>‹</button>
          <span style={{ fontSize:12, fontWeight:600, color:'oklch(28% 0.05 265)', minWidth:168, textAlign:'center' }}>{weekLabel}</span>
          <button onClick={() => setWeekOffset(w => w+1)} style={{ width:26, height:26, borderRadius:6, border:'1px solid oklch(90% 0.01 265)', background:'#fff', cursor:'pointer', fontSize:13, color:'oklch(44% 0.04 265)', display:'flex', alignItems:'center', justifyContent:'center' }}>›</button>
          <button onClick={() => setWeekOffset(0)} style={{ fontSize:11, color:'oklch(48% 0.04 265)', background:'oklch(96% 0.01 265)', border:'none', borderRadius:5, padding:'3px 9px', cursor:'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif" }}>Today</button>
        </div>

        <div style={{ flex:1 }} />

        <div style={{ display:'flex', alignItems:'center', gap:7 }}>
          {(data.teacherAssignments || []).length > 0 && (
            <button onClick={() => { setAddDef({ date:localDateStr(weekDates[1]), hour:10 }); setShowAdd(true); }} style={btnStyle(true)}>+ Add</button>
          )}
          <button onClick={() => setShowAvail(true)} style={btnStyle(false)}>Availability</button>
          <button onClick={() => setShowBlock(true)} style={btnStyle(false)} title="Block off a specific date or time">Block time</button>
          <div style={{ width:1, height:22, background:'oklch(91% 0.01 265)', margin:'0 4px' }} />
          <div style={{ fontSize:12, fontWeight:600, color:'oklch(28% 0.05 265)' }}>{instructor?.full_name || '—'}</div>
          <button onClick={onBack} style={{ background:'none', border:'none', fontSize:12, color:'oklch(62% 0.03 265)', cursor:'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif" }}>Sign out</button>
        </div>
      </div>

      {/* ── Calendar grid ── */}
      <div style={{ flex:1, overflowY:'auto', overflowX:'auto' }}>
        <table style={{ borderCollapse:'collapse', width:'100%', minWidth:620, tableLayout:'fixed' }}>
          <thead style={{ position:'sticky', top:0, zIndex:2 }}>
            <tr style={{ background:'#fff' }}>
              <th style={{ width:44, borderBottom:'1px solid oklch(92% 0.01 265)', borderRight:'1px solid oklch(92% 0.01 265)' }} />
              {weekDates.map((d, di) => (
                <th key={di} style={{
                  padding:'8px 6px', textAlign:'center',
                  borderBottom:'1px solid oklch(92% 0.01 265)',
                  borderRight:'1px solid oklch(94% 0.01 265)',
                  background: isToday(d) ? 'oklch(98% 0.015 265)' : '#fff',
                  fontWeight: isToday(d) ? 700 : 400,
                }}>
                  <div style={{ fontSize:10, textTransform:'uppercase', letterSpacing:'0.08em', color:isToday(d)?'oklch(22% 0.06 265)':'oklch(58% 0.03 265)', marginBottom:1 }}>{INST_DAYS[di]}</div>
                  <div style={{ fontFamily:"'Cormorant Garamond', serif", fontSize:18, fontWeight:700, lineHeight:1, color:isToday(d)?'oklch(22% 0.06 265)':'oklch(40% 0.06 265)' }}>{d.getDate()}</div>
                </th>
              ))}
            </tr>
          </thead>
          <tbody>
            {visibleHours.map(h => (
              <tr key={h} style={{ height:48 }}>
                <td style={{ fontSize:10, color:'oklch(68% 0.02 265)', textAlign:'right', paddingRight:8, borderRight:'1px solid oklch(93% 0.01 265)', borderBottom:'1px solid oklch(95% 0.005 60)', verticalAlign:'top', paddingTop:5, width:44, flexShrink:0 }}>
                  {instH(h)}
                </td>
                {weekDates.map((d, di) => {
                  const b      = bookingAt(d, h);
                  const blk    = !b && blockAt(d, h);
                  const avail  = isAvail(di, h);
                  const isTr   = b && /trial/i.test(b.message || '');
                  const cellBg = b ? 'transparent'
                               : blk ? (blk.is_imported ? 'oklch(96% 0.025 230)' : 'oklch(96% 0.018 25)')
                               : avail  ? 'oklch(98.2% 0.04 150)'
                               : isToday(d) ? 'oklch(99.5% 0.004 265)'
                               : '#fff';
                  return (
                    <td key={di}
                      onClick={() => {
                        if (b) setActiveBooking(b);
                        else if (blk) { setShowBlock(true); }
                        else if (avail) { setAddDef({ date:localDateStr(d), hour:h }); setShowAdd(true); }
                      }}
                      style={{
                        padding:'3px 3px',
                        borderRight:'1px solid oklch(94.5% 0.005 60)',
                        borderBottom:'1px solid oklch(95.5% 0.005 60)',
                        background: cellBg,
                        cursor: b || blk || avail ? 'pointer' : 'default',
                        verticalAlign:'top',
                      }}
                    >
                      {b ? (
                        <div style={{
                          height:40, borderRadius:5, padding:'4px 7px',
                          background: isTr ? 'oklch(72% 0.17 80)' : 'oklch(22% 0.06 265)',
                          color:      isTr ? 'oklch(18% 0.06 265)' : '#fff',
                          display:'flex', flexDirection:'column', justifyContent:'center',
                          position:'relative',
                        }}>
                          <div style={{ fontSize:11, fontWeight:700, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{(b.student?.name || '?').split(' ')[0]}</div>
                          {b.rescheduleRequestedAt && (
                            <span title="Reschedule pending" style={{ position:'absolute', top:2, right:3, fontSize:10, background:'oklch(72% 0.17 80)', color:'oklch(18% 0.06 265)', borderRadius:8, padding:'0 4px', fontWeight:700 }}>↻</span>
                          )}
                          {(() => {
                            const resolved = window.Meetings?.db?.resolveForBooking?.(
                              { studentId: b.studentId, instructorId: b.instructorId, meetingLink: b.meetingLink },
                              data.teacherAssignments
                            );
                            if (!resolved) return null;
                            const joinable = window.Meetings.util.isJoinable(b.scheduledAt);
                            if (!joinable) return <span title="Meeting link set" style={{ position:'absolute', bottom:2, right:3, fontSize:9, color:'rgba(255,255,255,0.7)' }}>link</span>;
                            return (
                              <a href={resolved} target="_blank" rel="noopener noreferrer"
                                 onClick={e => e.stopPropagation()}
                                 title="Join meeting"
                                 style={{ position:'absolute', bottom:2, right:3, fontSize:10, fontWeight:700, background:'#fff', color:'oklch(22% 0.06 265)', borderRadius:6, padding:'1px 6px', textDecoration:'none' }}>
                                Join
                              </a>
                            );
                          })()}
                        </div>
                      ) : blk ? (
                        <div title={blk.is_imported ? 'From Google Calendar — click to manage' : 'Blocked — click to manage'}
                             style={{
                               height:40, borderRadius:5, padding:'4px 7px',
                               background: blk.is_imported ? 'oklch(88% 0.06 230)' : 'oklch(88% 0.05 25)',
                               color: blk.is_imported ? 'oklch(28% 0.12 235)' : 'oklch(34% 0.13 25)',
                               display:'flex', flexDirection:'column', justifyContent:'center',
                               fontWeight:600, fontSize:11,
                               overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap',
                             }}>
                          <div style={{ overflow:'hidden', textOverflow:'ellipsis' }}>
                            {blk.is_imported ? (blk.imported_title || 'Google event') : (blk.reason || 'Blocked')}
                          </div>
                          {blk.is_imported && (
                            <div style={{ fontSize:9, fontWeight:500, opacity:0.75, marginTop:1 }}>from Google</div>
                          )}
                        </div>
                      ) : avail ? (
                        <div style={{ height:40, display:'flex', alignItems:'center', justifyContent:'center' }}>
                          <span style={{ fontSize:18, color:'oklch(70% 0.09 150)', fontWeight:300 }}>+</span>
                        </div>
                      ) : null}
                    </td>
                  );
                })}
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      {/* ── Balance strip — teacher only sees what they earned this month ── */}
      <div style={{ borderTop:'1px solid oklch(92% 0.01 265)', padding:'11px 26px', display:'flex', gap:32, alignItems:'center', flexShrink:0, background:'#fff' }}>
        <div>
          <div style={{ fontSize:10, fontWeight:700, textTransform:'uppercase', letterSpacing:'0.08em', color:'oklch(62% 0.03 265)', marginBottom:2 }}>Earned this month</div>
          <div style={{ fontFamily:"'Cormorant Garamond', serif", fontSize:21, fontWeight:700, color:'oklch(22% 0.06 265)' }}>{instFmt$(earnings.earnedThisMonthUnsettled)}</div>
        </div>
      </div>

      {toast && (
        <div style={{ position:'fixed', bottom:26, left:'50%', transform:'translateX(-50%)', background:'oklch(18% 0.06 265)', color:'#fff', padding:'9px 20px', borderRadius:10, fontSize:13, fontWeight:600, zIndex:9999, boxShadow:'0 8px 24px rgba(0,0,0,0.2)' }}>
          {toast}
        </div>
      )}

      {showAdd && (
        <window.AddLessonModal
          assignments={data.teacherAssignments || []}
          defaultDate={addDef.date}
          defaultHour={addDef.hour}
          onAdd={addLesson}
          onAddSeries={addSeries}
          onClose={() => setShowAdd(false)}
        />
      )}
      {showAvail && (
        <window.AvailabilityModal
          initialAvail={availMap}
          onSave={(rows) => data.saveAvailability(rows)}
          onClose={() => setShowAvail(false)}
        />
      )}
      {showBlock && instructor && (
        <window.BlockTimeModal
          instructorId={instructor.id}
          ownerName={instructor.full_name}
          ownerTimezone={auth.profile?.timezone || 'America/New_York'}
          onClose={() => setShowBlock(false)}
        />
      )}
      {activeBooking && (
        <window.LessonDetailPopup
          booking={activeBooking}
          onMarkComplete={() => updateBooking(activeBooking.id, { status: 'completed' })}
          onCancel={() => updateBooking(activeBooking.id, { status: 'cancelled' })}
          onMarkPaid={() => updateBooking(activeBooking.id, { paid: true })}
          onApproveReschedule={async () => {
            try { await data.approveReschedule(activeBooking.id); showToast('Reschedule approved'); setActiveBooking(null); }
            catch (e) { alert(e.message); }
          }}
          onDeclineReschedule={async () => {
            try { await data.declineReschedule(activeBooking.id); showToast('Reschedule declined'); setActiveBooking(null); }
            catch (e) { alert(e.message); }
          }}
          onSaveNotes={async (id, notes, complete, noShow) => {
            await data.saveLessonNotes(id, notes, complete, noShow);
            if (noShow)        { showToast('Marked absent');         setActiveBooking(null); }
            else if (complete) { showToast('Lesson marked complete'); setActiveBooking(null); }
          }}
          onClose={() => setActiveBooking(null)}
        />
      )}
    </div>
  );
};

// ── Portal (top-level entry, wired to Supabase auth) ────────────────

window.InstructorScheduleCalendar = InstructorScheduleCalendar;
