// MedBridge — Figma-style comments backed by Supabase.
// See SUPABASE_SETUP.md for schema + RLS setup.
//
// One CommentLayer is mounted inside every DCArtboard (patched into
// design-canvas.jsx). It listens for clicks when the global comment mode is
// on, drops a pin at the click position (normalised to 0..1 of the artboard),
// and stores it in Supabase. Realtime subscriptions push new comments to
// every connected reviewer.
//
// Identity: reviewer name + a deterministic colour, kept in localStorage.
// No signup, no auth — appropriate for an internal review tool.

const { useState: useCm, useEffect: useCe, useRef: useCr } = React;

// ─── Supabase client (lazy, no-op if credentials are placeholders) ──
function getSupabase() {
  if (typeof window.supabase === 'undefined') return null;
  const url = window.SUPABASE_URL;
  const key = window.SUPABASE_ANON_KEY;
  if (!url || !key) return null;
  if (url.includes('YOUR-PROJECT-REF') || key.includes('YOUR-ANON')) return null;
  if (!window.__mb_sb) {
    try {
      window.__mb_sb = window.supabase.createClient(url, key, {
        realtime: { params: { eventsPerSecond: 5 } },
      });
    } catch (e) {
      console.warn('[MedBridge] Supabase init failed:', e);
      return null;
    }
  }
  return window.__mb_sb;
}
const SB_READY = () => !!getSupabase();

// ─── Reviewer identity ──────────────────────────────────────────────
const REVIEWER_HUES = ['#1F4E8C','#0F766E','#B45309','#7A5C8E','#558578','#7B6648','#A86373','#2A66A8','#8A6F8C','#4E7388'];

function getReviewer() {
  let name = localStorage.getItem('mb_reviewer_name') || '';
  let color = localStorage.getItem('mb_reviewer_color');
  if (!color) {
    color = REVIEWER_HUES[Math.floor(Math.random() * REVIEWER_HUES.length)];
    localStorage.setItem('mb_reviewer_color', color);
  }
  return { name, color };
}
function setReviewerName(name) {
  localStorage.setItem('mb_reviewer_name', name);
  window.dispatchEvent(new Event('mb-reviewer-changed'));
}

// ─── Global comment-mode toggle (driven from Tweaks panel) ─────────
function useCommentMode() {
  const [on, setOn] = useCm(() => !!window.__mb_comment_mode);
  useCe(() => {
    const h = (e) => setOn(!!e.detail);
    window.addEventListener('mb-comment-mode', h);
    return () => window.removeEventListener('mb-comment-mode', h);
  }, []);
  return on;
}
function setCommentMode(on) {
  window.__mb_comment_mode = on;
  window.dispatchEvent(new CustomEvent('mb-comment-mode', { detail: on }));
}

// ─── Data hook per screen ───────────────────────────────────────────
function useComments(screenId) {
  const [comments, setComments] = useCm([]);
  const [loaded, setLoaded] = useCm(false);

  useCe(() => {
    const sb = getSupabase();
    if (!sb || !screenId) { setLoaded(true); return; }
    let alive = true;

    (async () => {
      const { data, error } = await sb
        .from('comments')
        .select('*, replies:comment_replies(*)')
        .eq('screen_id', screenId)
        .order('created_at', { ascending: true });
      if (!alive) return;
      if (error) console.warn('[MedBridge] load comments failed:', error);
      setComments(data || []);
      setLoaded(true);
    })();

    const channel = sb.channel(`comments:${screenId}`)
      .on('postgres_changes',
        { event: '*', schema: 'public', table: 'comments', filter: `screen_id=eq.${screenId}` },
        (payload) => {
          if (!alive) return;
          if (payload.eventType === 'INSERT') {
            setComments(c => c.some(x => x.id === payload.new.id) ? c : [...c, { ...payload.new, replies: [] }]);
          } else if (payload.eventType === 'UPDATE') {
            setComments(c => c.map(x => x.id === payload.new.id ? { ...x, ...payload.new } : x));
          } else if (payload.eventType === 'DELETE') {
            setComments(c => c.filter(x => x.id !== payload.old.id));
          }
        })
      .on('postgres_changes',
        { event: 'INSERT', schema: 'public', table: 'comment_replies' },
        (payload) => {
          if (!alive) return;
          setComments(c => c.map(x =>
            x.id === payload.new.comment_id
              ? { ...x, replies: [...(x.replies || []), payload.new] }
              : x
          ));
        })
      .subscribe();

    return () => { alive = false; sb.removeChannel(channel); };
  }, [screenId]);

  const addComment = async (x, y, body, reviewer) => {
    const sb = getSupabase();
    if (!sb) return null;
    const { data, error } = await sb.from('comments').insert({
      screen_id: screenId, x, y, body,
      author_name: reviewer.name, author_color: reviewer.color,
    }).select().single();
    if (error) { console.warn('[MedBridge] insert comment failed:', error); return null; }
    // Optimistic; realtime will dedupe via the `some()` check above.
    setComments(c => c.some(x => x.id === data.id) ? c : [...c, { ...data, replies: [] }]);
    return data;
  };
  const addReply = async (commentId, body, reviewer) => {
    const sb = getSupabase();
    if (!sb) return;
    const { data, error } = await sb.from('comment_replies').insert({
      comment_id: commentId, body,
      author_name: reviewer.name, author_color: reviewer.color,
    }).select().single();
    if (error) { console.warn('[MedBridge] insert reply failed:', error); return; }
    setComments(c => c.map(x =>
      x.id === commentId && !(x.replies || []).some(r => r.id === data.id)
        ? { ...x, replies: [...(x.replies || []), data] }
        : x
    ));
  };
  const toggleResolve = async (id, resolved) => {
    const sb = getSupabase();
    if (!sb) return;
    await sb.from('comments').update({ resolved: !resolved }).eq('id', id);
  };
  const deleteComment = async (id) => {
    const sb = getSupabase();
    if (!sb) return;
    await sb.from('comments').delete().eq('id', id);
  };

  return { comments, loaded, addComment, addReply, toggleResolve, deleteComment };
}

// ─── A single pin + its open thread ─────────────────────────────────
function CommentPin({ comment, index, open, onOpen, onClose, onReply, onToggleResolve, onDelete, reviewer }) {
  const [draft, setDraft] = useCm('');
  const replies = comment.replies || [];
  const initials = (comment.author_name || '?').trim().split(/\s+/).map(s => s[0]).filter(Boolean).slice(0,2).join('').toUpperCase();

  // Decide which side the thread opens on so it doesn't fall off the edge.
  const openRight = comment.x < 0.5;

  const submit = (e) => {
    e?.preventDefault();
    if (!draft.trim()) return;
    if (!reviewer.name) {
      const n = prompt('Ihr Name (wird mit Kommentaren angezeigt):', '');
      if (!n) return;
      setReviewerName(n.trim());
    }
    onReply(draft.trim());
    setDraft('');
  };

  return (
    <>
      <button
        onClick={(e) => { e.stopPropagation(); open ? onClose() : onOpen(); }}
        title={comment.author_name + ': ' + comment.body}
        style={{
          position: 'absolute',
          left: `${comment.x * 100}%`, top: `${comment.y * 100}%`,
          transform: 'translate(-50%, -100%)',
          pointerEvents: 'auto',
          border: 0, padding: 0, cursor: 'pointer',
          width: 28, height: 28, borderRadius: '50% 50% 50% 4px',
          background: comment.resolved ? '#E5E9F0' : (comment.author_color || '#1F4E8C'),
          color: '#fff',
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          fontFamily: '-apple-system, system-ui, sans-serif', fontSize: 11, fontWeight: 700,
          boxShadow: '0 2px 8px rgba(15,27,45,0.32), 0 0 0 2px #fff',
          opacity: comment.resolved ? 0.55 : 1,
        }}>
        {comment.resolved ? '✓' : index}
      </button>

      {open && (
        <div onClick={(e) => e.stopPropagation()} style={{
          position: 'absolute',
          left: `${comment.x * 100}%`, top: `${comment.y * 100}%`,
          transform: `translate(${openRight ? '20px' : 'calc(-100% - 20px)'}, -8px)`,
          width: 280, maxWidth: 280, pointerEvents: 'auto', zIndex: 200,
          background: 'rgba(255,255,255,0.96)',
          WebkitBackdropFilter: 'blur(20px) saturate(180%)',
          backdropFilter: 'blur(20px) saturate(180%)',
          border: '0.5px solid rgba(255,255,255,0.8)',
          borderRadius: 14,
          boxShadow: '0 1px 0 rgba(255,255,255,0.6) inset, 0 16px 40px rgba(15,27,45,0.22), 0 1px 2px rgba(15,27,45,0.08)',
          fontFamily: '-apple-system, system-ui, sans-serif', fontSize: 13, color: '#0F1B2D',
          overflow: 'hidden',
        }}>
          <div style={{ padding: 12, borderBottom: '0.5px solid rgba(15,27,45,0.08)' }}>
            <CommentHead c={comment} initials={initials} />
            <div style={{ marginTop: 6, fontSize: 13, color: '#0F1B2D', whiteSpace: 'pre-wrap', lineHeight: 1.45 }}>{comment.body}</div>
          </div>
          {replies.map((r) => (
            <div key={r.id} style={{ padding: 12, borderBottom: '0.5px solid rgba(15,27,45,0.06)' }}>
              <CommentHead c={r} initials={(r.author_name || '?').trim().split(/\s+/).map(s => s[0]).filter(Boolean).slice(0,2).join('').toUpperCase()} />
              <div style={{ marginTop: 6, fontSize: 13, whiteSpace: 'pre-wrap', lineHeight: 1.45 }}>{r.body}</div>
            </div>
          ))}
          <form onSubmit={submit} style={{ padding: 10, display: 'flex', gap: 6, alignItems: 'flex-end' }}>
            <textarea value={draft} onChange={(e) => setDraft(e.target.value)}
              onKeyDown={(e) => { if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) submit(e); }}
              placeholder="Antworten…" rows={1}
              style={{ flex: 1, resize: 'none', border: '1px solid rgba(15,27,45,0.14)', borderRadius: 10, padding: '8px 10px', fontFamily: 'inherit', fontSize: 13, outline: 'none', minHeight: 32, maxHeight: 96, background: '#fff' }} />
            <button type="submit" disabled={!draft.trim()} style={{
              border: 0, padding: '0 12px', height: 32, borderRadius: 10,
              background: draft.trim() ? '#1F4E8C' : '#E5E9F0', color: '#fff', cursor: draft.trim() ? 'pointer' : 'default',
              fontWeight: 600, fontSize: 12,
            }}>Senden</button>
          </form>
          <div style={{ display: 'flex', justifyContent: 'space-between', padding: '6px 10px 10px', borderTop: '0.5px solid rgba(15,27,45,0.06)' }}>
            <button onClick={onToggleResolve} style={{ border: 0, background: 'transparent', cursor: 'pointer', fontSize: 11, fontWeight: 600, color: comment.resolved ? '#1F7A4D' : '#42526E', padding: '4px 6px' }}>
              {comment.resolved ? '↺ Wieder öffnen' : '✓ Erledigt'}
            </button>
            <button onClick={onClose} style={{ border: 0, background: 'transparent', cursor: 'pointer', fontSize: 11, fontWeight: 600, color: '#6B7A90', padding: '4px 6px' }}>Schließen</button>
          </div>
        </div>
      )}
    </>
  );
}

function CommentHead({ c, initials }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
      <span style={{
        width: 22, height: 22, borderRadius: 999,
        background: c.author_color || '#1F4E8C', color: '#fff',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        fontSize: 10, fontWeight: 700, flex: 'none',
      }}>{initials}</span>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 12, fontWeight: 700, color: '#0F1B2D', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{c.author_name}</div>
        <div style={{ fontSize: 10.5, color: 'rgba(15,27,45,0.55)' }}>{relTime(c.created_at)}</div>
      </div>
    </div>
  );
}

function relTime(iso) {
  if (!iso) return '';
  const t = new Date(iso).getTime();
  const d = Date.now() - t;
  if (d < 45000) return 'jetzt';
  if (d < 3600000) return `vor ${Math.round(d/60000)} Min`;
  if (d < 86400000) return `vor ${Math.round(d/3600000)} h`;
  return new Date(iso).toLocaleDateString('de-AT');
}

// ─── Draft pin (the one being composed) ─────────────────────────────
function CommentDraft({ draft, onChange, onSubmit, onCancel, reviewer }) {
  const ref = useCr(null);
  useCe(() => { ref.current && ref.current.focus(); }, []);
  const openRight = draft.x < 0.5;
  return (
    <>
      <span style={{
        position: 'absolute',
        left: `${draft.x * 100}%`, top: `${draft.y * 100}%`,
        transform: 'translate(-50%, -100%)', pointerEvents: 'none',
        width: 28, height: 28, borderRadius: '50% 50% 50% 4px',
        background: reviewer.color, opacity: 0.5, border: '2px dashed rgba(255,255,255,0.9)',
      }} />
      <div onClick={(e) => e.stopPropagation()} style={{
        position: 'absolute',
        left: `${draft.x * 100}%`, top: `${draft.y * 100}%`,
        transform: `translate(${openRight ? '20px' : 'calc(-100% - 20px)'}, -8px)`,
        width: 280, pointerEvents: 'auto', zIndex: 220,
        background: 'rgba(255,255,255,0.97)',
        WebkitBackdropFilter: 'blur(20px) saturate(180%)',
        backdropFilter: 'blur(20px) saturate(180%)',
        border: '0.5px solid rgba(255,255,255,0.8)',
        borderRadius: 14,
        boxShadow: '0 16px 40px rgba(15,27,45,0.22), 0 1px 2px rgba(15,27,45,0.08)',
        fontFamily: '-apple-system, system-ui, sans-serif',
      }}>
        <form onSubmit={(e) => { e.preventDefault(); onSubmit(); }} style={{ padding: 10 }}>
          <textarea ref={ref} rows={3} value={draft.body}
            onChange={(e) => onChange({ ...draft, body: e.target.value })}
            onKeyDown={(e) => {
              if (e.key === 'Escape') onCancel();
              else if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) onSubmit();
            }}
            placeholder={reviewer.name ? `Kommentar als ${reviewer.name} …` : 'Kommentar — Sie werden gleich nach Ihrem Namen gefragt …'}
            style={{ width: '100%', resize: 'none', border: '1px solid rgba(15,27,45,0.14)', borderRadius: 10, padding: '8px 10px', fontFamily: 'inherit', fontSize: 13, outline: 'none', minHeight: 64, background: '#fff', boxSizing: 'border-box' }} />
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 8 }}>
            <span style={{ fontSize: 11, color: '#6B7A90' }}>⌘+Enter zum Senden</span>
            <div style={{ display: 'flex', gap: 6 }}>
              <button type="button" onClick={onCancel} style={{ border: 0, background: 'transparent', cursor: 'pointer', fontSize: 12, fontWeight: 600, color: '#42526E', padding: '6px 8px' }}>Abbrechen</button>
              <button type="submit" disabled={!draft.body.trim()} style={{
                border: 0, padding: '0 12px', height: 30, borderRadius: 10,
                background: draft.body.trim() ? reviewer.color : '#E5E9F0', color: '#fff', cursor: draft.body.trim() ? 'pointer' : 'default',
                fontWeight: 600, fontSize: 12,
              }}>Posten</button>
            </div>
          </div>
        </form>
      </div>
    </>
  );
}

// ─── The layer that wraps every artboard ───────────────────────────
// screenAware=true → append the inner-screen identifier published by the
// interactive prototype (via window.__mb_active_screen) so pins are scoped
// to the screen the user was on when they dropped them.
function CommentLayer({ screenId, screenAware = false }) {
  const mode = useCommentMode();
  const [reviewer, setReviewer] = useCm(getReviewer());
  useCe(() => {
    const h = () => setReviewer(getReviewer());
    window.addEventListener('mb-reviewer-changed', h);
    return () => window.removeEventListener('mb-reviewer-changed', h);
  }, []);
  const [innerScreen, setInnerScreen] = useCm(() => screenAware ? (window.__mb_active_screen || '') : '');
  useCe(() => {
    if (!screenAware) return;
    const h = (e) => setInnerScreen(e.detail || '');
    window.addEventListener('mb-active-screen', h);
    return () => window.removeEventListener('mb-active-screen', h);
  }, [screenAware]);
  const fullScreenId = screenAware && innerScreen ? `${screenId}/${innerScreen}` : screenId;
  const { comments, loaded, addComment, addReply, toggleResolve } = useComments(fullScreenId);
  const [drafting, setDrafting] = useCm(null);
  const [openId, setOpenId] = useCm(null);

  if (!SB_READY() || !screenId) return null;

  const onLayerClick = (e) => {
    // Only treat clicks directly on the layer surface as new-pin intent;
    // clicks bubbled up from pins or the draft popover are stopped earlier.
    if (e.target !== e.currentTarget) return;
    if (!mode) return;
    if (drafting) { setDrafting(null); return; }
    const rect = e.currentTarget.getBoundingClientRect();
    const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
    const y = Math.max(0, Math.min(1, (e.clientY - rect.top)  / rect.height));
    setDrafting({ x, y, body: '' });
    setOpenId(null);
  };

  const submitDraft = async () => {
    if (!drafting || !drafting.body.trim()) return;
    let r = reviewer;
    if (!r.name) {
      const n = prompt('Ihr Name (wird mit Kommentaren angezeigt):', '');
      if (!n || !n.trim()) return;
      setReviewerName(n.trim());
      r = { ...r, name: n.trim() };
      setReviewer(r);
    }
    await addComment(drafting.x, drafting.y, drafting.body.trim(), r);
    setDrafting(null);
  };

  const visible = comments; // include resolved (they show as grey ✓)

  return (
    <div
      onClick={onLayerClick}
      style={{
        position: 'absolute', inset: 0, zIndex: 90,
        pointerEvents: mode || visible.length > 0 ? 'auto' : 'none',
        cursor: mode && !drafting ? 'crosshair' : 'default',
        background: mode ? 'rgba(31,78,140,0.04)' : 'transparent',
        outline: mode ? '1.5px dashed rgba(31,78,140,0.35)' : 'none',
        outlineOffset: '-1px',
      }}>
      {/* Pins always allow pointer events even when layer is passthrough. */}
      <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
        {visible.map((c, i) => (
          <CommentPin
            key={c.id}
            comment={c}
            index={i + 1}
            open={openId === c.id}
            onOpen={() => { setOpenId(c.id); setDrafting(null); }}
            onClose={() => setOpenId(null)}
            onReply={(body) => addReply(c.id, body, reviewer)}
            onToggleResolve={() => toggleResolve(c.id, c.resolved)}
            reviewer={reviewer}
          />
        ))}
        {drafting && (
          <CommentDraft
            draft={drafting}
            onChange={setDrafting}
            onSubmit={submitDraft}
            onCancel={() => setDrafting(null)}
            reviewer={reviewer}
          />
        )}
      </div>
    </div>
  );
}

Object.assign(window, { CommentLayer, useCommentMode, setCommentMode, getReviewer, setReviewerName, SB_READY });
