// notifications.jsx — full-page inbox + bell slide-out panel
//
// State source: window.flowboardNotify (see notify-client.js). The
// notify-client owns the live list, fetches from /api/notifications
// on boot, listens for SSE notification.created events, and pushes
// updates to every subscriber. We just render whatever it gives us.
//
// The legacy NOTIFICATIONS mock data file is kept around for the
// label/icon palette (NOTIF_TYPES) but is no longer the data source.

// Hook: subscribe to the live notification state.
function useLiveNotifs() {
  const init = (typeof window !== "undefined" && window.flowboardNotify)
    ? window.flowboardNotify.state()
    : { items: [], unread: 0 };
  const [s, setS] = React.useState(init);
  React.useEffect(() => {
    if (!window.flowboardNotify) return;
    return window.flowboardNotify.subscribe(setS);
  }, []);
  return s;
}

// Map an audit-log row from the API into the shape the existing
// renderer expects (it predates the API and was built around mocks).
function _rowToCard(n) {
  const typeMap = {
    assigned: "assigned", mentioned: "mention", comment: "mention",
    completed: "status",  status:    "status",
    sprint:           "sprint",
    ticket_assigned:  "ticket", ticket_reply: "ticket", ticket_escalated: "ticket",
    bug_reported: "ticket", bug_fixed: "status",
  };
  const type = typeMap[n.kind] || "assigned";
  return {
    id: String(n.id),
    type,
    unread: !!n.unread,
    at: typeof fmtRelative === "function" ? fmtRelative(n.created_at) : (n.created_at || ""),
    actor: n.actor_id || null,
    project: null,
    projectColor: null,
    subject: n.subject_id ? { kind: n.subject_kind || "task", id: n.subject_id, title: n.title } : { title: n.title },
    body: n.body || "",
    cta: n.link ? "Open" : null,
    _link: n.link || null,
  };
}
function _bucketOf(n) {
  if (!n.created_at) return "earlier";
  const d = new Date(n.created_at);
  const now = new Date();
  if (isNaN(d)) return "earlier";
  if (d.toDateString() === now.toDateString()) return "today";
  const y = new Date(); y.setDate(y.getDate() - 1);
  if (d.toDateString() === y.toDateString()) return "yesterday";
  return "earlier";
}
function _navigate(link) {
  if (!link) return;
  try { window.dispatchEvent(new CustomEvent("flowboard:nav", { detail: { link, source: "notify" } })); }
  catch {}
}

// ── helpers ─────────────────────────────────────────────
function nfRenderBody(body) {
  // very lightweight **bold** → <strong>
  const parts = body.split(/(\*\*[^*]+\*\*)/g);
  return parts.map((p, i) =>
    p.startsWith("**") && p.endsWith("**")
      ? <strong key={i}>{p.slice(2, -2)}</strong>
      : <React.Fragment key={i}>{p}</React.Fragment>
  );
}

function nfSubjectPrefix(subj) {
  if (!subj) return null;
  if (subj.kind === "task")   return subj.id.toUpperCase();
  if (subj.kind === "ticket") return subj.id;
  if (subj.kind === "doc")    return "DOC";
  if (subj.kind === "sprint") return "SPRINT";
  if (subj.kind === "goal")   return "GOAL";
  return null;
}

// ── Icon block (avatar-style chip) ─────────────────────
function NFIcon({ n, size = 36 }) {
  const t = NOTIF_TYPES[n.type] || {};
  const Ic = (Icons && Icons[t.icon]) || Icons.Inbox;
  const iconSize = size === 36 ? 18 : 15;
  // If it has an actor (mention, assigned, status change), show avatar with type badge; else icon tile.
  if (n.actor) {
    const person = PEOPLE.find(p => p.id === n.actor);
    return (
      <div style={{ position: "relative", width: size, height: size, flexShrink: 0 }}>
        <Avatar person={person} size={size === 36 ? "lg" : "md"}/>
        <span style={{
          position: "absolute", right: -3, bottom: -3,
          width: 16, height: 16, borderRadius: "50%",
          background: t.color, color: "white",
          display: "flex", alignItems: "center", justifyContent: "center",
          border: "2px solid white",
        }}>
          <Ic size={9} stroke="white"/>
        </span>
      </div>
    );
  }
  return (
    <div className={`nf-card-avatar type-icon`} style={{ "--nf-icon-bg": t.color, width: size, height: size }}>
      <Ic size={iconSize} stroke="white"/>
    </div>
  );
}

function NFTypeChip({ type }) {
  const t = NOTIF_TYPES[type] || {}; if (!t.label) return null;
  return (
    <span className="nf-type-chip"
          style={{ "--nf-chip-bg": `color-mix(in oklab, ${t.color} 14%, white)`,
                   "--nf-chip-color": t.color }}>
      {t.label}
    </span>
  );
}

// ── Full-page inbox ─────────────────────────────────────
function NotificationsPage() {
  const [tab, setTab] = React.useState("all");
  const [unreadOnly, setUnreadOnly] = React.useState(false);
  // Live data from notify-client. Falls back to the legacy mock if
  // notify-client.js hasn't loaded yet (defence-in-depth — shouldn't
  // happen in production but keeps the page renderable in standalone
  // demos that don't bring the client in).
  const live = useLiveNotifs();
  const notifs = live.items.length
    ? live.items.map(n => ({ ..._rowToCard(n), bucket: _bucketOf(n) }))
    : (typeof NOTIFICATIONS !== "undefined" ? NOTIFICATIONS : []);

  const tabs = [
    { id: "all",       label: "All",         test: () => true },
    { id: "mention",   label: "@Mentions",   test: n => n.type === "mention" },
    { id: "assigned",  label: "Assigned",    test: n => n.type === "assigned" || n.type === "status" },
    { id: "due",       label: "Due & overdue", test: n => n.type === "due" || n.type === "overdue" },
    { id: "tickets",   label: "Tickets",     test: n => n.type === "ticket" },
    { id: "sprint",    label: "Sprints",     test: n => n.type === "sprint" },
    { id: "goal",      label: "Goals",       test: n => n.type === "goal" },
  ];
  const tabCounts = Object.fromEntries(tabs.map(t => [
    t.id, notifs.filter(n => t.test(n) && n.unread).length,
  ]));
  const activeTab = tabs.find(t => t.id === tab) || tabs[0];

  let list = notifs.filter(activeTab.test);
  if (unreadOnly) list = list.filter(n => n.unread);

  const buckets = [
    { id: "today",     label: "Today" },
    { id: "yesterday", label: "Yesterday" },
    { id: "earlier",   label: "Earlier this week" },
  ];

  const unreadCount = notifs.filter(n => n.unread).length;

  function markAllRead() {
    if (window.flowboardNotify) window.flowboardNotify.markAllRead();
  }
  function markOneRead(id) {
    if (window.flowboardNotify) window.flowboardNotify.markRead(parseInt(id, 10));
  }

  return (
    <div className="nf-page">
      {/* left rail */}
      <aside className="nf-rail">
        <div className="nf-rail-header">
          <div className="nf-rail-title">Notifications</div>
          {unreadCount > 0 && (
            <button className="nf-rail-mark" onClick={markAllRead}>Mark all read</button>
          )}
        </div>
        {tabs.map(t => (
          <div key={t.id}
               className={`nf-rail-item ${tab === t.id ? "is-active" : ""}`}
               onClick={() => setTab(t.id)}>
            <span className="nf-rail-icon">
              {t.id === "all" ? <Icons.Inbox size={15}/>
               : t.id === "mention" ? <Icons.MessageSq size={15}/>
               : t.id === "assigned" ? <Icons.Users size={15}/>
               : t.id === "due" ? <Icons.Clock size={15}/>
               : t.id === "tickets" ? <Icons.Alert size={15}/>
               : t.id === "sprint" ? <Icons.Lightning size={15}/>
               : <Icons.Star size={15}/>}
            </span>
            <span>{t.label}</span>
            {tabCounts[t.id] > 0
              ? <span className="nf-rail-count is-unread">{tabCounts[t.id]}</span>
              : <span className="nf-rail-count">{notifs.filter(t.test).length}</span>}
          </div>
        ))}

        <div className="nf-rail-section">Preferences</div>
        <div className="nf-rail-item" style={{ color: "var(--ink-muted)" }}>
          <Icons.Flag size={15} className="nf-rail-icon"/>
          <span>Notification settings</span>
        </div>
      </aside>

      {/* main */}
      <div className="nf-main">
        <div className="nf-head">
          <div>
            <div className="nf-head-title">{activeTab.label}</div>
            <div className="nf-head-sub">
              {list.length} {list.length === 1 ? "notification" : "notifications"}
              {unreadCount > 0 && <> · <span style={{ color: "var(--brand)", fontWeight: 600 }}>{unreadCount} unread</span></>}
            </div>
          </div>
          <div className="nf-head-filter">
            <button className={!unreadOnly ? "is-active" : ""} onClick={() => setUnreadOnly(false)}>All</button>
            <button className={unreadOnly ? "is-active" : ""} onClick={() => setUnreadOnly(true)}>Unread</button>
          </div>
        </div>

        <div className="nf-feed">
          {list.length === 0 ? (
            <EmptyState
              icon="Check"
              title="You're all caught up"
              sub={`Nothing ${unreadOnly ? "unread" : "new"} in ${activeTab.label}.`}
            />
          ) : buckets.map(b => {
            const items = list.filter(n => n.bucket === b.id);
            if (!items.length) return null;
            return (
              <React.Fragment key={b.id}>
                <div className="nf-day-header">{b.label}</div>
                {items.map(n => {
                  const subj = n.subject;
                  const project = n.project ? PROJECTS.find(p => p.id === n.project) : null;
                  return (
                    <div key={n.id}
                         className={`nf-card ${n.unread ? "is-unread" : ""}`}
                         onClick={() => markOneRead(n.id)}>
                      <NFIcon n={n}/>
                      <div className="nf-card-body">
                        <div className="nf-card-meta">
                          <NFTypeChip type={n.type}/>
                          {n.actor && <span className="nf-actor">{PEOPLE.find(p => p.id === n.actor)?.name}</span>}
                          {project && (
                            <>
                              <span className="nf-dot-sep">·</span>
                              <span className="nf-project">
                                <span className="nf-project-dot" style={{ background: n.projectColor }}/>
                                {project.name}
                              </span>
                            </>
                          )}
                        </div>
                        <div className="nf-subject">
                          {nfSubjectPrefix(subj) && <span className="nf-subject-id">{nfSubjectPrefix(subj)}</span>}
                          {subj?.title}
                        </div>
                        <div className="nf-message">{nfRenderBody(n.body)}</div>
                      </div>
                      <div className="nf-card-right">
                        <span className="nf-time">{n.at}</span>
                        {n.cta && <button className="nf-cta" onClick={e => e.stopPropagation()}>{n.cta}</button>}
                      </div>
                    </div>
                  );
                })}
              </React.Fragment>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// ── Bell slide-out panel ─────────────────────────────────
function NotificationsPanel({ open, onClose, onOpenFull }) {
  const [tab, setTab] = React.useState("all");
  const live = useLiveNotifs();
  const notifs = live.items.length
    ? live.items.map(n => ({ ..._rowToCard(n), bucket: _bucketOf(n) }))
    : (typeof NOTIFICATIONS !== "undefined" ? NOTIFICATIONS : []);
  // Local UI state for the desktop-toggle row.
  const [desktopOn, setDesktopOn] = React.useState(
    () => (window.flowboardNotify ? window.flowboardNotify.desktopEnabled() : false)
  );
  const desktopSupported = typeof window !== "undefined"
    && typeof window.Notification === "function";
  const [permState, setPermState] = React.useState(
    () => (desktopSupported ? Notification.permission : "unsupported")
  );
  async function toggleDesktop() {
    const next = !desktopOn;
    setDesktopOn(next);
    if (window.flowboardNotify) window.flowboardNotify.setDesktopEnabled(next);
    if (next && desktopSupported && Notification.permission === "default") {
      const r = await window.flowboardNotify.requestDesktopPermission();
      setPermState(r);
    }
  }
  if (!open) return null;

  const tabs = [
    { id: "all",     label: "All",        test: () => true },
    { id: "mention", label: "Mentions",   test: n => n.type === "mention" },
    { id: "due",     label: "Due",        test: n => n.type === "due" || n.type === "overdue" },
  ];
  const activeTab = tabs.find(t => t.id === tab);
  const list = notifs.filter(activeTab.test).slice(0, 8);

  function markAll() {
    if (window.flowboardNotify) window.flowboardNotify.markAllRead();
  }

  return (
    <>
      <div className="nf-panel-backdrop" onClick={onClose}/>
      <div className="nf-panel" onClick={e => e.stopPropagation()}>
        <div className="nf-panel-head">
          <div className="nf-panel-title">Notifications</div>
          <div className="nf-panel-head-actions">
            <button onClick={markAll}>Mark all read</button>
            <button onClick={onOpenFull}>Open full</button>
          </div>
        </div>
        <div className="nf-panel-tabs">
          {tabs.map(t => {
            const unread = notifs.filter(n => t.test(n) && n.unread).length;
            return (
              <button key={t.id}
                      className={`nf-panel-tab ${tab === t.id ? "is-active" : ""}`}
                      onClick={() => setTab(t.id)}>
                {t.label}
                {unread > 0 && <span className="nf-panel-tab-count">{unread}</span>}
              </button>
            );
          })}
        </div>
        <div className="nf-panel-body">
          {list.map(n => {
            const subj = n.subject;
            return (
              <div key={n.id}
                   className={`nf-panel-card ${n.unread ? "is-unread" : ""}`}
                   onClick={() => {
                     if (window.flowboardNotify) window.flowboardNotify.markRead(parseInt(n.id, 10));
                     if (n._link) _navigate(n._link);
                   }}>
                <NFIcon n={n} size={28}/>
                <div style={{ minWidth: 0 }}>
                  <div className="nf-panel-card-meta">
                    {n.actor
                      ? <><span className="nf-panel-card-actor">{PEOPLE.find(p => p.id === n.actor)?.name.split(" ")[0]}</span> · </>
                      : null}
                    <span>{NOTIF_TYPES[n.type]?.label}</span>
                    <span style={{ marginLeft: "auto" }}>{n.at.includes(":") && !n.at.includes(",") ? n.at : n.at.split(",")[0]}</span>
                  </div>
                  <div className="nf-panel-card-subject">{subj?.title}</div>
                  <div className="nf-panel-card-msg">{nfRenderBody(n.body)}</div>
                </div>
              </div>
            );
          })}
          {list.length === 0 && (
            <div style={{ padding: 40, textAlign: "center", color: "var(--ink-muted)", fontSize: 12 }}>
              Nothing new.
            </div>
          )}
        </div>
        {desktopSupported && (
          <div style={{
            display: "flex", alignItems: "center", justifyContent: "space-between",
            padding: "8px 14px", borderTop: "1px solid var(--border-row)",
            background: "var(--bg-app)", fontSize: 12,
          }}>
            <div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
              <div style={{ fontWeight: 600, color: "var(--ink-strong)" }}>
                Desktop notifications
              </div>
              <div style={{ color: "var(--ink-muted)", fontSize: 11 }}>
                {permState === "denied"
                  ? "Blocked in your browser — re-enable in site settings"
                  : desktopOn && permState === "granted"
                  ? "On — you'll get pop-ups when this tab isn't focused"
                  : "Off — flip on to get pop-ups"}
              </div>
            </div>
            <button
              onClick={toggleDesktop}
              disabled={permState === "denied"}
              style={{
                width: 38, height: 22, padding: 0, borderRadius: 11,
                border: "none",
                background: (desktopOn && permState !== "denied") ? "var(--brand)" : "#cbd5e1",
                position: "relative", cursor: permState === "denied" ? "not-allowed" : "pointer",
                transition: "background .15s",
              }}
              aria-label="Toggle desktop notifications">
              <span style={{
                position: "absolute", top: 2,
                left: (desktopOn && permState !== "denied") ? 18 : 2,
                width: 18, height: 18, borderRadius: "50%",
                background: "white", boxShadow: "0 1px 2px rgba(0,0,0,.18)",
                transition: "left .15s",
              }}/>
            </button>
          </div>
        )}
        <div className="nf-panel-foot">
          <button onClick={onOpenFull}>View all notifications →</button>
        </div>
      </div>
    </>
  );
}

// ── Standalone app ─────────────────────────────────
function NotificationsApp() {
  const [paletteOpen, setPaletteOpen] = React.useState(false);
  return (
    <div className="app">
      <Sidebar activeProject={null} onOpenTickets={() => {}} currentUserId="ay"/>
      <div className="main">
        <Topbar crumbs={[WORKSPACE.name, "Notifications"]}
                searchValue="" onSearch={() => {}}
                onOpenPalette={() => setPaletteOpen(true)}
                currentUserId="ay" people={PEOPLE}/>
        <NotificationsPage/>
      </div>
      <CommandPalette open={paletteOpen} onClose={() => setPaletteOpen(false)} onNavigate={() => {}}/>
    </div>
  );
}

// App that demos the slide-out panel on top of the main project screen
function NotificationsPanelDemoApp() {
  const [panelOpen, setPanelOpen] = React.useState(true);
  return (
    <div className="app" style={{ position: "relative" }}>
      <Sidebar activeProject={null} onOpenTickets={() => {}} currentUserId="ay"/>
      <div className="main" style={{ position: "relative" }}>
        <Topbar crumbs={[WORKSPACE.name, "My work"]}
                searchValue="" onSearch={() => {}}
                onOpenPalette={() => {}}
                currentUserId="ay" people={PEOPLE}
                onBellClick={() => setPanelOpen(v => !v)}
                bellCount={NOTIFICATIONS.filter(n => n.unread).length}/>
        <div style={{ flex: 1, background: "var(--surface-1)", padding: 48, display: "flex", alignItems: "flex-start", justifyContent: "center" }}>
          <div style={{ fontSize: 12, color: "var(--ink-muted)", fontWeight: 500, marginTop: 40 }}>
            <Icons.Inbox size={48} style={{ color: "#c4c7d0", marginBottom: 12 }}/>
            <div style={{ fontSize: 14, fontWeight: 600, color: "var(--ink-1)", marginBottom: 4 }}>Bell slide-out panel</div>
            <div>Click the bell in the topbar to toggle · click any row to mark read · "Open full" jumps to the inbox.</div>
          </div>
        </div>
        <NotificationsPanel open={panelOpen}
                            onClose={() => setPanelOpen(false)}
                            onOpenFull={() => setPanelOpen(false)}/>
      </div>
    </div>
  );
}

Object.assign(window, { NotificationsApp, NotificationsPage, NotificationsPanel, NotificationsPanelDemoApp, NFIcon });
