"use client";

import * as React from "react";
import { measureElement, useVirtualizer } from "@tanstack/react-virtual";
import { formatDistanceToNow } from "date-fns";
import { Bell, CheckCheck, KeyRound, MessageSquare, Settings, ShieldAlert, TrendingUp } from "lucide-react";
import type { ApiNotification } from "@/api/notifications/types";
import { apiBaseUrl } from "@/api/client";
import { sanitizeNoteHtml } from "@/components/deals/deal-note-composer";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import {
  formatNotificationTextWithStageName,
} from "@/lib/notification-message";
import { cn } from "@/lib/utils";

const TYPE_ICONS: Record<string, React.ReactNode> = {
  security: <ShieldAlert size={16} className="text-amber-500" />,
  info: <Bell size={16} className="text-accent" />,
  success: <CheckCheck size={16} className="text-green-500" />,
  warning: <ShieldAlert size={16} className="text-orange-500" />,
  system: <Settings size={16} className="text-gray-500" />,
  mention: <MessageSquare size={16} className="text-blue-500" />,
  update: <TrendingUp size={16} className="text-accent" />,
  reminder: <Bell size={16} className="text-amber-500" />,
};

const NOTIFICATION_ROW_GAP = 10;
const NOTIFICATION_ROW_ESTIMATE = 132;

function getTypeIcon(type: string) {
  return TYPE_ICONS[type] ?? <Bell size={16} className="text-accent" />;
}

function getNotificationId(notif: ApiNotification): string {
  return notif.id ?? (notif as { _id?: string })._id ?? "";
}

function formatNotificationMessageHtml(
  message: string,
  stageNameMap: Map<string, string>,
): string {
  const formattedMessage = formatNotificationTextWithStageName(message, stageNameMap);
  const sanitized = sanitizeNoteHtml(formattedMessage);
  const base = apiBaseUrl.replace(/\/$/, "");
  return sanitized.replace(/src="(\/[^"]+)"/g, (_, path) => `src="${base}${path}"`);
}

type NotificationRowProps = {
  notif: ApiNotification;
  stageNameMap: Map<string, string>;
  canUpdateUsers: boolean;
  onMarkAsRead: (id: string) => void;
  onOpenSetPassword: (notif: ApiNotification) => void;
};

const NotificationRow = React.memo(function NotificationRow({
  notif,
  stageNameMap,
  canUpdateUsers,
  onMarkAsRead,
  onOpenSetPassword,
}: NotificationRowProps) {
  const notificationId = getNotificationId(notif);
  const isPasswordRequest = notif.metadata?.category === "password_change_request";
  const hasHtmlMessage = /<\/?(\w+)[^>]*>/.test(notif.message ?? "");

  return (
    <Card
      className={cn(
        "gap-0 p-4 transition-all group relative",
        !notif.isRead && "border-accent/10",
      )}
    >
      <div className="flex items-start gap-4">
        <div className="relative shrink-0">
          <div className="size-10 rounded-full bg-accent/10 flex items-center justify-center text-accent">
            {getTypeIcon(notif.type)}
          </div>
        </div>
        <div className="flex-1 min-w-0 pr-8">
          <div className="flex items-center gap-2 mb-0.5">
            <h4
              className={cn(
                "text-[14px] font-['Lexend'] truncate",
                notif.isRead ? "font-semibold" : "font-bold",
              )}
            >
              {formatNotificationTextWithStageName(notif.title, stageNameMap)}
            </h4>
            {!notif.isRead && (
              <div className="size-2 bg-accent rounded-full shrink-0" />
            )}
          </div>
          {hasHtmlMessage ? (
            <div
              className="text-[12px] text-gray-700 leading-relaxed prose prose-p:my-0.5 prose-img:rounded-lg prose-img:max-h-[120px] prose-img:object-cover [&_img]:max-h-[120px] [&_img]:rounded-lg [&_img]:border [&_img]:border-gray-200"
              dangerouslySetInnerHTML={{
                __html: formatNotificationMessageHtml(notif.message ?? "", stageNameMap),
              }}
            />
          ) : (
            <p className="text-[12px] text-gray-700 line-clamp-2 leading-relaxed">
              {formatNotificationTextWithStageName(notif.message, stageNameMap)}
            </p>
          )}
          <div className="flex items-center gap-3 mt-2 flex-wrap">
            <span className="text-[10px] font-bold uppercase tracking-widest text-gray-500">
              {notif.createdAt
                ? formatDistanceToNow(new Date(notif.createdAt), { addSuffix: true })
                : ""}
            </span>
            {isPasswordRequest && canUpdateUsers && (
              <Button
                variant="outlineToolbarMini"
                onClick={() => onOpenSetPassword(notif)}
              >
                <KeyRound size={12} />
                Update password
              </Button>
            )}
          </div>
        </div>
        <div className="absolute right-4 top-4 flex items-center gap-1 opacity-100">
          {!notif.isRead && (
            <Button
              variant="ghostTableIcon"
              onClick={() => onMarkAsRead(notificationId)}
            >
              <CheckCheck size={14} />
            </Button>
          )}
        </div>
      </div>
    </Card>
  );
});

export type NotificationsVirtualListProps = {
  notifications: ApiNotification[];
  stageNameMap: Map<string, string>;
  canUpdateUsers: boolean;
  onMarkAsRead: (id: string) => void;
  onOpenSetPassword: (notif: ApiNotification) => void;
  className?: string;
};

export function NotificationsVirtualList({
  notifications,
  stageNameMap,
  canUpdateUsers,
  onMarkAsRead,
  onOpenSetPassword,
  className,
}: NotificationsVirtualListProps) {
  const parentRef = React.useRef<HTMLDivElement>(null);

  // eslint-disable-next-line react-hooks/incompatible-library -- TanStack Virtual
  const virtualizer = useVirtualizer({
    count: notifications.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => NOTIFICATION_ROW_ESTIMATE,
    gap: NOTIFICATION_ROW_GAP,
    overscan: 5,
    getItemKey: (index) => getNotificationId(notifications[index]!) || index,
    measureElement,
  });

  React.useLayoutEffect(() => {
    virtualizer.measure();
    const id = requestAnimationFrame(() => virtualizer.measure());
    return () => cancelAnimationFrame(id);
  }, [notifications.length, virtualizer]);

  return (
    <div
      ref={parentRef}
      className={cn(
        "min-h-0 overflow-y-auto overflow-x-hidden pr-1 scrollbar-themed",
        className,
      )}
    >
      <div
        className="relative w-full"
        style={{ height: `${virtualizer.getTotalSize()}px` }}
      >
        {virtualizer.getVirtualItems().map((virtualRow) => {
          const notif = notifications[virtualRow.index];
          if (!notif) return null;

          return (
            <div
              key={virtualRow.key}
              data-index={virtualRow.index}
              ref={virtualizer.measureElement}
              className="left-0 w-full"
              style={{
                position: "absolute",
                top: 0,
                transform: `translateY(${virtualRow.start}px)`,
              }}
            >
              <NotificationRow
                notif={notif}
                stageNameMap={stageNameMap}
                canUpdateUsers={canUpdateUsers}
                onMarkAsRead={onMarkAsRead}
                onOpenSetPassword={onOpenSetPassword}
              />
            </div>
          );
        })}
      </div>
    </div>
  );
}
