import { defineBoot } from "#q-app/wrappers";
import { io } from "socket.io-client";
import { reactive } from "vue";

const state = reactive({
  activeRooms: new Set(),
  isConnected: false,
});

const socket = io(process.env.WEBSOCKET_API, {
  transport: ["websocket"],
  path: process.env.WEBSOCKET_PATH,
  auth: {
    apiKey: process.env.WEBSOCKET_API_KEY,
  },
  reconnection: true,
  reconnectionDelay: 1000,
  timeout: 20000,
});

/**
 * Join a room with automatic reconnection handling
 * @param {string} roomName Room name to join
 * @return {boolean} Success status
 */
const joinRoom = (roomName) => {
  if (!roomName) return false;

  const fullRoomName = `${process.env.WEBSOCKET_ROOM}:${roomName}`;

  state.activeRooms.add(fullRoomName);

  if (state.isConnected) {
    socket.emit("join", fullRoomName);
    console.log(`Joined room: ${fullRoomName}`);
  } else {
    console.log(`Room ${fullRoomName} will join upon connection`);
  }

  return true;
};

/**
 * Leave a room and clean up
 * @param {string} roomName Room name to leave
 */
const leaveRoom = (roomName) => {
  if (!roomName) return;

  const fullRoomName = `${process.env.WEBSOCKET_ROOM}:${roomName}`;

  state.activeRooms.delete(fullRoomName);

  if (state.isConnected) {
    socket.emit("leave", fullRoomName);
    console.log(`Left room: ${fullRoomName}`);
  }
};

const sendEventToLaravel = (eventName, data) => {
  const channel = process.env.WEBSOCKET_ROOM + ":" + eventName;
  socket.emit("eventWrapperToLaravel", {
    channel: channel,
    data: data,
  });
};

const sendEvent = (room, eventName, data) => {
  const channel = process.env.WEBSOCKET_ROOM + ":" + room + "@" + eventName;
  socket.emit("eventWrapperToNode", {
    channel: channel,
    data: data,
  });
};

export default defineBoot(async () => {
  socket.on("connect", () => {
    console.log("Connected to websocket server!");
    state.isConnected = true;

    // Rejoin all active rooms after reconnection
    if (state.activeRooms.size > 0) {
      console.log(
        `Rejoining ${state.activeRooms.size} rooms after reconnection`,
      );
      state.activeRooms.forEach((room) => {
        socket.emit("join", room);
      });
    }
  });

  socket.on("disconnect", () => {
    console.log("Disconnected from websocket server!");
    state.isConnected = false;
  });

  socket.on("connect_error", (error) => {
    console.error("Websocket connection error: ", error);
    state.isConnected = false;
  });

  socket.on("connect_timeout", (timeout) => {
    console.error("Websocket connection timeout: ", timeout);
    state.isConnected = false;
  });

  socket.on("reconnect", (attemptNumber) => {
    console.log(
      "Reconnected to websocket server! Attempt number: ",
      attemptNumber,
    );
    state.isConnected = true;
  });

  socket.on("reconnect_attempt", (attemptNumber) => {
    console.log("Reconnect attempt number: ", attemptNumber);
  });
});

/**
 * Helper function to create one-time event listeners with automatic cleanup
 * @param {string} event Event name
 * @param {Function} callback Callback function
 * @param {string|null} roomName Optional room to leave on callback
 * @return {Function} Cleanup function
 */
const onceEvent = (event, callback, roomName = null) => {
  if (!event || typeof callback !== "function") return () => {};

  const wrappedCallback = (data) => {
    socket.off(event, wrappedCallback);

    if (roomName) {
      leaveRoom(roomName);
    }

    callback(data);
  };

  socket.on(event, wrappedCallback);

  return () => {
    socket.off(event, wrappedCallback);
    if (roomName) {
      leaveRoom(roomName);
    }
  };
};

export {
  socket,
  joinRoom,
  leaveRoom,
  sendEvent,
  sendEventToLaravel,
  onceEvent,
  state,
};
