/*
 * Copyright (c) 2022.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */

import { Manager, Socket, ManagerOptions }  from 'socket.io-client';
import {Context} from "@nuxt/types";

type SocketModuleManagerConfiguration = {url: string, options?: Partial<ManagerOptions>};
class SocketModule {
    protected ctx: Context;
    protected manager : Manager;
    protected sockets : Map<string,Socket>;

    //--------------------------------------------------------------------

    constructor(ctx: Context, managerConfiguration : SocketModuleManagerConfiguration) {
        this.ctx = ctx;
        this.sockets = new Map<string, Socket>();

        this.manager = new Manager(managerConfiguration.url, {
            autoConnect: false,
            ...managerConfiguration.options
        });

        this.subscribeStore();
    }

    private subscribeStore() {
        if (typeof this.ctx === 'undefined') return;

        this.ctx.store.subscribe((mutation: any, state: any) => {
            switch (mutation.type) {
                case 'auth/unsetToken':
                case 'auth/setToken':
                    this.sockets.forEach((value: Socket, key: string) => {
                        this.reconnect(key);
                    });
                    break
            }
        });
    }

    //--------------------------------------------------------------------

    public use(namespace?: string) : Socket {
        if(typeof this.manager === 'undefined') {
            throw new Error('Manager not initialized...');
        }

        if(typeof namespace === 'undefined') {
            namespace = '/';
        }

        let socket = this.sockets.get(namespace);
        if(typeof socket !== 'undefined') {
            return socket;
        }

        const getToken = (cb) => {
            let token : {accessToken: string, [key: string] : any} | undefined = this.ctx.$authWarehouse.get('token');
            if(typeof token === 'undefined') {
                token = this.ctx.store.getters['auth/token'];
            }

            if(typeof token !== 'undefined') {
                return cb({token: token.accessToken});
            }

            return cb();
        }

        getToken.bind(this);

        socket = this.manager.socket(namespace, {
            auth: getToken
        });

        if(!process.server) {
            socket.connect();
        }

        this.sockets.set(namespace, socket);

        return socket;
    }

    public unUse(namespace?: string) {
        if(typeof namespace === 'undefined') {
            namespace = '/';
        }

        if(this.sockets.has(namespace)) {
            this.sockets.delete(namespace);
        }
    }

    //--------------------------------------------------------------------

    public reconnect(namespace: string) {
        if(!this.sockets.has(namespace)) return;

        const socket = <Socket> this.sockets.get(namespace);
        socket.disconnect();

        setTimeout(() => {
            socket.connect();
        },0);

        this.sockets.set(namespace, socket);
    }
}

export default SocketModule;
