// src/features/messages/ui/messages-page.jsx
//
// Page-takeover layout for /?page=messages.
//
// Layout:
//   - Desktop (>=720px wide): two-pane — ConversationList on the left,
//     ThreadView + Composer on the right. One back affordance in the top
//     bar ("← Dashboard") returns to the dashboard.
//   - Mobile (<720px wide): single-pane messenger pattern. Shows the
//     conversation list OR the active thread, never both. The top bar's
//     back button is context-aware:
//       * list view  → "← Dashboard" returns to dashboard
//       * thread view → "← Inbox" returns to the conversation list
//     No second back arrow inside the thread header — collapsing to one
//     keeps mobile clean and matches the messenger pattern.
//
// Auto-mounts the heartbeat + inbox subscription on mount.

const MP_MOBILE_BREAKPOINT = 720;

// Conversations prefetched (hover / focus / touch on the Messages nav button)
// so the page can paint the inbox instantly instead of flashing "Loading…".
// Returns null when nothing is cached/fresh, or the rows (possibly []).
const MP_cached = (auth) => {
  const uid = auth && auth.user && auth.user.id;
  if (!uid || !window.Messages?.db?.getCachedConversations) return null;
  return window.Messages.db.getCachedConversations(uid);
};

const MessagesPage = ({ onBack }) => {
  const auth = window.useAuth ? window.useAuth() : { user: null, profile: null, loading: true };
  const [conversations, setConversations] = React.useState(() => MP_cached(auth) || []);
  const [selectedId,    setSelectedId]    = React.useState(null);
  const [loading,       setLoading]       = React.useState(() => MP_cached(auth) === null);
  const [err,           setErr]           = React.useState('');
  // Reactive viewport width — window.innerWidth alone never triggers a
  // re-render, which is why the old conditional rendering was broken on
  // mobile (both panes painted at once, or neither toggled correctly when
  // rotating). Update on resize + orientationchange.
  const [vw, setVw] = React.useState(typeof window !== 'undefined' ? window.innerWidth : 1024);
  React.useEffect(() => {
    const onResize = () => setVw(window.innerWidth);
    window.addEventListener('resize', onResize);
    window.addEventListener('orientationchange', onResize);
    return () => {
      window.removeEventListener('resize', onResize);
      window.removeEventListener('orientationchange', onResize);
    };
  }, []);
  const isMobile = vw < MP_MOBILE_BREAKPOINT;
  const viewerUserId = auth.user?.id;

  const refreshList = React.useCallback(async () => {
    if (!viewerUserId) return;
    // Paint instantly from the prefetch cache, then refresh in the background.
    const cached = window.Messages.db.getCachedConversations
      ? window.Messages.db.getCachedConversations(viewerUserId) : null;
    if (cached) {
      setConversations(cached);
      setSelectedId(prev => {
        if (prev) return prev;
        if (window.innerWidth < MP_MOBILE_BREAKPOINT) return null;
        return cached[0]?.id || null;
      });
      setLoading(false);
    }
    try {
      const rows = await window.Messages.db.loadConversations(viewerUserId);
      setConversations(rows);
      // Auto-select first conversation on desktop only. On mobile we want
      // the inbox to be the landing view — the user picks a thread.
      setSelectedId(prev => {
        if (prev) return prev;
        if (window.innerWidth < MP_MOBILE_BREAKPOINT) return null;
        return rows[0]?.id || null;
      });
      setLoading(false);
    } catch (e) { setErr(e.message || String(e)); setLoading(false); }
  }, [viewerUserId]);

  React.useEffect(() => {
    if (auth.loading) return;
    if (!viewerUserId) { setLoading(false); return; }
    refreshList();
    const sub = window.Messages.db.subscribeToInbox(() => { refreshList(); });
    const stopHeartbeat = window.Messages.db.startHeartbeat();
    return () => { sub.unsubscribe(); stopHeartbeat(); };
  }, [auth.loading, viewerUserId, refreshList]);

  const selected = conversations.find(c => c.id === selectedId) || null;
  const peerLabel = (() => {
    if (!selected) return '';
    const peer = selected.peer === 'student' ? selected.student : selected.instructor;
    return peer?.full_name || (selected.peer === 'student' ? 'your student' : 'your coach');
  })();

  // What the single back button does depends on context. On mobile with a
  // thread open, "back" returns to the inbox list. Otherwise it returns to
  // the dashboard (delegated to the host via onBack).
  const onMobileThread = isMobile && !!selected;
  const handleBack = () => {
    if (onMobileThread) {
      setSelectedId(null);
      return;
    }
    if (onBack) onBack();
    else { try { window.location.search = ''; } catch (e) {} }
  };
  const backLabel = onMobileThread ? 'Inbox' : 'Dashboard';

  if (auth.loading || loading) {
    return (
      <div style={{ minHeight:'100vh', display:'flex', alignItems:'center', justifyContent:'center',
                     fontFamily:"'Plus Jakarta Sans', sans-serif", color:'oklch(56% 0.03 265)' }}>
        Loading…
      </div>
    );
  }
  if (!viewerUserId) {
    return (
      <div style={{ minHeight:'100vh', display:'flex', alignItems:'center', justifyContent:'center',
                     fontFamily:"'Plus Jakarta Sans', sans-serif", color:'oklch(40% 0.04 265)', padding:24, textAlign:'center' }}>
        Sign in to view your messages.
      </div>
    );
  }

  // Pane visibility on mobile: list xor thread. On desktop both.
  const showList   = !isMobile || !selected;
  const showThread = !isMobile || !!selected;

  return (
    <div style={{ minHeight:'100vh', height:'100vh', display:'flex', flexDirection:'column',
                   background:'oklch(98.5% 0.007 60)',
                   fontFamily:"'Plus Jakarta Sans', sans-serif" }}>
      {/* Top bar — single back affordance whose target depends on context. */}
      <div style={{ display:'flex', alignItems:'center', gap:14,
                     padding:'12px 20px', background:'#fff',
                     borderBottom:'1px solid oklch(92% 0.01 265)',
                     flex:'0 0 auto' }}>
        <button onClick={handleBack}
          aria-label={`Back to ${backLabel.toLowerCase()}`}
          style={{ background:'none', border:'none', cursor:'pointer',
                   fontSize:14, color:'oklch(40% 0.04 265)',
                   padding:'4px 6px', minHeight:32,
                   fontFamily:"'Plus Jakarta Sans', sans-serif" }}
        >← {backLabel}</button>
        <div style={{ flex:1, fontFamily:"'Cormorant Garamond', serif", fontSize:18, fontWeight:700,
                       letterSpacing:'0.14em', color:'oklch(22% 0.06 265)',
                       overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
          {onMobileThread ? peerLabel.toUpperCase() : 'MESSAGES'}
        </div>
        {onMobileThread && selected && window.MaskedCalls?.ui && (
          /* Student-side renders the Phase 2 "Request call" button (posts an in-thread
             message + standard email/push notification) so the foreign-teacher case
             (Lebanon: receiving-party-pays + foreign-number ignored) is avoided.
             Teacher-side renders the browser-audio CallButton (Phase 2 with PSTN fallback). */
          selected.student_id === viewerUserId
            ? (window.MaskedCalls.ui.RequestCallButton && (
                <window.MaskedCalls.ui.RequestCallButton conversation={selected} viewerUserId={viewerUserId} />
              ))
            : (window.MaskedCalls.ui.CallButton && (
                <window.MaskedCalls.ui.CallButton
                  conversation={selected}
                  viewerUserId={viewerUserId}
                  instructorId={selected.instructor?.id || window.DASHBOARD_DATA?.teacherProfile?.id || null}
                />
              ))
        )}
      </div>

      {err && (
        <div style={{ padding:12, background:'oklch(96% 0.06 25)',
                       color:'oklch(40% 0.15 25)', fontSize:13, textAlign:'center',
                       flex:'0 0 auto' }}>{err}</div>
      )}

      <div style={{ flex:1, display:'flex', minHeight:0, minWidth:0 }}>
        {/* Conversation list — fixed-width on desktop, full-width on mobile. */}
        {showList && (
          <div style={{
            width: isMobile ? '100%' : 320,
            borderRight: isMobile ? 'none' : '1px solid oklch(92% 0.01 265)',
            background:'#fff', minWidth:0,
            display:'flex', flexDirection:'column',
          }}>
            <window.ConversationList
              conversations={conversations}
              selectedId={selectedId}
              onSelect={setSelectedId}
              viewerUserId={viewerUserId}
            />
          </div>
        )}

        {/* Right pane: thread + composer. */}
        {showThread && (
          <div style={{ flex:1, display:'flex', flexDirection:'column',
                         minWidth:0, minHeight:0, background:'#fff' }}>
            {!selected ? (
              <div style={{ flex:1, display:'flex', alignItems:'center', justifyContent:'center',
                             color:'oklch(56% 0.03 265)', fontSize:14, padding:24, textAlign:'center' }}>
                {conversations.length === 0
                  ? 'You have no conversations yet. An admin assigns students to teachers, which creates a chat thread automatically.'
                  : 'Pick a conversation on the left.'}
              </div>
            ) : (
              <React.Fragment>
                {/* On desktop, show the peer's name as a thread header. On
                    mobile the top bar already shows it, so we skip the
                    duplicate header entirely — that's what kills the
                    double back-arrow that used to stack here. */}
                {!isMobile && (
                  <div style={{ padding:'12px 18px',
                                 borderBottom:'1px solid oklch(92% 0.01 265)',
                                 display:'flex', alignItems:'center', gap:10,
                                 flex:'0 0 auto' }}>
                    <div style={{ fontSize:15, fontWeight:600,
                                   color:'oklch(22% 0.06 265)', flex:1, minWidth:0,
                                   overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
                      {peerLabel}
                    </div>
                    {window.MaskedCalls?.ui && (
                      /* Student-side renders Phase 2 "Request call" (in-thread message + the
                         standard new-message email/push); teacher-side renders the browser-audio
                         CallButton (Twilio Voice SDK with PSTN fallback). The split solves the
                         Lebanon-teacher receiving-party-pays + foreign-number-ignored problem. */
                      selected.student_id === viewerUserId
                        ? (window.MaskedCalls.ui.RequestCallButton && (
                            <window.MaskedCalls.ui.RequestCallButton conversation={selected} viewerUserId={viewerUserId} />
                          ))
                        : (window.MaskedCalls.ui.CallButton && (
                            <window.MaskedCalls.ui.CallButton
                              conversation={selected}
                              viewerUserId={viewerUserId}
                              instructorId={selected.instructor?.id || window.DASHBOARD_DATA?.teacherProfile?.id || null}
                            />
                          ))
                    )}
                  </div>
                )}
                <window.ThreadView
                  conversationId={selected.id}
                  viewerUserId={viewerUserId}
                  peerLabel={peerLabel}
                />
                <window.Composer
                  conversationId={selected.id}
                  onSent={refreshList}
                />
              </React.Fragment>
            )}
          </div>
        )}
      </div>
    </div>
  );
};

window.Messages.ui.MessagesPage = MessagesPage;
window.MessagesPage             = MessagesPage;
