// @File: src/components/notifications/NotificationBell.tsx
// @Desc: Notification bell dropdown for the client dashboard header.
//        - Polls recent notifications on mount
//        - Shows an unread badge
//        - Opens a dropdown list of recent events
//        - Allows marking all as read
//        - Kicks off a server-side ticket-event sync on first open
//          so new replies/status changes are surfaced even without polling.
import { useCallback, useEffect, useRef, useState } from "react";
import { Bell, Check, Loader2 } from "lucide-react";
import {
  listNotifications,
  markAllNotificationsRead,
  markNotificationRead,
  syncTicketNotifications,
  type NotificationRow,
} from "@/lib/notifications";

interface NotificationBellProps {
  /** Called after user clicks a ticket notification. Receives the ticket id. */
  onOpenTicket?: (ticketId: string) => void;
  /** Poll interval in ms. 0 disables polling. */
  pollMs?: number;
}

export default function NotificationBell({
  onOpenTicket,
  pollMs = 45000,
}: NotificationBellProps) {
  const [open, setOpen] = useState(false);
  const [items, setItems] = useState<NotificationRow[]>([]);
  const [unread, setUnread] = useState(0);
  const [loading, setLoading] = useState(false);
  const [synced, setSynced] = useState(false);
  const wrapperRef = useRef<HTMLDivElement | null>(null);

  const refresh = useCallback(async () => {
    setLoading(true);
    try {
      const r = await listNotifications(30);
      setItems(r.items);
      setUnread(r.unread);
    } finally {
      setLoading(false);
    }
  }, []);

  // Initial load.
  useEffect(() => {
    refresh();
  }, [refresh]);

  // Polling.
  useEffect(() => {
    if (!pollMs) return;
    const t = window.setInterval(() => {
      refresh();
    }, pollMs);
    return () => window.clearInterval(t);
  }, [pollMs, refresh]);

  // Close on outside click.
  useEffect(() => {
    if (!open) return;
    const onDocClick = (e: MouseEvent) => {
      if (!wrapperRef.current) return;
      if (!wrapperRef.current.contains(e.target as Node)) setOpen(false);
    };
    document.addEventListener("mousedown", onDocClick);
    return () => document.removeEventListener("mousedown", onDocClick);
  }, [open]);

  // On first open, do a best-effort server-side sync so the list reflects
  // any new WHMCS activity since the last visit.
  const handleOpen = useCallback(async () => {
    const willOpen = !open;
    setOpen(willOpen);
    if (willOpen && !synced) {
      setSynced(true);
      try {
        const s = await syncTicketNotifications();
        if (s && s.created > 0) await refresh();
      } catch {
        // ignore
      }
    }
  }, [open, synced, refresh]);

  const handleMarkAll = useCallback(async () => {
    const unreadIds = items.filter((i) => !i.read).map((i) => i.id);
    if (unreadIds.length === 0) return;
    // Optimistic update.
    setItems((prev) => prev.map((i) => (i.read ? i : { ...i, read: true })));
    setUnread(0);
    try {
      await markAllNotificationsRead(unreadIds);
    } catch {
      refresh();
    }
  }, [items, refresh]);

  const handleClick = useCallback(
    async (n: NotificationRow) => {
      if (!n.read) {
        setItems((prev) => prev.map((i) => (i.id === n.id ? { ...i, read: true } : i)));
        setUnread((u) => Math.max(0, u - 1));
        try {
          await markNotificationRead(n.id);
        } catch {
          // ignore
        }
      }
      if (n.ticket_id && onOpenTicket) {
        onOpenTicket(String(n.ticket_id));
        setOpen(false);
      }
    },
    [onOpenTicket],
  );

  const badge = unread > 9 ? "9+" : String(unread);

  return (
    <div ref={wrapperRef} className="relative">
      <button
        type="button"
        onClick={handleOpen}
        className="relative w-10 h-10 rounded-lg hover:bg-slate-100 flex items-center justify-center"
        title="Notifications"
      >
        <Bell className="w-5 h-5 text-slate-600" />
        {unread > 0 ? (
          <span className="absolute -top-0.5 -right-0.5 min-w-[18px] h-[18px] px-1 rounded-full bg-red-500 text-white text-[10px] font-bold flex items-center justify-center">
            {badge}
          </span>
        ) : null}
      </button>

      {open ? (
        <div className="absolute right-0 mt-2 w-[360px] max-w-[90vw] bg-white rounded-xl shadow-xl border border-slate-200 z-50 overflow-hidden">
          <div className="flex items-center justify-between px-4 py-3 border-b border-slate-100">
            <div className="text-sm font-bold text-slate-900">Notifications</div>
            <button
              type="button"
              onClick={handleMarkAll}
              disabled={unread === 0}
              className="inline-flex items-center gap-1 text-[11px] font-bold text-emerald-700 hover:text-emerald-800 disabled:text-slate-400"
            >
              <Check className="w-3 h-3" /> Mark all read
            </button>
          </div>

          <div className="max-h-[380px] overflow-y-auto">
            {loading && items.length === 0 ? (
              <div className="flex items-center justify-center py-6 text-slate-500 text-xs gap-2">
                <Loader2 className="w-3.5 h-3.5 animate-spin" />
                Loading notifications…
              </div>
            ) : items.length === 0 ? (
              <div className="py-8 text-center text-xs text-slate-500 px-6">
                You're all caught up. New ticket replies and status changes will
                appear here.
              </div>
            ) : (
              <ul className="divide-y divide-slate-100">
                {items.map((n) => (
                  <li key={n.id}>
                    <button
                      type="button"
                      onClick={() => handleClick(n)}
                      className={`w-full text-left px-4 py-3 hover:bg-slate-50 transition ${
                        n.read ? "bg-white" : "bg-emerald-50/40"
                      }`}
                    >
                      <div className="flex items-start gap-2">
                        {!n.read ? (
                          <span className="mt-1 w-2 h-2 rounded-full bg-emerald-500 shrink-0" />
                        ) : (
                          <span className="mt-1 w-2 h-2 rounded-full bg-transparent shrink-0" />
                        )}
                        <div className="flex-1 min-w-0">
                          <div className="text-xs font-bold text-slate-900 truncate">
                            {n.title || "(notification)"}
                          </div>
                          {n.body ? (
                            <div className="text-[11px] text-slate-600 line-clamp-2 mt-0.5">
                              {n.body}
                            </div>
                          ) : null}
                          {n.created_at ? (
                            <div className="text-[10px] text-slate-400 mt-1">
                              {formatDate(n.created_at)}
                            </div>
                          ) : null}
                        </div>
                      </div>
                    </button>
                  </li>
                ))}
              </ul>
            )}
          </div>

          <div className="px-4 py-2 border-t border-slate-100 text-[10px] text-slate-400 text-center">
            Notifications are generated from your WHMCS ticket activity.
          </div>
        </div>
      ) : null}
    </div>
  );
}

function formatDate(iso: string): string {
  try {
    const d = new Date(iso);
    if (isNaN(d.getTime())) return iso;
    const now = Date.now();
    const diff = now - d.getTime();
    if (diff < 60_000) return "just now";
    if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`;
    if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`;
    return d.toLocaleDateString();
  } catch {
    return iso;
  }
}