import { create } from "zustand";
import { io, Socket } from "socket.io-client";

interface SocketStore {
    builderSocket: Socket | null;
    visualizationSocket: Socket | null;
    projectSocket: Socket | null;
    threadSocket: Socket | null;

    initializeBuilderSocket: (token: string, refreshToken: string, projectId: string) => void;
    initializeVisualizationSocket: (token: string, refreshToken: string, projectId: string) => void;
    initializeThreadSocket: (token: string, refreshToken: string, projectId: string) => void;
    initializeProjectSocket: (token: string, refreshToken: string) => void;

    disconnectBuilderSocket: (projectId: string) => void;
    disconnectVisualizationSocket: (projectId: string) => void;
    disconnectThreadSocket: (projectId: string) => void;
    disconnectProjectSocket: () => void;

    disconnectAll: (projectId?: string) => void;
}

const socketOptions = (auth: any) => ({
    reconnection: true,
    reconnectionAttempts: Infinity,
    reconnectionDelay: 2000,
    timeout: 10000,
    auth,
});

const attachLogs = (name: string, socket: Socket) => {
    socket.on("connect", () => {
        // console.log(`${name} socket connected:`, socket.id);
    });

    socket.on("disconnect", (reason) => {
        // console.warn(`${name} socket disconnected:`, reason);
    });

    socket.on("error", (err) => {
        // console.error(`${name} socket error:`, err);
    });

    socket.on("connect_error", (err) => {
        console.warn(`${name} socket connection failed:`, err.message);
    });
};

export const useSocketStore = create<SocketStore>((set, get) => ({
    builderSocket: null,
    visualizationSocket: null,
    projectSocket: null,
    threadSocket: null,

    initializeBuilderSocket: (token, refreshToken, projectId) => {
        if (get().builderSocket) return;
        const builderSocket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder_v1`, socketOptions({ token, refreshToken, projectId }));
        attachLogs("Builder", builderSocket);
        set({ builderSocket });
    },

    initializeVisualizationSocket: (token, refreshToken, projectId) => {
        if (get().visualizationSocket) return;
        const visualizationSocket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization_v1`, socketOptions({ token, refreshToken, projectId }));
        attachLogs("Visualization", visualizationSocket);
        set({ visualizationSocket });
    },

    initializeThreadSocket: (token, refreshToken, projectId) => {
        if (get().threadSocket) return;
        const threadSocket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/thread`, socketOptions({ token, refreshToken, projectId }));
        attachLogs("Thread", threadSocket);
        set({ threadSocket });
    },

    initializeProjectSocket: (token, refreshToken) => {
        if (get().projectSocket) return;
        const projectSocket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/project`, socketOptions({ token, refreshToken }));
        attachLogs("Project", projectSocket);
        set({ projectSocket });
    },

    disconnectBuilderSocket: (projectId) => {
        get().builderSocket?.emit("leaveRoom", { projectId });
        setTimeout(() => {
            get().builderSocket?.disconnect();
            set({ builderSocket: null });
        }, 1000);
    },

    disconnectVisualizationSocket: (projectId) => {
        get().visualizationSocket?.emit("leaveRoom", { projectId });
        setTimeout(() => {
            get().visualizationSocket?.disconnect();
            set({ visualizationSocket: null });
        }, 1000);
    },

    disconnectThreadSocket: (projectId) => {
        get().threadSocket?.emit("leaveRoom", { projectId });
        setTimeout(() => {
            get().threadSocket?.disconnect();
            set({ threadSocket: null });
        }, 1000);
    },

    disconnectProjectSocket: () => {
        get().projectSocket?.disconnect();
        set({ projectSocket: null });
    },

    disconnectAll: (projectId) => {
        get().builderSocket?.emit("leaveRoom", { projectId });
        get().visualizationSocket?.emit("leaveRoom", { projectId });
        get().threadSocket?.emit("leaveRoom", { projectId });

        setTimeout(() => {
            get().builderSocket?.disconnect();
            get().visualizationSocket?.disconnect();
            get().projectSocket?.disconnect();
            get().threadSocket?.disconnect();
        }, 1000);

        set({
            builderSocket: null,
            visualizationSocket: null,
            projectSocket: null,
            threadSocket: null,
        });
    },
}));
