"use client";

/**
 * 通知 WebSocket Hook（P5）
 *
 * 维护者：ai13（teacher-portal）
 * 关联：02-architecture-design.md §5 实时推送（push-gateway）
 *
 * - 连接 push-gateway：ws://localhost:8081/ws
 * - useEffect 建立连接，useRef 持有 WebSocket 实例
 * - 监听消息事件，解析 JSON 后通过 state 通知
 * - 自动重连（指数退避，最多 5 次）
 * - 连接状态枚举：connecting / connected / disconnected / error
 * - Mock 模式（NEXT_PUBLIC_API_MOCKING=enabled）：用 setInterval 每 30s 推送 1 条 mock 通知
 *   （不依赖 mock-socket，避免新增 npm 依赖）
 * - 返回：{ notifications, connectionState, reconnect }
 */

import { useCallback, useEffect, useRef, useState } from "react";
import type { NotificationItem, NotificationType } from "@/lib/graphql-p5";
import { mockNotifications } from "@/mocks/fixtures/notifications";

export type ConnectionState =
  | "connecting"
  | "connected"
  | "disconnected"
  | "error";

export interface UseNotificationsWebSocketResult {
  notifications: NotificationItem[];
  connectionState: ConnectionState;
  reconnect: () => void;
}

const WS_URL = "ws://localhost:8081/ws";
const MAX_RETRIES = 5;
const MAX_DELAY_MS = 30000;
const MOCK_INTERVAL_MS = 30000;

export function useNotificationsWebSocket(): UseNotificationsWebSocketResult {
  const [notifications, setNotifications] = useState<NotificationItem[]>([]);
  const [connectionState, setConnectionState] =
    useState<ConnectionState>("disconnected");

  const wsRef = useRef<WebSocket | null>(null);
  const retryRef = useRef(0);
  const reconnectFnRef = useRef<() => void>(() => {});

  useEffect(() => {
    let reconnectTimer: ReturnType<typeof setTimeout> | undefined;
    let mockTimer: ReturnType<typeof setInterval> | undefined;
    let mockIndex = 0;
    let disposed = false;

    const pushNotification = (item: NotificationItem): void => {
      setNotifications((prev) => {
        if (prev.some((n) => n.id === item.id)) return prev;
        return [item, ...prev];
      });
    };

    const connect = (): void => {
      if (disposed) return;
      setConnectionState("connecting");

      let ws: WebSocket;
      try {
        ws = new WebSocket(WS_URL);
      } catch {
        setConnectionState("error");
        scheduleReconnect();
        return;
      }
      wsRef.current = ws;

      ws.onopen = () => {
        retryRef.current = 0;
        setConnectionState("connected");
      };

      ws.onmessage = (event: MessageEvent) => {
        const raw = typeof event.data === "string" ? event.data : "";
        if (!raw) return;
        try {
          const msg = JSON.parse(raw) as {
            type?: string;
            payload?: NotificationItem;
          };
          const item = msg.payload;
          if (item && item.id) {
            pushNotification(item);
          }
        } catch {
          // 忽略非 JSON 消息
        }
      };

      ws.onerror = () => {
        setConnectionState("error");
      };

      ws.onclose = () => {
        if (disposed) return;
        setConnectionState("disconnected");
        scheduleReconnect();
      };
    };

    const scheduleReconnect = (): void => {
      if (disposed) return;
      if (retryRef.current >= MAX_RETRIES) return;
      const delay = Math.min(1000 * 2 ** retryRef.current, MAX_DELAY_MS);
      retryRef.current += 1;
      reconnectTimer = setTimeout(connect, delay);
    };

    const startMock = (): void => {
      setConnectionState("connected");
      mockTimer = setInterval(() => {
        if (disposed) return;
        const base =
          mockNotifications[mockIndex % mockNotifications.length];
        mockIndex += 1;
        if (!base) return;
        pushNotification({
          ...base,
          id: `${base.id}-ws-${Date.now()}`,
          read: false,
          createdAt: new Date().toISOString(),
        });
      }, MOCK_INTERVAL_MS);
    };

    const start = (): void => {
      if (process.env.NEXT_PUBLIC_API_MOCKING === "enabled") {
        startMock();
      } else {
        connect();
      }
    };

    start();

    reconnectFnRef.current = (): void => {
      retryRef.current = 0;
      if (reconnectTimer) clearTimeout(reconnectTimer);
      if (mockTimer) clearInterval(mockTimer);
      wsRef.current?.close();
      wsRef.current = null;
      start();
    };

    return () => {
      disposed = true;
      if (reconnectTimer) clearTimeout(reconnectTimer);
      if (mockTimer) clearInterval(mockTimer);
      wsRef.current?.close();
      wsRef.current = null;
    };
  }, []);

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

  return { notifications, connectionState, reconnect };
}

/** 通知类型标签映射（供页面渲染图标/文案时复用） */
export const NOTIFICATION_TYPE_LABEL: Record<NotificationType, string> = {
  HOMEWORK_SUBMITTED: "作业",
  EXAM_GRADED: "考试",
  BROADCAST: "广播",
  ROLE_CHANGED: "角色",
};
