// User-facing notifications: bell icon in the navbar + drawer + SSE.
//
// Reads from /api/users/me/notifications; SSE on /api/users/me/events.
// Click a notification: marks it read, navigates via its `link` field
// (format 'rental:<id>' / 'listing:<id>') if recognised.

const { useState: uSN, useEffect: uEN, useRef: uRN, useCallback: uCN } = React;

const TONE = {
  info:    'border-blue-200 bg-blue-50/40',
  warn:    'border-amber-200 bg-amber-50/40',
  critical:'border-red-200 bg-red-50/40',
};

function fmtRel(iso) {
  if (!iso) return '';
  const d = new Date(iso);
  const diff = (Date.now() - d.getTime()) / 1000;
  if (diff < 60)        return 'just now';
  if (diff < 3600)      return `${Math.floor(diff / 60)}m ago`;
  if (diff < 86400)     return `${Math.floor(diff / 3600)}h ago`;
  if (diff < 86400 * 7) return `${Math.floor(diff / 86400)}d ago`;
  return d.toLocaleDateString();
}

function NotificationRow({ n, onClick }) {
  const tone = TONE[n.severity] || TONE.info;
  return (
    <button onClick={onClick}
      className={`w-full text-left p-3 border ${tone} rounded-xl hover:shadow-sm transition ${
        n.read_at ? 'opacity-60' : ''
      }`}>
      <div className="flex items-baseline justify-between gap-2">
        <span className="font-semibold text-indigo-deep text-[13px] truncate">{n.title || n.kind}</span>
        <span className="text-[10px] text-slate-soft flex-none">{fmtRel(n.triggered_at)}</span>
      </div>
      {n.body && <div className="text-[12px] text-slate-soft mt-1 line-clamp-3">{n.body}</div>}
    </button>
  );
}

function UserFeed({ go, accessToken, apiBase }) {
  const [open, setOpen] = uSN(false);
  const [items, setItems] = uSN([]);
  const [unread, setUnread] = uSN(0);
  const drawerRef = uRN(null);
  const esRef = uRN(null);

  const fetchNotifs = uCN(async () => {
    if (!accessToken) return;
    try {
      const r = await fetch(`${apiBase}/api/users/me/notifications`, {
        headers: { Authorization: `Bearer ${accessToken}` },
      });
      if (r.ok) {
        const data = await r.json();
        setItems(data.items || []);
        setUnread(data.unread_count || 0);
      }
    } catch {}
  }, [accessToken, apiBase]);

  uEN(() => { fetchNotifs(); }, [fetchNotifs]);

  // SSE: live update when a new notification fires.
  uEN(() => {
    if (!accessToken) return;
    let es;
    try {
      const url = `${apiBase}/api/users/me/events?token=${encodeURIComponent(accessToken)}`;
      es = new EventSource(url);
      esRef.current = es;
    } catch { return; }
    const onMsg = () => { fetchNotifs(); };
    // All `user.<id>.notification` events have the type `user.<id>.notification`
    // — addEventListener with a non-`message` name catches that exact name.
    // Since the type contains the user id we can't pre-register one listener,
    // so use a generic message listener instead — EventSource fires `message`
    // for unnamed events but named events need their own listener. To keep
    // the code simple, we re-fetch on EVERY event we receive, even the hello.
    es.addEventListener('open', onMsg);
    // Catch-all: EventSource's `onmessage` only fires for events with no
    // type field. We re-poll on a short interval as belt + suspenders since
    // the named-event names contain the user id and can't be statically
    // listed. The polling fallback is 30s.
    const tick = setInterval(fetchNotifs, 30_000);
    return () => {
      clearInterval(tick);
      try { es.close(); } catch {}
    };
  }, [accessToken, apiBase, fetchNotifs]);

  // Close on outside click.
  uEN(() => {
    if (!open) return;
    const onDown = (e) => {
      if (drawerRef.current && !drawerRef.current.contains(e.target)) setOpen(false);
    };
    document.addEventListener('mousedown', onDown);
    return () => document.removeEventListener('mousedown', onDown);
  }, [open]);

  async function markRead(n) {
    if (!n.read_at) {
      try {
        await fetch(`${apiBase}/api/users/me/notifications/${n.id}/read`, {
          method: 'POST',
          headers: { Authorization: `Bearer ${accessToken}` },
        });
      } catch {}
    }
    fetchNotifs();
    // Resolve the link.
    if (n.link) {
      const [kind, id] = n.link.split(':');
      if (kind === 'rental') go('profile', { tab: 'my-requests' });
      else if (kind === 'listing') go('listing-detail', { id: parseInt(id, 10), from: 'notification' });
    }
    setOpen(false);
  }

  async function markAll() {
    try {
      await fetch(`${apiBase}/api/users/me/notifications/read-all`, {
        method: 'POST',
        headers: { Authorization: `Bearer ${accessToken}` },
      });
    } catch {}
    fetchNotifs();
  }

  if (!accessToken) return null;

  return (
    <div className="relative" ref={drawerRef}>
      <button onClick={() => setOpen(o => !o)}
        className="relative w-9 h-9 rounded-full flex items-center justify-center border border-slate-line hover:border-indigo-deep transition"
        aria-label="Notifications">
        <Icon name="Bell" size={16} />
        {unread > 0 && (
          <span className="absolute -top-1 -right-1 min-w-[18px] h-[18px] px-1 rounded-full bg-coral text-white text-[10px] font-bold flex items-center justify-center">
            {unread > 99 ? '99+' : unread}
          </span>
        )}
      </button>
      {open && (
        <div className="absolute right-0 top-full mt-2 w-[360px] max-w-[92vw] bg-white rounded-2xl border border-slate-line shadow-xl overflow-hidden">
          <div className="px-4 py-3 flex items-baseline justify-between border-b border-slate-line/60">
            <div className="font-bold text-indigo-deep text-[14px]">Notifications</div>
            {unread > 0 && (
              <button onClick={markAll}
                className="text-[11px] text-indigo-deep hover:underline font-medium">
                Mark all read
              </button>
            )}
          </div>
          <div className="max-h-[400px] overflow-y-auto p-2 space-y-2">
            {items.length === 0 ? (
              <div className="p-8 text-center text-slate-soft text-sm">
                Nothing here yet.
              </div>
            ) : (
              items.map(n => <NotificationRow key={n.id} n={n} onClick={() => markRead(n)} />)
            )}
          </div>
        </div>
      )}
    </div>
  );
}

// Export
window.UserFeed = UserFeed;
