import type { NotificationDto, NotificationListResult } from "@edr/types";
import {
  NotificationBell,
  NotificationDrawer,
  NotificationToast,
  type NotificationItemData,
} from "@edr/ui-common";
import { useState } from "react";
import toast from "react-hot-toast";
import { useNavigate } from "react-router-dom";

import {
  resolveNotificationHref,
  resolveNotificationVisual,
} from "./notificationConfig";
import {
  useInfiniteNotifications,
  useMarkAllRead,
  useMarkRead,
  useUnreadCount,
} from "./useNotifications";
import { useNotificationSocket } from "./useNotificationSocket";

/** Map a server notification into the shared presentational item shape. */
function toItem(n: NotificationDto): NotificationItemData {
  return {
    id: n.id,
    type: n.type,
    title: n.title,
    body: n.body,
    createdAt: n.createdAt,
    isRead: n.isRead,
    priority: n.priority,
    link: n.link,
    data: n.data,
  };
}

/** Flatten an infinite query's pages into the drawer's item shape. */
function toItems(
  data: { pages: NotificationListResult[] } | undefined,
): NotificationItemData[] {
  return (data?.pages ?? []).flatMap((p) => p.items).map(toItem);
}

/**
 * Wires react-query (infinite unread/read lists) + the notification WebSocket
 * into the shared bell + drawer. Lists are only fetched while the drawer is
 * open; the badge is driven by the lightweight unread-count query + socket.
 */
export default function NotificationBellContainer({
  enabled = true,
}: {
  enabled?: boolean;
}) {
  const navigate = useNavigate();
  const [opened, setOpened] = useState(false);

  const unreadQ = useInfiniteNotifications(false, enabled && opened);
  const readQ = useInfiniteNotifications(true, enabled && opened);
  const unread = useUnreadCount(enabled);
  const markRead = useMarkRead();
  const markAllRead = useMarkAllRead();

  const unreadItems = toItems(unreadQ.data);
  const readItems = toItems(readQ.data);
  const unreadCount = unread.data ?? 0;

  const handleItemClick = (item: NotificationItemData) => {
    if (!item.isRead) markRead.mutate(item.id);
    const href = resolveNotificationHref(item);
    setOpened(false);
    if (href) navigate(href);
  };

  // Live push → rich toast that reuses the same registry + click action.
  useNotificationSocket(enabled, (n) => {
    const item = toItem(n);
    toast.custom(
      (t) => (
        <NotificationToast
          item={item}
          visual={resolveNotificationVisual(item)}
          visible={t.visible}
          onClick={() => {
            toast.dismiss(t.id);
            handleItemClick(item);
          }}
          onDismiss={() => toast.dismiss(t.id)}
        />
      ),
      { duration: 6000 },
    );
  });

  return (
    <>
      <NotificationBell
        unreadCount={unreadCount}
        onClick={() => setOpened(true)}
      />
      <NotificationDrawer
        opened={opened}
        onClose={() => setOpened(false)}
        unread={unreadItems}
        read={readItems}
        unreadCount={unreadCount}
        loading={opened && (unreadQ.isLoading || readQ.isLoading)}
        hasMoreUnread={unreadQ.hasNextPage}
        hasMoreRead={readQ.hasNextPage}
        loadingMoreUnread={unreadQ.isFetchingNextPage}
        loadingMoreRead={readQ.isFetchingNextPage}
        onLoadMoreUnread={() => {
          if (unreadQ.hasNextPage && !unreadQ.isFetchingNextPage) {
            void unreadQ.fetchNextPage();
          }
        }}
        onLoadMoreRead={() => {
          if (readQ.hasNextPage && !readQ.isFetchingNextPage) {
            void readQ.fetchNextPage();
          }
        }}
        onItemClick={handleItemClick}
        onMarkAllRead={() => markAllRead.mutate()}
        resolveVisual={resolveNotificationVisual}
      />
    </>
  );
}
