/* The one bell (Interaction Standard §6).
 *
 * Presentational: the app supplies rows in the standard's event shape and two
 * callbacks; polling and transport stay in the app's query layer, because
 * apps differ there and must not differ HERE. Three button states — idle,
 * unread (count), needs-you (accent: a mention or a turn is waiting, which is
 * a stronger claim than "something happened"). The panel follows review's
 * format: rows read as sentences, "on {context}", a category chip, a one-line
 * preview, the time. */

import { useState, type ReactNode } from "react";
import { relTime } from "./time";
import { StatusChip } from "./Thread";

export type BellItem = {
  id: number | string;
  actor: string;
  /** mention · reply · turn · status · resolved · decision (the standard's
   *  vocabulary, 04 §5). Only mention and turn are "needs you". */
  kind: string;
  context_label?: string | null;
  category?: string | null;
  body?: string | null;
  url: string;
  at: string;
  read: boolean;
  resolved?: boolean;
};

const KIND_SENTENCE: Record<string, string> = {
  mention: "mentioned you",
  reply: "replied in a thread",
  turn: "needs you",
  status: "made a change",
  resolved: "resolved a thread",
  // Something was DECIDED — an approval given, a direction picked. It closes
  // a loop rather than opening one, so it is news, never "needs you".
  decision: "made a decision",
};

export function kindSentence(kind: string): string {
  return KIND_SENTENCE[kind] ?? "notified you";
}

export function Bell({
  items, unread, needsYou, onOpen, onMarkAll, categoryTone, showResolvedToggle,
  extraHead,
}: {
  items: BellItem[];
  unread: number;
  needsYou: boolean;
  onOpen: (item: BellItem) => void;
  onMarkAll?: () => void;
  /** Map a category to a CSS colour TOKEN expression (e.g. "var(--cat-x)"). */
  categoryTone?: (category: string) => string | undefined;
  showResolvedToggle?: boolean;
  extraHead?: ReactNode;
}) {
  const [open, setOpen] = useState(false);
  const [showResolved, setShowResolved] = useState(false);
  const visible = showResolvedToggle && !showResolved
    ? items.filter((n) => !n.resolved) : items;

  return (
    <div className="cu-bellwrap">
      <button
        type="button"
        className={`cu-bell ${needsYou ? "is-needs-you" : unread ? "is-unread" : ""}`}
        aria-label={unread ? `${unread} unread notifications` : "Notifications"}
        aria-expanded={open}
        onClick={() => setOpen((v) => !v)}
      >
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none"
             stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"
             strokeLinejoin="round" aria-hidden="true">
          <path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9" />
          <path d="M13.7 21a2 2 0 0 1-3.4 0" />
        </svg>
        {unread > 0 && (
          <span className="cu-bell-count">{unread > 99 ? "99+" : unread}</span>
        )}
      </button>

      {open && (
        <>
          <button type="button" className="cu-bell-scrim" aria-label="Close"
                  onClick={() => setOpen(false)} />
          <div className="cu-bell-menu" role="menu">
            <div className="cu-bell-head">
              <span>Notifications</span>
              <span className="cu-bell-head-right">
                {extraHead}
                {showResolvedToggle && (
                  <label className="cu-bell-toggle">
                    <input type="checkbox" checked={showResolved}
                           onChange={(e) => setShowResolved(e.target.checked)} />
                    Show resolved
                  </label>
                )}
                {unread > 0 && onMarkAll && (
                  <button type="button" className="cu-bell-link" onClick={onMarkAll}>
                    Mark all read
                  </button>
                )}
              </span>
            </div>
            <div className="cu-bell-list">
              {visible.length === 0 && (
                <div className="cu-bell-empty">
                  {items.length === 0 ? "Nothing yet." : "Nothing unresolved."}
                </div>
              )}
              {visible.map((n) => (
                <button key={n.id} type="button"
                        className={`cu-bell-row ${n.read ? "" : "is-unread"}`}
                        onClick={() => { setOpen(false); onOpen(n); }}>
                  <span className="cu-bell-dot" aria-hidden="true" />
                  <span className="cu-bell-main">
                    <span className="cu-bell-line">
                      <b>{n.actor}</b>{" "}
                      <span className="cu-bell-kind">{kindSentence(n.kind)}</span>
                    </span>
                    {n.context_label && (
                      <span className="cu-bell-on">on <b>{n.context_label}</b></span>
                    )}
                    {(n.category || n.resolved) && (
                      <span className="cu-bell-chips">
                        {n.category && (
                          <StatusChip label={n.category}
                                      tone={categoryTone?.(n.category)} />
                        )}
                        {n.resolved && <StatusChip label="Resolved" tone="var(--cu-done)" />}
                      </span>
                    )}
                    {n.body && <span className="cu-bell-preview">{n.body}</span>}
                    <span className="cu-bell-when">{relTime(n.at)}</span>
                  </span>
                </button>
              ))}
            </div>
          </div>
        </>
      )}
    </div>
  );
}
