import {
  ActionIcon,
  Badge,
  Box,
  Button,
  Drawer,
  Group,
  Loader,
  ScrollArea,
  Stack,
  Text,
  ThemeIcon,
} from "@mantine/core";
import { Bell, CheckCheck, Inbox, X } from "lucide-react";
import { useEffect, useRef, useState } from "react";

import { NotificationItem } from "./NotificationItem";
import { edr } from "./PriorityTag";
import type {
  NotificationItemData,
  ResolveNotificationVisual,
} from "./types";

export interface NotificationDrawerProps {
  opened: boolean;
  onClose: () => void;
  /** Unread items (newest first), already flattened across pages. */
  unread: NotificationItemData[];
  /** Read items (newest first), already flattened across pages. */
  read: NotificationItemData[];
  unreadCount?: number;
  /** Initial load spinner (before any page resolves). */
  loading?: boolean;
  hasMoreUnread?: boolean;
  hasMoreRead?: boolean;
  loadingMoreUnread?: boolean;
  loadingMoreRead?: boolean;
  onLoadMoreUnread?: () => void;
  onLoadMoreRead?: () => void;
  onItemClick?: (item: NotificationItemData) => void;
  onMarkAllRead?: () => void;
  /** App-owned registry: `item` → icon + color. */
  resolveVisual?: ResolveNotificationVisual;
  emptyLabel?: string;
  title?: string;
  width?: number;
}

/** Fires `onVisible` when it scrolls into view within `root`. Infinite-scroll trigger. */
function Sentinel({
  root,
  onVisible,
}: {
  root: HTMLElement | null;
  onVisible: () => void;
}) {
  const ref = useRef<HTMLDivElement>(null);
  const cb = useRef(onVisible);
  cb.current = onVisible;

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const obs = new IntersectionObserver(
      (entries) => {
        if (entries.some((e) => e.isIntersecting)) cb.current();
      },
      { root, rootMargin: "160px" },
    );
    obs.observe(el);
    return () => obs.disconnect();
  }, [root]);

  return <div ref={ref} style={{ height: 1 }} aria-hidden />;
}

function SectionLabel({
  label,
  count,
}: {
  label: string;
  count?: number;
}) {
  return (
    <Group
      gap={8}
      px="md"
      py={8}
      wrap="nowrap"
      style={{
        position: "sticky",
        top: 0,
        zIndex: 1,
        background: edr.card,
        borderBottom: `1px solid ${edr.border}`,
      }}
    >
      <Text
        size="xs"
        fw={700}
        tt="uppercase"
        style={{ color: edr.muted, letterSpacing: 0.6 }}
      >
        {label}
      </Text>
      {typeof count === "number" && count > 0 && (
        <Text size="xs" fw={600} style={{ color: edr.muted }}>
          {count}
        </Text>
      )}
    </Group>
  );
}

function LoadingRow() {
  return (
    <Group justify="center" py="md">
      <Loader size="xs" color="edr-green" />
    </Group>
  );
}

/**
 * Right slide-in notification center, styled to the EDR design system: a
 * branded header, an "Unread" section over an "Earlier" (read) section, each
 * with its own infinite-scroll trigger in one scroll surface. Presentational —
 * the host wires data, paging, and the visual registry.
 */
export function NotificationDrawer({
  opened,
  onClose,
  unread,
  read,
  unreadCount,
  loading = false,
  hasMoreUnread = false,
  hasMoreRead = false,
  loadingMoreUnread = false,
  loadingMoreRead = false,
  onLoadMoreUnread,
  onLoadMoreRead,
  onItemClick,
  onMarkAllRead,
  resolveVisual,
  emptyLabel = "You're all caught up",
  title = "Notifications",
  width = 424,
}: NotificationDrawerProps) {
  const [viewport, setViewport] = useState<HTMLElement | null>(null);
  const viewportRef = useRef<HTMLDivElement>(null);

  // Capture the scroll viewport so the sentinels observe within it, not the page.
  useEffect(() => {
    if (opened) setViewport(viewportRef.current);
  }, [opened]);

  const totalUnread = unreadCount ?? unread.length;
  const isEmpty = !loading && unread.length === 0 && read.length === 0;

  const renderItem = (item: NotificationItemData) => (
    <NotificationItem
      key={item.id}
      item={item}
      visual={resolveVisual?.(item)}
      onClick={onItemClick}
    />
  );

  return (
    <Drawer
      opened={opened}
      onClose={onClose}
      position="right"
      size={width}
      padding={0}
      withCloseButton={false}
      overlayProps={{ backgroundOpacity: 0.45, blur: 3 }}
      transitionProps={{ transition: "slide-left", duration: 220 }}
      styles={{
        content: {
          display: "flex",
          flexDirection: "column",
          background: edr.card,
        },
        body: {
          flex: 1,
          minHeight: 0,
          display: "flex",
          flexDirection: "column",
          padding: 0,
        },
      }}
    >
      <Group
        justify="space-between"
        px="md"
        py="md"
        wrap="nowrap"
        style={{ borderBottom: `1px solid ${edr.border}` }}
      >
        <Group gap="sm" wrap="nowrap">
          <ThemeIcon variant="light" color="edr-green" radius="md" size={36}>
            <Bell size={18} strokeWidth={2} />
          </ThemeIcon>
          <Box>
            <Text fw={700} size="md" style={{ color: edr.text, lineHeight: 1.2 }}>
              {title}
            </Text>
            <Text size="xs" style={{ color: edr.muted }}>
              {totalUnread > 0 ? `${totalUnread} unread` : "All caught up"}
            </Text>
          </Box>
          {totalUnread > 0 && (
            <Badge color="red" variant="filled" size="sm" radius="xl">
              {totalUnread > 99 ? "99+" : totalUnread}
            </Badge>
          )}
        </Group>
        <Group gap={4} wrap="nowrap">
          {totalUnread > 0 && onMarkAllRead && (
            <Button
              variant="subtle"
              size="compact-xs"
              color="edr-green"
              leftSection={<CheckCheck size={13} />}
              onClick={onMarkAllRead}
            >
              Mark all read
            </Button>
          )}
          <ActionIcon
            variant="subtle"
            color="gray"
            radius="xl"
            size={32}
            aria-label="Close notifications"
            onClick={onClose}
          >
            <X size={18} />
          </ActionIcon>
        </Group>
      </Group>

      <ScrollArea
        type="hover"
        viewportRef={viewportRef}
        style={{ flex: 1, minHeight: 0 }}
      >
        {loading && unread.length === 0 && read.length === 0 ? (
          <LoadingRow />
        ) : isEmpty ? (
          <Stack align="center" gap="sm" py={72} px="lg">
            <ThemeIcon variant="light" color="edr-green" radius="xl" size={56}>
              <Inbox size={26} strokeWidth={1.6} />
            </ThemeIcon>
            <Text size="sm" fw={600} style={{ color: edr.text }}>
              {emptyLabel}
            </Text>
            <Text size="xs" ta="center" style={{ color: edr.muted, maxWidth: 240 }}>
              New notifications about your bookings, clearances and invoices will
              show up here.
            </Text>
          </Stack>
        ) : (
          <Box pb="md">
            {unread.length > 0 && (
              <>
                <SectionLabel label="Unread" count={totalUnread} />
                <Stack gap={0}>{unread.map(renderItem)}</Stack>
                {hasMoreUnread && (
                  <Sentinel
                    root={viewport}
                    onVisible={() => onLoadMoreUnread?.()}
                  />
                )}
                {loadingMoreUnread && <LoadingRow />}
              </>
            )}

            {read.length > 0 && (
              <>
                <SectionLabel label="Earlier" />
                <Stack gap={0}>{read.map(renderItem)}</Stack>
                {hasMoreRead && (
                  <Sentinel
                    root={viewport}
                    onVisible={() => onLoadMoreRead?.()}
                  />
                )}
                {loadingMoreRead && <LoadingRow />}
              </>
            )}
          </Box>
        )}
      </ScrollArea>
    </Drawer>
  );
}

export default NotificationDrawer;
