"use client";

import { useCallback, useEffect, useState } from "react";
import { createClient, getBrowserUserId } from "@/lib/supabase/client";
import type { Notification, Profile } from "@/types";

const NOTIFICATIONS_CHANGED_EVENT = "mindavio:notifications-changed";
const NOTIFICATION_POLL_INTERVAL_MS = 10 * 1000;

function getNotificationKey(notification: Notification) {
  if (notification.kind !== "reminder_due") return notification.id;

  const reminderId = notification.details?.reminder_id;
  const dueAt = notification.details?.due_at;

  if (typeof reminderId === "string" && typeof dueAt === "string") {
    return `reminder_due:${reminderId}:${dueAt}`;
  }

  return notification.id;
}

function dedupeNotifications(notifications: Notification[]) {
  const seen = new Set<string>();

  return notifications.filter((notification) => {
    const key = getNotificationKey(notification);
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });
}

export function useNotifications() {
  const [notifications, setNotifications] = useState<Notification[]>([]);
  const [loading, setLoading] = useState(true);
  const [userId, setUserId] = useState<string | null>(null);
  const supabase = createClient();

  useEffect(() => {
    void getBrowserUserId().then(setUserId).catch(() => setUserId(null));
  }, []);

  const fetchNotifications = useCallback(async () => {
    if (!userId) {
      setNotifications([]);
      setLoading(false);
      return;
    }

    setLoading(true);

    const { data, error } = await supabase
      .from("notifications")
      .select("*")
      .eq("user_id", userId)
      .order("created_at", { ascending: false })
      .limit(40);

    if (error) {
      console.error("Failed to load notifications", error);
      setLoading(false);
      return;
    }

    const rawNotifications = dedupeNotifications((data || []) as Notification[]);
    const actorIds = Array.from(
      new Set(rawNotifications.map((notification) => notification.actor_id).filter(Boolean))
    ) as string[];

    let profileMap = new Map<string, Profile>();
    if (actorIds.length > 0) {
      const { data: profilesData } = await supabase
        .from("profiles")
        .select("id, email, full_name, avatar_url, created_at, updated_at")
        .in("id", actorIds);

      profileMap = new Map(
        ((profilesData || []) as Profile[]).map((profile) => [profile.id, profile])
      );
    }

    setNotifications(
      rawNotifications.map((notification) => ({
        ...notification,
        actor: notification.actor_id
          ? profileMap.get(notification.actor_id) || null
          : null,
      }))
    );
    setLoading(false);
  }, [supabase, userId]);

  useEffect(() => {
    const timer = window.setTimeout(() => {
      void fetchNotifications();
    }, 0);

    return () => window.clearTimeout(timer);
  }, [fetchNotifications]);

  useEffect(() => {
    if (!userId) return;

    const interval = window.setInterval(() => {
      void fetchNotifications();
    }, NOTIFICATION_POLL_INTERVAL_MS);

    return () => window.clearInterval(interval);
  }, [fetchNotifications, userId]);

  useEffect(() => {
    if (!userId) return;

    function handleNotificationsChanged() {
      void fetchNotifications();
    }

    window.addEventListener(NOTIFICATIONS_CHANGED_EVENT, handleNotificationsChanged);
    return () => {
      window.removeEventListener(NOTIFICATIONS_CHANGED_EVENT, handleNotificationsChanged);
    };
  }, [fetchNotifications, userId]);

  useEffect(() => {
    if (!userId) return;

    const channelName = `notifications:${userId}:${Math.random()
      .toString(36)
      .slice(2, 8)}`;
    const channel = supabase
      .channel(channelName)
      .on(
        "postgres_changes",
        {
          event: "*",
          schema: "public",
          table: "notifications",
          filter: `user_id=eq.${userId}`,
        },
        () => {
          void fetchNotifications();
        }
      )
      .subscribe();

    return () => {
      void supabase.removeChannel(channel);
    };
  }, [fetchNotifications, supabase, userId]);

  async function markAsRead(id: string) {
    const { error } = await supabase
      .from("notifications")
      .update({ is_read: true })
      .eq("id", id);
    if (error) throw error;
    setNotifications((prev) =>
      prev.map((notification) =>
        notification.id === id
          ? { ...notification, is_read: true }
          : notification
      )
    );
  }

  async function markAllAsRead() {
    if (!userId) return;

    const unreadIds = notifications
      .filter((notification) => !notification.is_read)
      .map((notification) => notification.id);

    if (unreadIds.length === 0) return;

    const { error } = await supabase
      .from("notifications")
      .update({ is_read: true })
      .in("id", unreadIds);

    if (error) throw error;

    setNotifications((prev) =>
      prev.map((notification) => ({ ...notification, is_read: true }))
    );
  }

  const unreadCount = notifications.filter((notification) => !notification.is_read).length;

  return {
    notifications,
    unreadCount,
    loading,
    markAsRead,
    markAllAsRead,
    refetch: fetchNotifications,
  };
}
