import { io, type Socket } from "socket.io-client";
import { decode, encode } from "@msgpack/msgpack";
import { $is_run_local } from "./utils";

export type SocketIOClientOptions = {
    url?: string;
    auth?: Record<string, any> | (() => Record<string, any>);
};

export type SocketInitArgs = {
    auth: any,
    socket_path: string,
    client_ns: string,
    onConnect?: (socket: Socket) => void;
    onMessage?: (evt: string, data: any) => void;
    onDisconnect?: (reason: string) => void;
    onError?: (error: any) => void;
};

let socket: Socket | null = null;
const messageHandlers = new Set<(evt: string, data: any) => void>();

export const SocketIOClient = null;

export function onSocketMessage(handler: (evt: string, data: any) => void) {
    messageHandlers.add(handler);
    return () => messageHandlers.delete(handler);
}

export function initSocket(args: SocketInitArgs): Socket | null {
    if (args.onMessage) {
        onSocketMessage(args.onMessage);
    }

    if (socket) {
        if (socket.connected) {
            args.onConnect?.(socket);
            return socket;
        }
        socket.removeAllListeners();
        socket.disconnect();
        socket = null;
    }

    socket = io(args.client_ns, {
        path: args.socket_path,
        auth: args.auth, 
        transports: ["websocket"],
        upgrade: false,
        forceNew: true,
        timeout: 10000
    });

    socket.on("connect", () => {
        args.onConnect?.(socket!);
    });
    socket.onAny((evt, payload) => {
        const data = decode(payload);
        if ($is_run_local()) {
            console.log("============>recv", evt, data);
        }
        messageHandlers.forEach((handler) => handler(evt, data));
    });

    socket.on("disconnect", (reason) => {
        args.onDisconnect?.(reason);
    });

    socket.on("connect_error", (error) => {
        args.onError?.(error);
    });

    return socket;
}

export function sendSocketMessage(cmd: string, data: any = {}): boolean {
    if (!socket?.connected) {
        return false;
    }
    if ($is_run_local()) {
        console.log('send<================', cmd, data);
    }
    socket.send(encode({ cmd, data }));
    return true;
}

export function getSocket(): Socket | null {
    return socket;
}

export function emitSocket(eventName: string, data?: any): boolean {
    if (!socket?.connected) {
        return false;
    }

    socket.emit(eventName, data);
    return true;
}

export function closeSocket(): void {
    socket?.disconnect();
    socket = null;
}
