/* Notifications inbox — wired to /api/broadcasts (list) + /api/events. */

/* Broadcast composer — POST /api/broadcasts. Used to push an
   organisation-wide message to every screen + the topbar bell. Backend
   accepts: severity (info|warning|critical), title, body, optional
   site_id / zone_id / screen_id scope, optional expires_at. We expose
   the most common fields and let advanced scoping live in the API. */
function BroadcastComposerModal({ open, onClose, onSent }) {
  const [severity, setSeverity] = useState('info');
  const [title, setTitle]       = useState('');
  const [body, setBody]         = useState('');
  const [expiresHours, setExpiresHours] = useState('24');
  const [busy, setBusy]         = useState(false);
  const [err, setErr]           = useState(null);

  useEffect(() => {
    if (!open) return;
    setSeverity('info'); setTitle(''); setBody(''); setExpiresHours('24');
    setBusy(false); setErr(null);
  }, [open]);

  useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === 'Escape') onClose && onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [open, onClose]);

  async function send(e) {
    e && e.preventDefault();
    if (!title.trim()) { setErr('Title is required.'); return; }
    setErr(null); setBusy(true);
    try {
      const expires_at = expiresHours
        ? new Date(Date.now() + Number(expiresHours) * 3600 * 1000).toISOString()
        : null;
      await apiFetch('/api/broadcasts', {
        method: 'POST',
        body: JSON.stringify({
          severity,
          title: title.trim(),
          body: body.trim(),
          expires_at,
        }),
      });
      window.toast && window.toast.success('Broadcast sent to every screen');
      if (onSent) onSent();
      onClose && onClose();
    } catch (e2) {
      setErr(e2.message);
    } finally {
      setBusy(false);
    }
  }

  if (!open) return null;
  return (
    <div
      onClick={(e) => { if (e.target === e.currentTarget) onClose && onClose(); }}
      style={{
        position: 'fixed', inset: 0, zIndex: 220,
        background: 'rgba(6, 7, 10, 0.78)', backdropFilter: 'blur(6px)',
        display: 'grid', placeItems: 'center', padding: 24,
      }}
    >
      <form
        onSubmit={send}
        style={{
          width: 'min(480px, 100%)',
          background: 'var(--aq-surface)',
          border: '1px solid var(--aq-line)',
          borderRadius: 14, boxShadow: 'var(--aq-shadow-2)',
          display: 'flex', flexDirection: 'column', maxHeight: '82vh',
        }}
      >
        <header style={{
          padding: '14px 18px', borderBottom: '1px solid var(--aq-line)',
          display: 'flex', alignItems: 'center', gap: 10,
        }}>
          <div style={{
            width: 28, height: 28, borderRadius: 8,
            background: 'var(--aq-accent-soft)', color: 'var(--aq-accent)',
            display: 'grid', placeItems: 'center',
          }}><Icon name="bell" size={14} /></div>
          <div style={{ flex: 1 }}>
            <div style={{ fontFamily: 'var(--aq-ff-display)', fontSize: 14.5, color: 'var(--aq-text)', fontWeight: 500 }}>
              New broadcast
            </div>
            <div style={{ fontSize: 11.5, color: 'var(--aq-text-faint)' }}>
              Goes to every screen in your org until dismissed or expired.
            </div>
          </div>
          <button type="button" className="aq-icon-btn" onClick={onClose}><Icon name="close" size={13} /></button>
        </header>

        <div style={{ overflowY: 'auto', padding: 16, display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div>
            <div style={{ fontSize: 11.5, color: 'var(--aq-text-dim)', marginBottom: 5 }}>Severity</div>
            <div style={{ display: 'flex', gap: 6 }}>
              {[
                ['info',     'Info',     'var(--aq-accent)'],
                ['warning',  'Warning',  'var(--aq-warn, #F2C879)'],
                ['critical', 'Critical', 'var(--aq-danger)'],
              ].map(([id, label, color]) => (
                <button
                  key={id}
                  type="button"
                  onClick={() => setSeverity(id)}
                  style={{
                    flex: 1, padding: '8px 10px', borderRadius: 7,
                    background: severity === id ? color : 'transparent',
                    color: severity === id ? '#06090f' : 'var(--aq-text-dim)',
                    border: `1px solid ${severity === id ? color : 'var(--aq-line)'}`,
                    fontSize: 12, fontWeight: 500, cursor: 'pointer',
                    fontFamily: 'inherit',
                  }}
                >{label}</button>
              ))}
            </div>
          </div>

          <label style={{ display: 'block' }}>
            <div style={{ fontSize: 11.5, color: 'var(--aq-text-dim)', marginBottom: 5 }}>Title</div>
            <input
              type="text"
              required
              autoFocus
              maxLength={120}
              value={title}
              onChange={(e) => setTitle(e.target.value)}
              placeholder="Reef tank closed for cleaning"
              style={{
                width: '100%', padding: '8px 10px', fontSize: 13,
                background: 'var(--aq-surface-2)', border: '1px solid var(--aq-line)',
                borderRadius: 7, color: 'var(--aq-text)', font: 'inherit', outline: 0,
              }}
            />
          </label>

          <label style={{ display: 'block' }}>
            <div style={{ fontSize: 11.5, color: 'var(--aq-text-dim)', marginBottom: 5 }}>Body <span style={{ color: 'var(--aq-text-faint)' }}>(optional)</span></div>
            <textarea
              rows={4}
              maxLength={500}
              value={body}
              onChange={(e) => setBody(e.target.value)}
              placeholder="One or two short sentences — visitors won't see this; staff will."
              style={{
                width: '100%', padding: '8px 10px', fontSize: 13,
                background: 'var(--aq-surface-2)', border: '1px solid var(--aq-line)',
                borderRadius: 7, color: 'var(--aq-text)', font: 'inherit', outline: 0,
                resize: 'vertical',
              }}
            />
          </label>

          <label style={{ display: 'block' }}>
            <div style={{ fontSize: 11.5, color: 'var(--aq-text-dim)', marginBottom: 5 }}>Auto-expire after</div>
            <select
              value={expiresHours}
              onChange={(e) => setExpiresHours(e.target.value)}
              style={{
                width: '100%', padding: '8px 10px', fontSize: 13,
                background: 'var(--aq-surface-2)', border: '1px solid var(--aq-line)',
                borderRadius: 7, color: 'var(--aq-text)', font: 'inherit', outline: 0,
              }}
            >
              <option value="1">1 hour</option>
              <option value="4">4 hours</option>
              <option value="24">24 hours</option>
              <option value="168">1 week</option>
              <option value="">Never (manual dismiss)</option>
            </select>
          </label>

          {err && (
            <div style={{
              padding: '8px 10px', borderRadius: 6,
              background: 'color-mix(in srgb, var(--aq-danger) 12%, transparent)',
              color: 'var(--aq-danger)', fontSize: 12,
            }}>{err}</div>
          )}
        </div>

        <footer style={{
          padding: '10px 16px', borderTop: '1px solid var(--aq-line)',
          background: 'var(--aq-surface-2)',
          display: 'flex', justifyContent: 'flex-end', gap: 8,
        }}>
          <button type="button" className="x-btn ghost" onClick={onClose}>Cancel</button>
          <button
            type="submit"
            className="x-btn"
            disabled={busy || !title.trim()}
          >{busy ? 'Sending…' : 'Send broadcast'}</button>
        </footer>
      </form>
    </div>
  );
}

function NF_relativeTime(iso) {
  if (!iso) return '—';
  const ageMs = Date.now() - new Date(iso).getTime();
  const m = Math.round(ageMs / 60000);
  if (m < 1) return 'just now';
  if (m < 60) return `${m} min ago`;
  const h = Math.round(m / 60);
  if (h < 24) return `${h} h ago`;
  const d = Math.round(h / 24);
  return `${d} d ago`;
}

/* Per-user "read" state. The original CMS persisted this client-side
   too — localStorage-keyed by user id so multiple operators on the
   same browser don't overwrite each other's read state. */
const NF_READ_KEY = (uid) => `aquaos.notif.read.${uid || 'anon'}`;
function NF_loadRead(uid) {
  try { return new Set(JSON.parse(localStorage.getItem(NF_READ_KEY(uid)) || '[]')); }
  catch (_) { return new Set(); }
}
function NF_saveRead(uid, set) {
  try { localStorage.setItem(NF_READ_KEY(uid), JSON.stringify([...set])); } catch (_) {}
}

function NotificationsScreen() {
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [filter, setFilter] = useState('all');
  const me = Auth.getUser();
  const [readIds, setReadIds] = useState(() => NF_loadRead(me && me.id));
  /* Composer modal — only org_admin / site_manager can post a
     broadcast (backend enforces the same; the UI hides the entry point
     for everyone else so the role surfaces match). */
  const [composerOpen, setComposerOpen] = useState(false);
  const [reload, setReload] = useState(0);
  const canBroadcast = me && (me.role === 'org_admin' || me.role === 'site_manager');

  useEffect(() => {
    let cancelled = false;
    Promise.all([
      apiFetch('/api/broadcasts').catch(() => null),
      apiFetch('/api/events').catch(() => null),
    ]).then(([bc, ev]) => {
      void reload; /* re-run on broadcast creation */
      if (cancelled) return;
      const broadcasts = ((bc && (bc.broadcasts || bc)) || []).map((b) => ({
        id: 'b-' + b.id, lvl: b.severity === 'critical' ? 'warn' : (b.severity || 'info'),
        title: b.title || b.message || 'Broadcast',
        meta: `${b.scope || 'Site-wide'} · ${NF_relativeTime(b.created_at || b.starts_at)}`,
        tag: 'Broadcast', ts: b.created_at || b.starts_at,
        sourceType: 'broadcast', sourceId: b.id, screenId: b.screen_id, siteId: b.site_id,
      }));
      const events = ((ev && (ev.events || ev)) || []).map((e) => ({
        id: 'e-' + (e.id || Math.random()), lvl: e.kind === 'alert' ? 'warn' : 'info',
        title: e.title || e.event_type || 'Event',
        meta: `${e.site_name || e.scope || 'Site'} · ${NF_relativeTime(e.starts_at || e.created_at)}`,
        tag: e.event_type || 'Event', ts: e.starts_at || e.created_at,
        sourceType: 'event', sourceId: e.id, screenCode: e.screen_code, siteName: e.site_name,
      }));
      const all = [...broadcasts, ...events].sort((a, b) => new Date(b.ts || 0) - new Date(a.ts || 0));
      setItems(all);
      setLoading(false);
    }).catch((err) => { if (!cancelled) { setError(err.message); setLoading(false); } });
    return () => { cancelled = true; };
  }, [reload]);

  function markOne(id) {
    setReadIds((cur) => {
      const next = new Set(cur); next.add(id);
      NF_saveRead(me && me.id, next);
      return next;
    });
  }
  function markAllRead() {
    setReadIds((cur) => {
      const next = new Set(cur);
      items.forEach((i) => next.add(i.id));
      NF_saveRead(me && me.id, next);
      return next;
    });
  }
  function open(n) {
    markOne(n.id);
    /* Drilldown by source type. Broadcasts have no detail page; events
       link back to the screen / site that produced them. */
    if (n.sourceType === 'event' && n.screenCode) {
      window.location.hash = `#screen/${n.screenCode}`;
    } else if (n.sourceType === 'event' && n.siteName) {
      const slug = n.siteName.toLowerCase().replace(/\s+/g, '-');
      window.location.hash = `#site/${slug}`;
    } else if (n.sourceType === 'broadcast') {
      /* Broadcasts surface from the popover; opening goes back to the
         feed in lieu of a dedicated broadcast detail. */
      window.location.hash = '#notifications';
    }
  }

  const filtered = filter === 'all' ? items : items.filter((n) => n.lvl === filter);
  const unreadCount = items.filter((n) => !readIds.has(n.id)).length;

  return (
    <div className="nf-content">
      <header style={{ marginBottom: 8 }}>
        <div className="aq-eyebrow"><span>Slate</span><span className="aq-sep">·</span><span>Activity feed</span></div>
        <h1 style={{
          fontFamily: 'var(--aq-ff-display)', fontSize: 26, fontWeight: 500,
          letterSpacing: '-0.012em', color: 'var(--aq-text)', margin: '4px 0 4px',
        }}>Notifications</h1>
        <p style={{ fontSize: 13, color: 'var(--aq-text-faint)', margin: 0 }}>
          Combines broadcasts (org announcements) and events (timed activities).
        </p>
      </header>

      <div style={{
        display: 'flex', alignItems: 'center', gap: 8, padding: 8,
        background: 'var(--aq-surface)', border: '1px solid var(--aq-line)', borderRadius: 8,
      }}>
        {[['all', 'All', items.length], ['warn', 'Warnings', items.filter((n) => n.lvl === 'warn').length], ['info', 'Info', items.filter((n) => n.lvl === 'info').length]].map(([id, label, count]) => (
          <button
            key={id} onClick={() => setFilter(id)}
            style={{
              padding: '5px 12px', borderRadius: 5,
              background: filter === id ? 'var(--aq-surface-2)' : 'transparent',
              border: 0, color: filter === id ? 'var(--aq-text)' : 'var(--aq-text-dim)',
              font: 'inherit', fontSize: 12, cursor: 'pointer',
            }}
          >{label}<span style={{
            marginLeft: 6, fontFamily: 'var(--aq-ff-mono)', fontSize: 10.5,
            padding: '1px 5px', borderRadius: 99,
            background: 'rgba(255,255,255,0.06)', color: 'var(--aq-text-faint)',
          }}>{count}</span></button>
        ))}
        <div style={{ flex: 1 }} />
        {canBroadcast && (
          <button
            className="x-btn sm"
            onClick={() => setComposerOpen(true)}
            title="Send a message to every screen in your org"
          >
            <Icon name="bell" size={11} />
            <span style={{ marginLeft: 5 }}>New broadcast</span>
          </button>
        )}
        <button
          className="x-btn ghost sm"
          onClick={markAllRead}
          disabled={unreadCount === 0}
          title={unreadCount === 0 ? 'No unread items' : `Mark ${unreadCount} as read`}
        >Mark all read</button>
      </div>

      {loading && <div style={{ padding: 60, textAlign: 'center', color: 'var(--aq-text-faint)' }}>Loading activity…</div>}
      {error && (
        <div style={{
          padding: '10px 14px',
          background: 'color-mix(in srgb, var(--aq-danger) 12%, transparent)',
          border: '1px solid color-mix(in srgb, var(--aq-danger) 30%, transparent)',
          borderRadius: 6, color: 'var(--aq-danger)', fontSize: 12,
        }}>{error}</div>
      )}

      {!loading && !error && filtered.length === 0 && (
        <div className="x-card" style={{ padding: '60px 24px', textAlign: 'center' }}>
          <div style={{ fontSize: 13, color: 'var(--aq-text-faint)' }}>
            {items.length === 0 ? 'No activity yet.' : 'No items match this filter.'}
          </div>
        </div>
      )}

      <BroadcastComposerModal
        open={composerOpen}
        onClose={() => setComposerOpen(false)}
        onSent={() => setReload((n) => n + 1)}
      />

      {!loading && !error && filtered.length > 0 && (
        <ul className="x-card" style={{ listStyle: 'none', margin: 0, padding: 0 }}>
          {filtered.map((n, i) => {
            const isRead = readIds.has(n.id);
            return (
              <li key={n.id} style={{
                display: 'grid', gridTemplateColumns: '24px 1fr 80px 80px',
                gap: 12, alignItems: 'center', padding: '12px 16px',
                borderBottom: i < filtered.length - 1 ? '1px solid var(--aq-line)' : '0',
                opacity: isRead ? 0.55 : 1,
              }}>
                <span style={{
                  width: 8, height: 8, borderRadius: '50%',
                  background: isRead ? 'var(--aq-text-faint)'
                    : (n.lvl === 'warn' ? 'var(--aq-warn)' : 'var(--aq-accent)'),
                  boxShadow: !isRead && n.lvl === 'warn' ? '0 0 8px var(--aq-warn)' : undefined,
                }} />
                <div>
                  <div style={{ color: 'var(--aq-text)', fontSize: 13, fontWeight: isRead ? 400 : 500 }}>{n.title}</div>
                  <div style={{ color: 'var(--aq-text-faint)', fontSize: 11.5, marginTop: 2 }}>{n.meta}</div>
                </div>
                <span style={{
                  fontFamily: 'var(--aq-ff-mono)', fontSize: 10,
                  color: 'var(--aq-text-faint)', letterSpacing: '0.04em', textTransform: 'uppercase',
                }}>{n.tag}</span>
                <button className="x-btn ghost sm" onClick={() => open(n)}>Open</button>
              </li>
            );
          })}
        </ul>
      )}
    </div>
  );
}

window.NotificationsScreen = NotificationsScreen;
