"use client";

/**
 * 通知 WebSocket Hook（ai14，P5）
 *
 * - 连接 push-gateway：ws://localhost:8081/ws（NEXT_PUBLIC_PUSH_GATEWAY_URL）
 * - 指数退避重连：1s → 2s → 4s → 8s → … 上限 30s
 * - 收到消息：派发 CustomEvent 通知页面重新查询 + BroadcastChannel 跨标签广播
 * - Mock 模式（NEXT_PUBLIC_API_MOCKING=enabled）：使用 mock-socket 模拟推送
 * - 返回：{ isConnected, lastMessage, reconnect }
 *
 * 注意：urql 不支持 react-query 的 invalidateQueries，改用 CustomEvent 解耦。
 * 通知页面监听 'edu-notifications-updated' 事件后调用 reexecute()。
 */

import { useCallback, useEffect, useRef, useState } from "react";

/** 通知更新事件名（供页面监听） */
export const NOTIFICATION_UPDATED_EVENT = "edu-notifications-updated";

export interface NotificationMessage {
  type: string;
  payload: Record<string, unknown>;
}

export interface UseNotificationsWebSocketResult {
  isConnected: boolean;
  lastMessage: NotificationMessage | null;
  reconnect: () => void;
}

const CHANNEL_NAME = "edu-notification";
const MAX_DELAY_MS = 30000;

export function useNotificationsWebSocket(): UseNotificationsWebSocketResult {
  const [isConnected, setIsConnected] = useState(false);
  const [lastMessage, setLastMessage] = useState<NotificationMessage | null>(
    null,
  );
  const wsRef = useRef<WebSocket | null>(null);
  const retryRef = useRef(0);
  const reconnectRef = useRef<() => void>(() => {});

  useEffect(() => {
    const base =
      process.env.NEXT_PUBLIC_PUSH_GATEWAY_URL ?? "ws://localhost:8081";
    const url = `${base}/ws`;
    const channel =
      typeof BroadcastChannel !== "undefined"
        ? new BroadcastChannel(CHANNEL_NAME)
        : null;

    const notifyUpdate = (): void => {
      if (typeof window === "undefined") return;
      window.dispatchEvent(new CustomEvent(NOTIFICATION_UPDATED_EVENT));
    };

    const onMessage = (raw: string): void => {
      let msg: NotificationMessage;
      try {
        msg = JSON.parse(raw) as NotificationMessage;
      } catch {
        return;
      }
      setLastMessage(msg);
      notifyUpdate();
      channel?.postMessage(msg);
    };

    const connect = (): void => {
      const ws = new WebSocket(url);
      wsRef.current = ws;
      ws.onopen = () => {
        retryRef.current = 0;
        setIsConnected(true);
      };
      ws.onmessage = (e: MessageEvent) => {
        const raw = typeof e.data === "string" ? e.data : "";
        if (raw) onMessage(raw);
      };
      ws.onclose = () => {
        setIsConnected(false);
        const delay = Math.min(1000 * 2 ** retryRef.current, MAX_DELAY_MS);
        retryRef.current += 1;
        connectTimer = setTimeout(connect, delay);
      };
      ws.onerror = () => ws.close();
    };

    let connectTimer: ReturnType<typeof setTimeout>;
    let mockServer: { stop: () => void } | null = null;

    // 跨标签广播监听：其他标签的 WebSocket 消息也会触发本标签更新
    const onChannelMessage = (): void => {
      notifyUpdate();
    };
    channel?.addEventListener("message", onChannelMessage);

    if (process.env.NEXT_PUBLIC_API_MOCKING === "enabled") {
      // Mock 模式：mock-socket 拦截 WebSocket，周期性推送通知
      void import("mock-socket").then(({ Server }) => {
        const server = new Server(url);
        server.on("connection", (socket) => {
          const timer = setInterval(() => {
            socket.send(
              JSON.stringify({
                type: "NotificationRequested",
                payload: { ts: Date.now() },
              }),
            );
          }, 60000);
          socket.on("close", () => clearInterval(timer));
        });
        mockServer = server;
        connect();
      });
    } else {
      connect();
    }

    reconnectRef.current = (): void => {
      retryRef.current = 0;
      clearTimeout(connectTimer);
      wsRef.current?.close();
    };

    return () => {
      clearTimeout(connectTimer);
      mockServer?.stop();
      wsRef.current?.close();
      channel?.removeEventListener("message", onChannelMessage);
      channel?.close();
    };
  }, []);

  const reconnect = useCallback((): void => {
    reconnectRef.current();
  }, []);

  return { isConnected, lastMessage, reconnect };
}
