import { io, Socket } from "socket.io-client";
import Cookies from "js-cookie";
import { SOCKET_URL } from "@/components/main/BaseUrl";

let sharedSocket: Socket | null = null;
let refCount = 0;
const connectionListeners = new Set<(connected: boolean) => void>();

type PendingGroupJoin = { roomId: string };
const pendingGroupJoins = new Map<string, PendingGroupJoin>();
let pendingUserJoin = false;

function notifyConnectionListeners(connected: boolean) {
  connectionListeners.forEach((fn) => fn(connected));
}

function getAuthToken(): string {
  if (typeof window === "undefined") return "";
  return Cookies.get("token") || "";
}

function flushPendingJoins(): void {
  if (!sharedSocket?.connected) return;

  if (pendingUserJoin) {
    sharedSocket.emit("joinUser");
  }

  for (const { roomId } of pendingGroupJoins.values()) {
    sharedSocket.emit("joinGroupRoom", { roomId });
  }
}

export function resolveSocketUrl(): string {
  return process.env.NEXT_PUBLIC_SOCKET_URL || SOCKET_URL;
}

export function acquireChatSocket(): Socket {
  if (!sharedSocket) {
    sharedSocket = io(resolveSocketUrl(), {
      path: "/socket.io/",
      transports: ["websocket", "polling"],
      reconnection: true,
      reconnectionAttempts: Infinity,
      reconnectionDelay: 800,
      reconnectionDelayMax: 5000,
      timeout: 20000,
      withCredentials: true,
      autoConnect: true,
      auth: (cb) => {
        cb({ token: getAuthToken() });
      },
    });

    sharedSocket.on("connect", () => {
      notifyConnectionListeners(true);
      flushPendingJoins();
    });
    sharedSocket.on("disconnect", () => notifyConnectionListeners(false));
    sharedSocket.on("connect_error", () => notifyConnectionListeners(false));
  }
  refCount += 1;
  return sharedSocket;
}

/** Use after acquire — does not change ref count */
export function getChatSocket(): Socket | null {
  return sharedSocket;
}

export function releaseChatSocket(): void {
  refCount = Math.max(0, refCount - 1);
}

export function disconnectChatSocket(): void {
  if (sharedSocket) {
    sharedSocket.disconnect();
    sharedSocket = null;
    refCount = 0;
    pendingGroupJoins.clear();
    pendingUserJoin = false;
    notifyConnectionListeners(false);
  }
}

export function joinUserRoom(_userId?: string): void {
  pendingUserJoin = true;
  if (sharedSocket?.connected) {
    sharedSocket.emit("joinUser");
  }
}

export function joinChatRoom(_userId: string, receiverId: string): void {
  const rid = String(receiverId).trim();
  if (!rid || !sharedSocket) return;
  sharedSocket.emit("joinChat", { receiverId: rid });
}

export function joinGroupRoom(roomId: string, _userId?: string): void {
  const rid = String(roomId).trim();
  if (!rid) return;
  pendingGroupJoins.set(rid, { roomId: rid });
  if (sharedSocket?.connected) {
    sharedSocket.emit("joinGroupRoom", { roomId: rid });
  }
}

export function leaveGroupRoom(roomId: string): void {
  pendingGroupJoins.delete(String(roomId).trim());
}

export function isChatSocketConnected(): boolean {
  return Boolean(sharedSocket?.connected);
}

export function subscribeChatSocketConnection(
  listener: (connected: boolean) => void
): () => void {
  connectionListeners.add(listener);
  listener(isChatSocketConnected());
  return () => {
    connectionListeners.delete(listener);
  };
}
