import { useState, useEffect, useCallback } from "react";
import {
  getAllNotification,
  getUnSeenNotification,
  readNotification,
} from "@/shared/services/notificationService";

export interface Notification {
  id: string;
  createdAt: string;
  message: string;
  isSeen: boolean;
  subject: { en: string };
  itemId: string;
  updatedAt: string;
  metadata: {
    route: string;
    recordType: string;
  };
}

interface Params {
  take: number;
  skip: number;
  orderBy: string;
}

const SOCKET_NOTIFICATION_EVENT = "record-management:notification-received";

const mergeUniqueById = (items: Notification[]): Notification[] => {
  const seen = new Set<string>();
  return items.filter((item) => {
    if (!item?.id || seen.has(item.id)) return false;
    seen.add(item.id);
    return true;
  });
};

const normalizeNotification = (raw: any): Notification | null => {
  if (!raw || !raw.message) return null;
  const now = new Date().toISOString();
  const subject =
    typeof raw.subject === "object" && raw.subject !== null
      ? raw.subject
      : { en: String(raw.subject || "Notification") };
  const metadata =
    typeof raw.metadata === "object" && raw.metadata !== null
      ? {
          route: String(raw.metadata.route || ""),
          recordType: String(raw.metadata.recordType || ""),
        }
      : { route: "", recordType: "" };

  return {
    id: String(raw.id || raw.itemId || `rt-${Date.now()}`),
    createdAt: String(raw.createdAt || raw.timestamp || now),
    updatedAt: String(raw.updatedAt || raw.createdAt || raw.timestamp || now),
    message: String(raw.message),
    isSeen: Boolean(raw.isSeen),
    subject: { en: String(subject.en || "Notification") },
    itemId: String(raw.itemId || raw.id || ""),
    metadata,
  };
};

export const useNotifications = (
  initialParams: Params,
  externalSetUnseenCount?: (count: number) => void,
) => {
  const [notifications, setNotifications] = useState<Notification[]>([]);
  const [unseenCount, setUnseenCount] = useState(0);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [hasMore, setHasMore] = useState(true);
  const [params, setParams] = useState<Params>(initialParams);

  useEffect(() => {
    const newUnseenCount = notifications.filter((n) => !n.isSeen).length;
    setUnseenCount(newUnseenCount);
    if (externalSetUnseenCount) externalSetUnseenCount(newUnseenCount);
  }, [notifications, externalSetUnseenCount]);

  const fetchNotifications = useCallback(
    async (newParams: Params, reset = false) => {
      if (!reset && !hasMore) return;

      setLoading(true);
      setError(null);
      try {
        const response = await getAllNotification({
          ...newParams,
          orderBy: "createdAt:DESC",
        });

        // ✅ Normalize items safely
        const items: Notification[] = Array.isArray(response.data?.items)
          ? response.data.items
          : Array.isArray(response.data)
            ? response.data
            : [];

        setNotifications((prev) =>
          reset ? mergeUniqueById(items) : mergeUniqueById([...prev, ...items]),
        );

        setHasMore(items.length === newParams.take);
      } catch (err: any) {
        setError(err.message || "Failed to fetch notifications");
      } finally {
        setLoading(false);
      }
    },
    [hasMore],
  );

  const fetchUnseenCount = useCallback(async () => {
    try {
      const response = await getUnSeenNotification({
        take: 1,
        skip: 0,
        orderBy: "createdAt:DESC",
      });

      // Use the total count from the paginated response directly
      const count =
        response.data?.count ??
        response.data?.total ??
        (Array.isArray(response.data?.items)
          ? response.data.items.length
          : Array.isArray(response.data)
            ? response.data.length
            : 0);

      setUnseenCount(count);
      if (externalSetUnseenCount) {
        externalSetUnseenCount(count);
      }
    } catch (err) {
      console.error("Error fetching unseen count", err);
    }
  }, [externalSetUnseenCount]);

  const markAsRead = useCallback(
    async (id: string) => {
      try {
        await readNotification(id);
        setNotifications((prev) =>
          prev.map((n) => (n.id === id ? { ...n, isSeen: true } : n)),
        );
        setUnseenCount((prev) => (prev > 0 ? prev - 1 : 0));
        fetchUnseenCount();
      } catch (err) {
        console.error("Error marking as read", err);
      }
    },
    [fetchUnseenCount],
  );

  const refresh = useCallback(() => {
    setParams(initialParams);
    fetchNotifications(initialParams, true);
    fetchUnseenCount();
  }, [initialParams, fetchNotifications, fetchUnseenCount]);

  useEffect(() => {
    fetchNotifications(params);
  }, [params, fetchNotifications]);

  useEffect(() => {
    fetchNotifications(initialParams, true);
    fetchUnseenCount();
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  // Poll unseen count every 30 s so the badge stays fresh without a full refresh
  useEffect(() => {
    const id = setInterval(fetchUnseenCount, 30_000);
    return () => clearInterval(id);
  }, [fetchUnseenCount]);

  useEffect(() => {
    const handleSocketNotification = (event: Event) => {
      const payload = (event as CustomEvent).detail;
      const normalized = normalizeNotification(payload);
      if (!normalized) return;
      setNotifications((prev) => mergeUniqueById([normalized, ...prev]));
      // New real-time notification is always unseen — bump the badge immediately
      if (!normalized.isSeen) {
        setUnseenCount((prev) => {
          const next = prev + 1;
          if (externalSetUnseenCount) externalSetUnseenCount(next);
          return next;
        });
      }
    };

    window.addEventListener(
      SOCKET_NOTIFICATION_EVENT,
      handleSocketNotification as EventListener,
    );
    return () => {
      window.removeEventListener(
        SOCKET_NOTIFICATION_EVENT,
        handleSocketNotification as EventListener,
      );
    };
  }, []);

  return {
    notifications,
    unseenCount,
    loading,
    error,
    hasMore,
    markAsRead,
    refresh,
    setParams,
  };
};
