import { MESSAGETYPES, getMessageTypeName } from "@/constants";
import { getUserGameState } from "@/apis";

let socket = null;
let heartbeatInterval = null;
let reconnectTimer = null;
let manualClose = false;
let checkingSession = false;
let kickedOut = false;
let isConnecting = false;

function createWebSocket(token, onMessage) {
  if (!token) return;
  if (kickedOut) kickedOut = false;
  if (socket || isConnecting) return;

  manualClose = false;
  isConnecting = true;

  let url = "wss://api.shelingxingqiu.com/socket";
  try {
    const accountInfo = uni.getAccountInfoSync();
    const envVersion = accountInfo.miniProgram.envVersion;

    switch (envVersion) {
      case "develop": // 开发版
        // url = "ws://192.168.1.30:8000/socket";
        url = "wss://apitest.shelingxingqiu.com/socket";
        break;
      case "trial": // 体验版
        url = "wss://apitest.shelingxingqiu.com/socket";
        break;
      case "trial":
        url = "wss://apitest.shelingxingqiu.com/socket";
        break;
      case "release":
        url = "wss://api.shelingxingqiu.com/socket";
        break;
      default:
        break;
    }
  } catch (e) {
    console.error("获取 WebSocket 环境信息失败，使用默认正式环境", e);
  }

  url += `?authorization=${token}`;
  const socketTask = uni.connectSocket({
    url,
    success: () => {
      console.log("WebSocket 已发起连接");
    },
    fail: (err) => {
      if (socket !== socketTask) return;
      console.error("WebSocket 连接失败", err);
      socket = null;
      isConnecting = false;
      reconnect(onMessage);
    },
  });

  socket = socketTask;

  socketTask.onOpen(() => {
    if (socket !== socketTask) return;
    console.log("WebSocket 连接成功");
    isConnecting = false;
    startHeartbeat(onMessage);
  });

  socketTask.onMessage((res) => {
    if (socket !== socketTask) return;

    const { data, event } = JSON.parse(res.data);
    if (event === "pong") return;
    if (data.type) {
      console.log(
        "收到 WebSocket 消息",
        getMessageTypeName(data.type),
        data.data
      );
      if (onMessage) onMessage({ ...(data.data || {}), type: data.type });
      return;
    }
    if (onMessage && data.updates) onMessage(data.updates);
    const msg = data.updates[0];
    if (msg) {
      console.log(
        "收到 WebSocket 更新",
        getMessageTypeName(msg.constructor),
        msg
      );
      if (msg.constructor === MESSAGETYPES.RankUpdate) {
        uni.setStorageSync("latestRank", msg.lvl);
      } else if (msg.constructor === MESSAGETYPES.LvlUpdate) {
        uni.setStorageSync("latestLvl", msg.lvl);
      } else if (msg.constructor === MESSAGETYPES.DeviceOnline) {
        uni.$emit("update-online");
      } else if (msg.constructor === MESSAGETYPES.DeviceOffline) {
        uni.$emit("update-online");
      }
    }
  });

  socketTask.onError((err) => {
    if (socket !== socketTask) return;
    console.error("WebSocket 错误", err);
  });

  socketTask.onClose(async (result) => {
    if (socket !== socketTask) return;
    console.log("WebSocket 已关闭", result);
    stopHeartbeat();
    socket = null;
    isConnecting = false;

    if (manualClose || kickedOut) return;
    await handleUnexpectedClose(onMessage);
  });
}

async function handleUnexpectedClose(onMessage) {
  if (checkingSession || manualClose || kickedOut) return;

  const token = uni.getStorageSync(
    `${uni.getAccountInfoSync().miniProgram.envVersion}_token`
  );
  if (!token) return;

  checkingSession = true;
  try {
    await getUserGameState();
    if (!manualClose && !kickedOut) reconnect(onMessage);
  } catch (err) {
    if (err?.type === "AUTH_INVALID") {
      kickedOut = true;
      manualClose = true;
      reconnectTimer && clearTimeout(reconnectTimer);
      uni.$emit("session-kicked-out");
      return;
    }
    if (!manualClose && !kickedOut) reconnect(onMessage);
  } finally {
    checkingSession = false;
  }
}

function reconnect(onMessage) {
  reconnectTimer && clearTimeout(reconnectTimer);

  const token = uni.getStorageSync(
    `${uni.getAccountInfoSync().miniProgram.envVersion}_token`
  );
  if (!token || manualClose || kickedOut || socket || isConnecting) return;

  reconnectTimer = setTimeout(() => {
    if (manualClose || kickedOut || socket || isConnecting) return;
    console.log("WebSocket 正在重连...");
    createWebSocket(token, onMessage);
  }, 1000);
}

function closeWebSocket(isManual = true) {
  manualClose = isManual;
  reconnectTimer && clearTimeout(reconnectTimer);
  stopHeartbeat();
  isConnecting = false;

  if (socket) {
    const currentSocket = socket;
    socket = null;

    try {
      currentSocket.close();
    } catch (err) {
      console.error("关闭 WebSocket 失败", err);
    }
  }
}

function sendHeartbeat(onMessage) {
  if (!socket) return;

  const currentSocket = socket;
  currentSocket.send({
    data: JSON.stringify({ event: "ping", data: {} }),
    success: () => {},
    fail: (err) => {
      if (socket !== currentSocket) return;
      console.error("心跳发送失败", err);
      stopHeartbeat();
      closeWebSocket(false);
      reconnect(onMessage);
    },
  });
}

function startHeartbeat(onMessage) {
  stopHeartbeat();

  heartbeatInterval = setInterval(() => {
    if (socket) {
      sendHeartbeat(onMessage);
    }
  }, 10000);
}

function stopHeartbeat() {
  if (heartbeatInterval) {
    clearInterval(heartbeatInterval);
    heartbeatInterval = null;
  }
}

export default {
  createWebSocket,
  closeWebSocket,
};
