// useWebSocket：push-gateway WebSocket 连接
// 依据：02-architecture-design.md §5 实时推送（P5）
// - 连接 push-gateway WebSocket（NEXT_PUBLIC_PUSH_GATEWAY_WS_URL）
// - 自动重连（指数退避，最大 30s）
// - WebSocket 不可用时降级为 HTTP 轮询（60s）
// - 事件处理：NotificationRequested / GradeRecorded / SchoolAnnouncement / ChildBound / ChildUnbound

"use client";

import { useEffect, useRef, useState, useCallback } from "react";
import { getToken } from "@/lib/auth";
import { getBackoffDelay } from "@/lib/utils";
import type { WebSocketEvent } from "@/types";

const WS_URL = process.env.NEXT_PUBLIC_PUSH_GATEWAY_WS_URL || "";
const POLL_INTERVAL = 60_000; // 60s 轮询降级
const MAX_RECONNECT_ATTEMPTS = 10;

type ConnectionStatus = "connecting" | "connected" | "disconnected" | "polling";

interface UseWebSocketOptions {
  onEvent?: (event: WebSocketEvent) => void;
  enabled?: boolean;
}

export function useWebSocket({
  onEvent,
  enabled = true,
}: UseWebSocketOptions = {}) {
  const [status, setStatus] = useState<ConnectionStatus>("disconnected");
  const wsRef = useRef<WebSocket | null>(null);
  const reconnectAttempts = useRef(0);
  const reconnectTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const pollTimer = useRef<ReturnType<typeof setInterval> | null>(null);
  const onEventRef = useRef(onEvent);

  // 保持 onEvent 引用最新
  useEffect(() => {
    onEventRef.current = onEvent;
  }, [onEvent]);

  const handleEvent = useCallback((event: WebSocketEvent) => {
    onEventRef.current?.(event);
  }, []);

  // HTTP 轮询降级
  const startPolling = useCallback(() => {
    if (pollTimer.current) return;
    setStatus("polling");
    // 立即拉取一次
    void pollNotifications(handleEvent);
    pollTimer.current = setInterval(() => {
      void pollNotifications(handleEvent);
    }, POLL_INTERVAL);
  }, [handleEvent]);

  const stopPolling = useCallback(() => {
    if (pollTimer.current) {
      clearInterval(pollTimer.current);
      pollTimer.current = null;
    }
  }, []);

  // WebSocket 连接
  const connect = useCallback(() => {
    if (!enabled || !WS_URL || typeof window === "undefined") {
      startPolling();
      return;
    }

    const token = getToken();
    if (!token) {
      setStatus("disconnected");
      return;
    }

    setStatus("connecting");

    try {
      const wsUrl = `${WS_URL}?token=${encodeURIComponent(token)}`;
      const ws = new WebSocket(wsUrl);
      wsRef.current = ws;

      ws.onopen = () => {
        reconnectAttempts.current = 0;
        setStatus("connected");
        stopPolling();
      };

      ws.onmessage = (event) => {
        try {
          const data = JSON.parse(event.data) as WebSocketEvent;
          handleEvent(data);
        } catch {
          // 忽略无效消息
        }
      };

      ws.onerror = () => {
        // 错误不立即断开，等 onclose 处理
      };

      ws.onclose = () => {
        setStatus("disconnected");
        wsRef.current = null;

        // 重连或降级
        if (reconnectAttempts.current < MAX_RECONNECT_ATTEMPTS) {
          const delay = getBackoffDelay(reconnectAttempts.current);
          reconnectAttempts.current++;
          reconnectTimer.current = setTimeout(() => {
            connect();
          }, delay);
        } else {
          // 超过重连次数，降级为轮询
          startPolling();
        }
      };
    } catch {
      // WebSocket 创建失败，降级为轮询
      startPolling();
    }
  }, [enabled, stopPolling, startPolling, handleEvent]);

  useEffect(() => {
    connect();

    return () => {
      if (reconnectTimer.current) {
        clearTimeout(reconnectTimer.current);
      }
      stopPolling();
      wsRef.current?.close();
      wsRef.current = null;
    };
  }, [connect, stopPolling]);

  return {
    status,
    reconnect: connect,
  };
}

// HTTP 轮询：拉取最新通知
async function pollNotifications(
  onEvent: (event: WebSocketEvent) => void,
): Promise<void> {
  try {
    const res = await fetch("/api/v1/parent/v1/notifications?since=true", {
      headers: {
        Authorization: `Bearer ${localStorage.getItem("parent_access_token") ?? ""}`,
      },
    });
    if (!res.ok) return;
    const json = await res.json();
    // 将新通知转为 WebSocketEvent 格式
    if (json.data?.notifications) {
      for (const notif of json.data.notifications) {
        onEvent({
          type: "NotificationRequested",
          notification: notif,
        });
      }
    }
  } catch {
    // 轮询失败静默忽略
  }
}
