import { io, Socket } from "socket.io-client";

const WS_URL = process.env.NEXT_PUBLIC_WS_URL || "http://localhost:3002";

let socket: Socket | null = null;
let refreshFn: (() => Promise<string | null>) | null = null;

/**
 * Register a token refresh function that the socket can call
 * when it receives an auth error, to get a fresh token.
 */
export function setTokenRefreshFn(fn: () => Promise<string | null>) {
  refreshFn = fn;
}

export function getSocket(): Socket {
  if (!socket) {
    socket = io(WS_URL, {
      autoConnect: false,
      transports: ["websocket", "polling"],
      reconnection: true,
      reconnectionAttempts: 10,
      reconnectionDelay: 1000,
      reconnectionDelayMax: 10000,
      timeout: 20000,
    });

    // Handle auth/connection errors
    socket.on("connect_error", async (err: any) => {
      console.error("[Socket] Connection error:", err.message);
      // If auth-related, try refreshing the token once
      if (err.message?.includes("auth") || err.message?.includes("token") || err.message?.includes("unauthorized")) {
        if (refreshFn) {
          const newToken = await refreshFn();
          if (newToken && socket) {
            socket.auth = { token: newToken };
            socket.connect();
          }
        }
      }
    });

    socket.on("disconnect", (reason: string) => {
      console.warn("[Socket] Disconnected:", reason);
    });

    socket.on("connect", () => {
      console.log("[Socket] Connected successfully");
    });
  }
  return socket;
}

export function connectSocket(token: string) {
  const s = getSocket();
  s.auth = { token };
  if (!s.connected) {
    s.connect();
  }
  return s;
}

export function disconnectSocket() {
  if (socket) {
    socket.removeAllListeners();
    socket.disconnect();
    socket = null;
  }
}

export default getSocket;