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;
    // }

    if ($is_run_local()) {
        console.log('socket.io init:', {
            namespace: args.client_ns,
            path: args.socket_path,
            auth: args.auth,
        });
    }

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

    socket.on('connect', () => {
        if ($is_run_local()) {
            console.log('socket.io connected:', socket?.id);
        }
        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) => {
        if ($is_run_local()) {
            console.log('socket.io 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) {
        if ($is_run_local()) {
            console.log('socket.io send blocked:', cmd, data);
        }
        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;
// }
