const { useState, useEffect, useMemo, useRef } = React;

const CFG = window.FEED_CONFIG;
const HEADERS = {
  apikey: CFG.supabaseAnonKey,
  Authorization: 'Bearer ' + CFG.supabaseAnonKey,
  'Content-Type': 'application/json',
  Prefer: 'return=representation',
};

// ─────────────────────────────────────────────
// API helpers
// ─────────────────────────────────────────────
async function api(path, opts) {
  const res = await fetch(CFG.supabaseUrl + '/rest/v1' + path, Object.assign({ headers: HEADERS }, opts || {}));
  const text = await res.text();
  let body = null;
  try { body = text ? JSON.parse(text) : null; } catch (e) { body = text; }
  if (!res.ok) throw new Error((body && body.message) || text || ('HTTP ' + res.status));
  return body;
}

const Comments = {
  list: () => api('/design_comments?order=created_at.desc'),
  create: (row) => api('/design_comments', { method: 'POST', body: JSON.stringify(row) }),
  update: (id, patch) => api('/design_comments?id=eq.' + id, { method: 'PATCH', body: JSON.stringify(patch) }),
  remove: (id) => api('/design_comments?id=eq.' + id, { method: 'DELETE' }),
};

// ─────────────────────────────────────────────
// Author name (persisted in localStorage)
// ─────────────────────────────────────────────
const AUTHOR_KEY = 'feed_review_author';
function loadAuthor() { try { return localStorage.getItem(AUTHOR_KEY) || ''; } catch { return ''; } }
function saveAuthor(name) { try { localStorage.setItem(AUTHOR_KEY, name); } catch {} }

// ─────────────────────────────────────────────
// PAGE REGISTRY — single source of truth for what's reviewable.
// To add a new page (e.g. an A/B variant):
//   1. Add a row here with a unique `id`, a `label`, and the `src` path
//   2. (Optional) `group` to organize variants under a heading in the dropdown
//   3. (Optional) `description` shows as a subtle line under the label in the menu
// Comments are scoped per `id`, so each variant has its own thread.
// ─────────────────────────────────────────────
const PAGES = [
  { id: 'landing',  label: 'Registration',      src: '/landing.html',
    group: 'Registration', description: 'Main landing page (ICP-1: monetization-stuck newsletter operator)' },
  { id: 'landing-agency', label: 'Registration · Agency', src: '/landing-agency.html',
    group: 'Registration', description: 'ICP-2 variant (agency owners, coaches, consultants, low ROAS)' },
  { id: 'post_reg', label: 'Post-Registration', src: '/post-registration.html',
    group: 'Post-Registration', description: 'Thank-you + video page' },
];

const VIEWPORTS = [
  { id: 'desktop', label: 'Desktop', icon: <DesktopIcon /> },
  { id: 'mobile',  label: 'Mobile',  icon: <MobileIcon /> },
];

function App() {
  const [author, setAuthorState] = useState(loadAuthor());
  const [page, setPage] = useState('landing');
  const [viewport, setViewport] = useState('desktop');
  const [commentMode, setCommentMode] = useState(false);
  const [comments, setComments] = useState([]);
  const [loading, setLoading] = useState(true);
  const [activeCommentId, setActiveCommentId] = useState(null);
  const [draftPosition, setDraftPosition] = useState(null); // {x, y} in iframe content coords
  const [sidebarOpen, setSidebarOpen] = useState(true);
  // Bumping this timestamp forces the iframe to reload its content with a fresh URL
  // (cache-busting via querystring). The "Refresh" button in the top bar bumps it.
  const [lastRefresh, setLastRefresh] = useState(Date.now());

  function setAuthor(name) {
    saveAuthor(name);
    setAuthorState(name);
  }

  async function refresh() {
    try {
      const rows = await Comments.list();
      setComments(rows);
    } catch (e) {
      console.error('refresh failed:', e);
    } finally {
      setLoading(false);
    }
  }

  // Initial load + Realtime
  useEffect(() => {
    refresh();
    if (!window.supabase || !window.supabase.createClient) return;
    const client = window.supabase.createClient(CFG.supabaseUrl, CFG.supabaseAnonKey);
    const channel = client.channel('design-review-' + Math.random().toString(36).slice(2, 8))
      .on('postgres_changes',
        { event: '*', schema: 'public', table: 'design_comments' },
        () => refresh())
      .subscribe();
    return () => { try { client.removeChannel(channel); } catch (e) {} };
  }, []);

  // Visible comments for the current page+viewport
  const visibleComments = useMemo(
    () => comments.filter(c => c.page === page && c.viewport === viewport),
    [comments, page, viewport]
  );

  // Assign each comment a stable chronological number (1 = oldest in this view).
  // The display list can be sorted however we want — the number stays anchored to creation order.
  const commentNumbers = useMemo(() => {
    const byOldest = [...visibleComments].sort(
      (a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
    );
    const map = {};
    byOldest.forEach((c, i) => { map[c.id] = i + 1; });
    return map;
  }, [visibleComments]);

  function handleIframeClick({ x, y }) {
    if (!commentMode) return;
    setDraftPosition({ x, y });
    setActiveCommentId(null);
  }

  async function saveDraft(text) {
    if (!draftPosition || !text.trim()) {
      setDraftPosition(null);
      return;
    }
    try {
      await Comments.create({
        page, viewport,
        x: Math.round(draftPosition.x),
        y: Math.round(draftPosition.y),
        comment: text.trim(),
        author: author || 'Anonymous',
      });
    } catch (e) {
      alert('Failed to save: ' + e.message);
    } finally {
      setDraftPosition(null);
      setCommentMode(false); // exit comment mode after placing one
    }
  }

  async function deleteComment(id) {
    if (!confirm('Delete this note?')) return;
    try { await Comments.remove(id); }
    catch (e) { alert('Delete failed: ' + e.message); }
  }

  async function toggleResolved(c) {
    try {
      if (c.status === 'resolved') {
        // Reopen — clear resolver attribution
        await Comments.update(c.id, {
          status: 'open',
          resolved_by: null,
          resolved_at: null,
        });
      } else {
        // Resolved by designer — stamp who + when
        await Comments.update(c.id, {
          status: 'resolved',
          resolved_by: author || 'Designer',
          resolved_at: new Date().toISOString(),
        });
      }
    } catch (e) { alert('Update failed: ' + e.message); }
  }

  // Name prompt if first visit
  if (!author) {
    return <AuthorPrompt onSave={setAuthor} />;
  }

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}>
      <TopBar
        page={page} setPage={setPage}
        viewport={viewport} setViewport={setViewport}
        commentMode={commentMode} setCommentMode={setCommentMode}
        author={author} onChangeAuthor={() => setAuthor('')}
        commentCount={visibleComments.length}
        openCount={visibleComments.filter(c => c.status === 'open').length}
        sidebarOpen={sidebarOpen}
        onToggleSidebar={() => setSidebarOpen(s => !s)}
        lastRefresh={lastRefresh}
        onRefresh={() => setLastRefresh(Date.now())}
      />

      <div style={{ flex: 1, display: 'flex', minHeight: 0 }}>
        <PageViewer
          key={page + viewport /* remount on switch to reset scroll */}
          page={page}
          viewport={viewport}
          commentMode={commentMode}
          comments={visibleComments}
          commentNumbers={commentNumbers}
          activeCommentId={activeCommentId}
          setActiveCommentId={setActiveCommentId}
          draftPosition={draftPosition}
          onIframeClick={handleIframeClick}
          onSaveDraft={saveDraft}
          onCancelDraft={() => setDraftPosition(null)}
          lastRefresh={lastRefresh}
        />

        {sidebarOpen && (
          <Sidebar
            comments={visibleComments}
            commentNumbers={commentNumbers}
            activeCommentId={activeCommentId}
            setActiveCommentId={setActiveCommentId}
            onDelete={deleteComment}
            onToggleResolved={toggleResolved}
            page={page}
            viewport={viewport}
          />
        )}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────
// Top bar
// ─────────────────────────────────────────────
function TopBar({ page, setPage, viewport, setViewport, commentMode, setCommentMode, author, onChangeAuthor, commentCount, openCount, sidebarOpen, onToggleSidebar, lastRefresh, onRefresh }) {
  return (
    <header style={{
      background: '#000',
      borderBottom: '1px solid var(--line)',
      padding: '12px 18px',
      display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap',
      flexShrink: 0, zIndex: 100,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
        <span className="mono" style={{
          fontSize: 10, letterSpacing: '0.22em', textTransform: 'uppercase',
          color: 'var(--accent)', fontWeight: 700,
          padding: '4px 9px', border: '1px solid var(--accent)', borderRadius: 3
        }}>REVIEW</span>
        <span style={{
          fontWeight: 800, fontSize: 17, letterSpacing: '-0.035em', lineHeight: 1
        }}>The Feed Media</span>
      </div>

      <div style={{ width: 1, height: 22, background: 'var(--line)' }} />

      {/* Page picker dropdown — scales as we add A/B variants */}
      <PagePicker pages={PAGES} value={page} onChange={setPage} />

      {/* Refresh — pulls the latest deployed version of the iframe */}
      <RefreshButton lastRefresh={lastRefresh} onRefresh={onRefresh} />

      {/* Viewport toggle */}
      <div style={{ display: 'inline-flex', gap: 2, padding: 3, background: 'var(--bg-3)', borderRadius: 7 }}>
        {VIEWPORTS.map(v => (
          <Pill key={v.id} active={viewport === v.id} onClick={() => setViewport(v.id)} icon={v.icon}>{v.label}</Pill>
        ))}
      </div>

      <div style={{ width: 1, height: 22, background: 'var(--line)' }} />

      {/* + Add comment */}
      <button
        onClick={() => setCommentMode(m => !m)}
        style={{
          display: 'inline-flex', alignItems: 'center', gap: 8,
          padding: '8px 14px',
          background: commentMode ? 'var(--accent)' : 'transparent',
          color: commentMode ? '#fff' : 'var(--ink-2)',
          border: '1px solid ' + (commentMode ? 'var(--accent)' : 'var(--line-2)'),
          borderRadius: 6,
          fontSize: 13, fontWeight: 600,
          cursor: 'pointer',
        }}
      >
        {commentMode ? '✕ Click a spot to comment' : '+ Add comment'}
      </button>

      <span className="mono" style={{ fontSize: 11, letterSpacing: '0.16em', color: 'var(--muted)' }}>
        {commentCount} {commentCount === 1 ? 'note' : 'notes'}
        {openCount > 0 && <span> · <span style={{ color: '#fbbf24' }}>{openCount} open</span></span>}
        {(commentCount - openCount) > 0 && <span> · <span style={{ color: '#00ff85' }}>{commentCount - openCount} resolved</span></span>}
      </span>

      <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 10 }}>
        <button
          onClick={onToggleSidebar}
          title="Toggle sidebar"
          style={{
            display: 'inline-flex', alignItems: 'center', gap: 6,
            padding: '6px 10px',
            background: 'transparent',
            color: 'var(--muted)',
            border: '1px solid var(--line-2)',
            borderRadius: 5,
            fontSize: 12,
            cursor: 'pointer'
          }}
        >
          {sidebarOpen ? 'Hide list' : 'Show list'}
        </button>
        <button
          onClick={onChangeAuthor}
          title="Change name"
          style={{
            display: 'inline-flex', alignItems: 'center', gap: 6,
            padding: '6px 10px',
            background: 'var(--bg-3)',
            color: 'var(--ink-2)',
            border: '1px solid var(--line-2)',
            borderRadius: 5,
            fontSize: 12,
            cursor: 'pointer'
          }}
        >
          <span className="mono" style={{ fontSize: 10, letterSpacing: '0.18em', color: 'var(--muted)' }}>YOU</span>
          {author}
        </button>
        <button
          onClick={() => window.FeedAuth && window.FeedAuth.signOut()}
          title="Sign out"
          style={{
            display: 'inline-flex', alignItems: 'center', gap: 6,
            padding: '6px 10px',
            background: 'transparent',
            color: 'var(--muted)',
            border: '1px solid var(--line-2)',
            borderRadius: 5,
            fontSize: 12, fontFamily: 'inherit',
            cursor: 'pointer'
          }}
        >Sign out</button>
      </div>
    </header>
  );
}

function Pill({ active, onClick, icon, children }) {
  return (
    <button
      onClick={onClick}
      style={{
        display: 'inline-flex', alignItems: 'center', gap: 6,
        padding: '6px 12px',
        background: active ? 'var(--accent)' : 'transparent',
        color: active ? '#fff' : 'var(--ink-2)',
        border: 'none',
        borderRadius: 5,
        fontSize: 12, fontWeight: active ? 600 : 500,
        cursor: 'pointer'
      }}
    >
      {icon}{children}
    </button>
  );
}

function DesktopIcon() {
  return <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>;
}

// ─────────────────────────────────────────────
// PagePicker — dropdown menu for selecting which page to review.
// Designed to scale to many pages (A/B variants, future pages) by grouping.
// ─────────────────────────────────────────────
function PagePicker({ pages, value, onChange }) {
  const [open, setOpen] = useState(false);
  const wrapRef = useRef(null);
  const current = pages.find(p => p.id === value) || pages[0];

  // Close on outside click
  useEffect(() => {
    if (!open) return;
    function onDocClick(e) {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
    }
    function onKey(e) { if (e.key === 'Escape') setOpen(false); }
    document.addEventListener('mousedown', onDocClick);
    document.addEventListener('keydown', onKey);
    return () => {
      document.removeEventListener('mousedown', onDocClick);
      document.removeEventListener('keydown', onKey);
    };
  }, [open]);

  // Group pages by group field (falls back to flat list)
  const grouped = useMemo(() => {
    const out = {};
    pages.forEach(p => {
      const key = p.group || 'Pages';
      if (!out[key]) out[key] = [];
      out[key].push(p);
    });
    return Object.entries(out); // [[groupName, pages[]], ...]
  }, [pages]);

  return (
    <div ref={wrapRef} style={{ position: 'relative' }}>
      <button
        onClick={() => setOpen(o => !o)}
        style={{
          display: 'inline-flex', alignItems: 'center', gap: 8,
          padding: '7px 12px',
          background: 'var(--bg-3)',
          border: '1px solid var(--line-2)',
          borderRadius: 7,
          color: 'var(--ink)',
          fontSize: 13, fontWeight: 600,
          cursor: 'pointer', fontFamily: 'inherit',
          minWidth: 180, justifyContent: 'space-between',
        }}
      >
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
          <span className="mono" style={{ fontSize: 9, letterSpacing: '0.18em', color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase' }}>
            Page
          </span>
          <span>{current.label}</span>
        </span>
        <span style={{ color: 'var(--muted)', fontSize: 10, transform: open ? 'rotate(180deg)' : 'none', transition: 'transform 140ms' }}>▾</span>
      </button>

      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 6px)', left: 0,
          minWidth: 280,
          background: '#111',
          border: '1px solid var(--line-2)',
          borderRadius: 8,
          boxShadow: '0 20px 60px rgba(0,0,0,0.6)',
          padding: 6, zIndex: 1000,
        }}>
          {grouped.map(([groupName, groupPages], gi) => (
            <div key={groupName} style={{ marginTop: gi > 0 ? 6 : 0 }}>
              {grouped.length > 1 && (
                <div className="mono" style={{
                  fontSize: 9, letterSpacing: '0.22em', textTransform: 'uppercase',
                  color: 'var(--muted)', fontWeight: 600,
                  padding: '8px 10px 4px',
                }}>{groupName}</div>
              )}
              {groupPages.map(p => {
                const active = p.id === value;
                return (
                  <button
                    key={p.id}
                    onClick={() => { onChange(p.id); setOpen(false); }}
                    style={{
                      width: '100%',
                      display: 'flex', alignItems: 'center', gap: 10,
                      padding: '8px 10px',
                      background: active ? 'rgba(168,85,247,0.10)' : 'transparent',
                      border: 'none',
                      borderRadius: 5,
                      color: active ? 'var(--accent)' : 'var(--ink)',
                      fontSize: 13, fontWeight: active ? 600 : 500,
                      textAlign: 'left',
                      cursor: 'pointer', fontFamily: 'inherit',
                      transition: 'background 100ms',
                    }}
                    onMouseEnter={e => { if (!active) e.currentTarget.style.background = 'rgba(255,255,255,0.04)'; }}
                    onMouseLeave={e => { if (!active) e.currentTarget.style.background = 'transparent'; }}
                  >
                    <span style={{
                      width: 6, height: 6, borderRadius: '50%',
                      background: active ? 'var(--accent)' : 'var(--line-2)',
                      flexShrink: 0,
                    }} />
                    <span style={{ flex: 1, minWidth: 0 }}>
                      <div>{p.label}</div>
                      {p.description && (
                        <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 2, fontWeight: 400 }}>
                          {p.description}
                        </div>
                      )}
                    </span>
                  </button>
                );
              })}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}
function MobileIcon() {
  return <svg width="11" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="5" y="2" width="14" height="20" rx="2"/><circle cx="12" cy="18" r="1" fill="currentColor" stroke="none"/></svg>;
}

// Refresh button — forces the iframe to fetch the latest version (cache-bust).
// Shows a ticking "last refreshed Xs ago" so the reviewer knows the freshness.
function RefreshButton({ lastRefresh, onRefresh }) {
  const [now, setNow] = useState(Date.now());
  const [spinning, setSpinning] = useState(false);
  useEffect(() => {
    const t = setInterval(() => setNow(Date.now()), 5000);
    return () => clearInterval(t);
  }, []);
  function handleClick() {
    setSpinning(true);
    setTimeout(() => setSpinning(false), 600);
    onRefresh();
  }
  const secs = Math.floor((now - lastRefresh) / 1000);
  let freshness;
  if (secs < 5)        freshness = 'just now';
  else if (secs < 60)  freshness = secs + 's ago';
  else if (secs < 3600) freshness = Math.floor(secs / 60) + 'm ago';
  else                  freshness = Math.floor(secs / 3600) + 'h ago';
  return (
    <button
      onClick={handleClick}
      title="Pull the latest version of the page"
      style={{
        display: 'inline-flex', alignItems: 'center', gap: 7,
        padding: '7px 12px',
        background: 'var(--bg-3)',
        border: '1px solid var(--line-2)',
        borderRadius: 7,
        color: 'var(--ink-2)',
        fontSize: 12,
        cursor: 'pointer', fontFamily: 'inherit',
      }}
    >
      <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
        style={{ transform: spinning ? 'rotate(360deg)' : 'rotate(0deg)', transition: 'transform 600ms ease-out' }}>
        <polyline points="23 4 23 10 17 10"/>
        <polyline points="1 20 1 14 7 14"/>
        <path d="M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"/>
      </svg>
      <span>Refresh</span>
      <span className="mono" style={{ fontSize: 10, color: 'var(--muted)', letterSpacing: '0.06em' }}>
        · {freshness}
      </span>
    </button>
  );
}

// ─────────────────────────────────────────────
// Iframe viewer + click-capture overlay + markers
// ─────────────────────────────────────────────
function PageViewer({ page, viewport, commentMode, comments, commentNumbers, activeCommentId, setActiveCommentId, draftPosition, onIframeClick, onSaveDraft, onCancelDraft, lastRefresh }) {
  const iframeRef = useRef(null);
  const overlayRef = useRef(null);
  const [scrollVersion, setScrollVersion] = useState(0); // forces marker reposition on iframe scroll

  // Listen to iframe scroll → bump version so markers re-render
  useEffect(() => {
    const iframe = iframeRef.current;
    if (!iframe) return;
    function onLoad() {
      try {
        iframe.contentWindow.addEventListener('scroll', onScroll, { passive: true });
      } catch (e) {}
    }
    function onScroll() { setScrollVersion(v => v + 1); }
    iframe.addEventListener('load', onLoad);
    // If already loaded, attach immediately
    try { iframe.contentWindow.addEventListener('scroll', onScroll, { passive: true }); } catch {}
    return () => {
      iframe.removeEventListener('load', onLoad);
      try { iframe.contentWindow.removeEventListener('scroll', onScroll); } catch {}
    };
  }, [page, viewport]);

  function handleOverlayClick(e) {
    if (!commentMode) return;
    // Only react when the click hit the overlay itself, not a child
    // (e.g. an existing marker or the new-comment popover)
    if (e.target !== e.currentTarget) return;
    const iframe = iframeRef.current;
    if (!iframe) return;
    const iframeRect = iframe.getBoundingClientRect();
    const scrollX = (iframe.contentWindow && iframe.contentWindow.scrollX) || 0;
    const scrollY = (iframe.contentWindow && iframe.contentWindow.scrollY) || 0;
    const x = (e.clientX - iframeRect.left) + scrollX;
    const y = (e.clientY - iframeRect.top) + scrollY;
    onIframeClick({ x, y });
  }

  const isMobile = viewport === 'mobile';
  const pageSrc = (PAGES.find(p => p.id === page) || PAGES[0]).src;

  return (
    <div style={{
      flex: 1, position: 'relative', overflow: 'auto',
      background: isMobile ? '#1a1a1a' : '#0a0a0a',
      display: 'flex', justifyContent: 'center', alignItems: isMobile ? 'flex-start' : 'stretch',
      padding: isMobile ? '24px 0 32px' : 0,
    }}
    // crosshair cursor only while actively waiting for the next click — not when popover is open
    className={(commentMode && !draftPosition) ? 'comment-mode' : ''}
    >
      <div style={{
        position: 'relative',
        width: isMobile ? 390 : '100%',
        height: isMobile ? 844 : '100%',
        flexShrink: 0,
        boxShadow: isMobile ? '0 0 0 1px #333, 0 32px 80px rgba(0,0,0,0.7)' : 'none',
        borderRadius: isMobile ? 16 : 0,
        overflow: 'hidden',
      }}>
        <iframe
          ref={iframeRef}
          // Append the refresh timestamp as a querystring to bust browser cache.
          // Each click of the Refresh button bumps lastRefresh → iframe reloads with new URL.
          src={pageSrc + '?v=' + lastRefresh}
          style={{
            width: '100%', height: '100%', border: 'none',
            display: 'block',
            pointerEvents: commentMode ? 'none' : 'auto',
          }}
        />
        <div
          ref={overlayRef}
          className="iframe-overlay"
          onClick={handleOverlayClick}
          style={{
            position: 'absolute', inset: 0,
            // Only capture clicks while waiting for the next new-comment placement.
            // Once a draft is open, the overlay stops absorbing clicks so the popover
            // (which is a child with pointer-events: auto) owns the interaction.
            pointerEvents: (commentMode && !draftPosition) ? 'auto' : 'none',
          }}
        >
          {/* Markers — re-render on scrollVersion change */}
          {comments.map((c) => (
            <CommentMarker
              key={c.id}
              comment={c}
              index={commentNumbers[c.id] || '?'}
              iframeRef={iframeRef}
              active={activeCommentId === c.id}
              scrollVersion={scrollVersion}
              onClick={() => setActiveCommentId(activeCommentId === c.id ? null : c.id)}
            />
          ))}
          {/* Draft (new) comment popover */}
          {draftPosition && (
            <DraftPopover
              x={draftPosition.x}
              y={draftPosition.y}
              iframeRef={iframeRef}
              onSave={onSaveDraft}
              onCancel={onCancelDraft}
              scrollVersion={scrollVersion}
            />
          )}
        </div>
      </div>
    </div>
  );
}

// Position a marker visually, accounting for iframe scroll.
function CommentMarker({ comment, index, iframeRef, active, onClick }) {
  const iframe = iframeRef.current;
  if (!iframe) return null;
  const scrollX = (iframe.contentWindow && iframe.contentWindow.scrollX) || 0;
  const scrollY = (iframe.contentWindow && iframe.contentWindow.scrollY) || 0;
  const left = comment.x - scrollX;
  const top  = comment.y - scrollY;
  const resolved = comment.status === 'resolved';

  return (
    <div
      onClick={(e) => { e.stopPropagation(); onClick(); }}
      style={{
        position: 'absolute',
        left: left - 14, top: top - 14,
        width: 28, height: 28,
        borderRadius: '50% 50% 50% 4px',
        transform: 'rotate(-45deg)',
        background: resolved ? '#525252' : 'var(--accent)',
        boxShadow: active ? '0 0 0 3px rgba(168,85,247,0.4), 0 4px 12px rgba(0,0,0,0.5)' : '0 2px 8px rgba(0,0,0,0.6)',
        border: '2px solid #0a0a0a',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        cursor: 'pointer',
        pointerEvents: 'auto',
        transition: 'box-shadow 140ms',
      }}
      title={(comment.author ? comment.author + ' · ' : '') + formatFullDateTime(comment.created_at) + '\n\n' + comment.comment}
    >
      <span style={{
        transform: 'rotate(45deg)',
        color: '#fff', fontSize: 11, fontWeight: 700, fontFamily: 'Geist, sans-serif'
      }}>{index}</span>
    </div>
  );
}

function DraftPopover({ x, y, iframeRef, onSave, onCancel }) {
  const [text, setText] = useState('');
  const taRef = useRef(null);

  useEffect(() => { taRef.current && taRef.current.focus(); }, []);

  const iframe = iframeRef.current;
  if (!iframe) return null;
  const scrollX = (iframe.contentWindow && iframe.contentWindow.scrollX) || 0;
  const scrollY = (iframe.contentWindow && iframe.contentWindow.scrollY) || 0;
  const overlayRect = iframe.getBoundingClientRect();
  // Render popover next to the click, but keep it visible if near edges
  const popLeft = Math.min(x - scrollX + 20, overlayRect.width - 320);
  const popTop  = Math.max(8, y - scrollY - 20);

  function handleKey(e) {
    if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) { e.preventDefault(); onSave(text); }
    if (e.key === 'Escape') onCancel();
  }

  return (
    <>
      {/* The marker dot for the draft */}
      <div style={{
        position: 'absolute',
        left: (x - scrollX) - 7, top: (y - scrollY) - 7,
        width: 14, height: 14,
        borderRadius: '50%',
        background: 'var(--accent)',
        border: '2px solid #fff',
        boxShadow: '0 0 0 2px var(--accent)',
        pointerEvents: 'none',
      }} />
      <div
        onClick={(e) => e.stopPropagation()}
        onMouseDown={(e) => e.stopPropagation()}
        style={{
        position: 'absolute', left: popLeft, top: popTop,
        width: 300,
        background: '#161616',
        border: '1px solid var(--accent)',
        borderRadius: 10,
        padding: 12,
        boxShadow: '0 20px 60px -10px rgba(0,0,0,0.7)',
        pointerEvents: 'auto',
        zIndex: 999,
      }}>
        <div className="mono" style={{ fontSize: 9, letterSpacing: '0.22em', textTransform: 'uppercase', color: 'var(--accent)', fontWeight: 700, marginBottom: 8 }}>
          New note
        </div>
        <textarea
          ref={taRef}
          value={text}
          onChange={e => setText(e.target.value)}
          onKeyDown={handleKey}
          placeholder="Type your note…"
          style={{
            width: '100%', minHeight: 90,
            padding: '10px 12px',
            background: '#0d0d0d',
            border: '1px solid var(--line-2)',
            borderRadius: 6,
            color: 'var(--ink)',
            fontSize: 13, fontFamily: 'inherit',
            outline: 'none', resize: 'vertical'
          }}
        />
        <div style={{ display: 'flex', gap: 6, justifyContent: 'flex-end', marginTop: 10 }}>
          <button onClick={onCancel} style={btnGhost}>Cancel</button>
          <button onClick={() => onSave(text)} disabled={!text.trim()} style={{
            ...btnPrimary,
            opacity: text.trim() ? 1 : 0.4,
            cursor: text.trim() ? 'pointer' : 'not-allowed'
          }}>Save · ⌘↵</button>
        </div>
      </div>
    </>
  );
}

// ─────────────────────────────────────────────
// Sidebar — comment list
// ─────────────────────────────────────────────
function Sidebar({ comments, commentNumbers, activeCommentId, setActiveCommentId, onDelete, onToggleResolved, page, viewport }) {
  const [filter, setFilter] = useState('all'); // 'all' | 'open' | 'resolved'

  const filtered = useMemo(() => {
    if (filter === 'open')     return comments.filter(c => c.status === 'open');
    if (filter === 'resolved') return comments.filter(c => c.status === 'resolved');
    return comments;
  }, [comments, filter]);

  return (
    <aside style={{
      width: 340,
      borderLeft: '1px solid var(--line)',
      background: 'var(--bg-2)',
      display: 'flex', flexDirection: 'column',
      flexShrink: 0,
    }}>
      <div style={{ padding: '14px 16px', borderBottom: '1px solid var(--line)' }}>
        <div className="mono" style={{ fontSize: 9, letterSpacing: '0.22em', textTransform: 'uppercase', color: 'var(--muted)', fontWeight: 600, marginBottom: 4 }}>
          {page === 'landing' ? 'Landing' : 'Post-Reg'} · {viewport}
        </div>
        <div style={{ fontSize: 16, fontWeight: 600 }}>{comments.length} {comments.length === 1 ? 'note' : 'notes'}</div>
        <div style={{ display: 'flex', gap: 4, marginTop: 12 }}>
          {[
            { id: 'all', label: 'All' },
            { id: 'open', label: 'Open' },
            { id: 'resolved', label: 'Resolved' },
          ].map(f => (
            <button key={f.id} onClick={() => setFilter(f.id)} style={{
              padding: '4px 9px',
              background: filter === f.id ? 'var(--accent)' : 'transparent',
              color: filter === f.id ? '#fff' : 'var(--muted)',
              border: '1px solid ' + (filter === f.id ? 'var(--accent)' : 'var(--line-2)'),
              borderRadius: 5,
              fontSize: 11, fontWeight: 500,
              cursor: 'pointer',
            }}>{f.label}</button>
          ))}
        </div>
      </div>

      <div style={{ flex: 1, overflowY: 'auto', padding: 8 }}>
        {filtered.length === 0 ? (
          <div style={{ padding: '24px 16px', textAlign: 'center', color: 'var(--muted-2)', fontSize: 13, fontStyle: 'italic' }}>
            {comments.length === 0 ? 'No notes yet. Click "+ Add comment" to leave one.' : 'No notes match this filter.'}
          </div>
        ) : (
          filtered.map((c) => (
            <CommentRow
              key={c.id}
              comment={c}
              index={commentNumbers[c.id] || '?'}
              active={activeCommentId === c.id}
              onSelect={() => setActiveCommentId(activeCommentId === c.id ? null : c.id)}
              onToggleResolved={() => onToggleResolved(c)}
              onDelete={() => onDelete(c.id)}
            />
          ))
        )}
      </div>
    </aside>
  );
}

function CommentRow({ comment, index, active, onSelect, onToggleResolved, onDelete }) {
  const resolved = comment.status === 'resolved';
  const [hoverResolve, setHoverResolve] = useState(false);
  const [hoverDelete, setHoverDelete] = useState(false);
  return (
    <div
      onClick={onSelect}
      style={{
        padding: 11,
        marginBottom: 6,
        background: active ? 'rgba(168,85,247,0.08)' : 'var(--card)',
        border: '1px solid ' + (active ? 'var(--accent)' : 'var(--line)'),
        borderRadius: 8,
        cursor: 'pointer',
        opacity: resolved ? 0.6 : 1,
        transition: 'border-color 140ms, background 140ms',
      }}
    >
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
        <span style={{
          width: 22, height: 22, borderRadius: '50% 50% 50% 4px',
          transform: 'rotate(-45deg)',
          background: resolved ? '#525252' : 'var(--accent)',
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          flexShrink: 0,
        }}>
          <span style={{ transform: 'rotate(45deg)', color: '#fff', fontSize: 10, fontWeight: 700 }}>{index}</span>
        </span>
        <span style={{ fontSize: 12, fontWeight: 600 }}>{comment.author || 'Anonymous'}</span>
        <span
          className="mono"
          title={formatFullDateTime(comment.created_at)}
          style={{ marginLeft: 'auto', fontSize: 10, color: 'var(--ink-2)' }}
        >
          {timeAgo(comment.created_at)}
        </span>
      </div>
      <div style={{
        fontSize: 13, color: resolved ? 'var(--muted)' : 'var(--ink-2)',
        textDecoration: resolved ? 'line-through' : 'none',
        whiteSpace: 'pre-wrap', lineHeight: 1.45,
      }}>{comment.comment}</div>
      <div className="mono" style={{
        marginTop: 8, fontSize: 11, letterSpacing: '0.06em',
        color: 'var(--ink-2)', fontWeight: 500
      }}>
        {formatFullDateTime(comment.created_at)}
      </div>
      {resolved && comment.resolved_at && (
        <div style={{
          marginTop: 8,
          padding: '6px 9px',
          background: 'rgba(0,255,133,0.06)',
          border: '1px solid rgba(0,255,133,0.18)',
          borderRadius: 5,
          display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap'
        }}>
          <span style={{
            fontSize: 10, fontWeight: 700, letterSpacing: '0.16em',
            color: '#00ff85', fontFamily: 'Geist Mono, ui-monospace, monospace'
          }}>✓ RESOLVED</span>
          <span style={{ fontSize: 11, color: 'var(--ink-2)' }}>
            by {comment.resolved_by || 'Designer'}
          </span>
          <span className="mono" style={{
            fontSize: 11, color: 'var(--ink-2)', letterSpacing: '0.04em'
          }}>
            · {formatFullDateTime(comment.resolved_at)}
          </span>
        </div>
      )}
      <div style={{ display: 'flex', gap: 6, marginTop: 8 }}>
        <button
          onClick={(e) => { e.stopPropagation(); onToggleResolved(); }}
          onMouseEnter={() => !resolved && setHoverResolve(true)}
          onMouseLeave={() => setHoverResolve(false)}
          style={resolved ? smallBtn : {
            padding: '4px 9px',
            background: hoverResolve ? '#4ade80' : 'rgba(74,222,128,0.10)',
            border: '1px solid ' + (hoverResolve ? '#4ade80' : 'rgba(74,222,128,0.35)'),
            color: hoverResolve ? '#ffffff' : '#4ade80',
            borderRadius: 4,
            fontSize: 11, fontWeight: 600,
            cursor: 'pointer', fontFamily: 'inherit',
            transition: 'background 120ms, color 120ms, border-color 120ms',
          }}
        >
          {resolved ? '↶ Reopen' : '✓ Mark as resolved'}
        </button>
        <button
          onClick={(e) => { e.stopPropagation(); onDelete(); }}
          onMouseEnter={() => setHoverDelete(true)}
          onMouseLeave={() => setHoverDelete(false)}
          style={{
            padding: '4px 9px',
            background: hoverDelete ? '#ef4444' : 'rgba(239,68,68,0.08)',
            border: '1px solid ' + (hoverDelete ? '#ef4444' : 'rgba(239,68,68,0.3)'),
            color: hoverDelete ? '#ffffff' : '#fca5a5',
            borderRadius: 4,
            fontSize: 11, fontWeight: 600,
            cursor: 'pointer', fontFamily: 'inherit',
            transition: 'background 120ms, color 120ms, border-color 120ms',
          }}
        >Delete</button>
      </div>
    </div>
  );
}

function timeAgo(iso) {
  const diff = Date.now() - new Date(iso).getTime();
  const mins = Math.floor(diff / 60000);
  if (mins < 1) return 'just now';
  if (mins < 60) return mins + 'm';
  const hrs = Math.floor(mins / 60);
  if (hrs < 24) return hrs + 'h';
  return Math.floor(hrs / 24) + 'd';
}

// Full timestamp display — "May 30, 2026 · 11:54 PM EDT"
function formatFullDateTime(iso) {
  if (!iso) return '';
  const d = new Date(iso);
  const tz = (window.FEED_CONFIG && window.FEED_CONFIG.displayTimeZone) || undefined;
  const datePart = new Intl.DateTimeFormat('en-US', {
    month: 'short', day: 'numeric', year: 'numeric', timeZone: tz
  }).format(d);
  const timePart = new Intl.DateTimeFormat('en-US', {
    hour: 'numeric', minute: '2-digit', hour12: true, timeZone: tz
  }).format(d);
  const zoneAbbrev = new Intl.DateTimeFormat('en-US', {
    timeZoneName: 'short', timeZone: tz
  }).format(d).split(' ').pop();
  return datePart + ' · ' + timePart + ' ' + zoneAbbrev;
}

// ─────────────────────────────────────────────
// First-visit author prompt
// ─────────────────────────────────────────────
function AuthorPrompt({ onSave }) {
  const [name, setName] = useState('');
  return (
    <div style={{
      position: 'fixed', inset: 0,
      background: '#0a0a0a',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: 24,
    }}>
      <form onSubmit={(e) => { e.preventDefault(); if (name.trim()) onSave(name.trim()); }}
        style={{
          width: 380,
          background: 'var(--bg-2)',
          border: '1px solid var(--accent)',
          borderRadius: 12,
          padding: 28,
          boxShadow: '0 30px 80px -20px rgba(168,85,247,0.3)',
        }}>
        <div className="mono" style={{
          fontSize: 10, letterSpacing: '0.22em', textTransform: 'uppercase',
          color: 'var(--accent)', fontWeight: 700, marginBottom: 6
        }}>REVIEW</div>
        <h1 style={{ fontSize: 24, fontWeight: 700, margin: '0 0 6px', letterSpacing: '-0.02em' }}>
          The Feed Media — design review
        </h1>
        <p style={{ fontSize: 13, color: 'var(--muted)', margin: '0 0 22px', lineHeight: 1.5 }}>
          Welcome. Click anywhere on the page to leave a note. What's your name? (so the team knows who left which comments)
        </p>
        <input
          value={name}
          onChange={e => setName(e.target.value)}
          placeholder="Your name"
          autoFocus
          style={{
            width: '100%', padding: '12px 14px',
            background: 'var(--bg-3)',
            border: '1px solid var(--line-2)',
            borderRadius: 7,
            color: 'var(--ink)',
            fontSize: 15,
            outline: 'none',
            fontFamily: 'inherit'
          }}
        />
        <button type="submit" disabled={!name.trim()} style={{
          width: '100%',
          marginTop: 14,
          padding: '12px 16px',
          background: name.trim() ? 'linear-gradient(135deg, #7c3aed 0%, #a855f7 48%, #d946ef 100%)' : 'var(--bg-3)',
          color: name.trim() ? '#fff' : 'var(--muted)',
          border: 'none',
          borderRadius: 7,
          fontSize: 14, fontWeight: 600, letterSpacing: '0.04em',
          cursor: name.trim() ? 'pointer' : 'not-allowed',
          fontFamily: 'inherit'
        }}>
          Start reviewing →
        </button>
      </form>
    </div>
  );
}

// ─────────────────────────────────────────────
// Shared styles
// ─────────────────────────────────────────────
const btnPrimary = {
  padding: '7px 12px',
  background: 'linear-gradient(135deg, #7c3aed 0%, #a855f7 48%, #d946ef 100%)',
  color: '#fff',
  border: 'none', borderRadius: 5,
  fontSize: 12, fontWeight: 600, letterSpacing: '0.04em',
  cursor: 'pointer', fontFamily: 'inherit',
};
const btnGhost = {
  padding: '7px 12px',
  background: 'transparent',
  color: 'var(--ink-2)',
  border: '1px solid var(--line-2)',
  borderRadius: 5,
  fontSize: 12, cursor: 'pointer', fontFamily: 'inherit',
};
const smallBtn = {
  padding: '4px 8px',
  background: 'transparent',
  border: '1px solid var(--line-2)',
  borderRadius: 4,
  color: 'var(--muted)',
  fontSize: 11, cursor: 'pointer', fontFamily: 'inherit',
};
const smallBtnAccent = {
  ...smallBtn,
  background: 'rgba(0,255,133,0.08)',
  border: '1px solid rgba(0,255,133,0.3)',
  color: '#00ff85',
  fontWeight: 600,
};

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
