import { Injectable } from "../../Injector/ServiceDecorator";
import { RPCExporter } from "rpclibrary";
import { IPubSub } from "./Interface";
const uuidv4 = require('uuid/v4')

@Injectable(IPubSub)
export class PubSub<UpdateType = any>
implements RPCExporter{
    name = "PubSub"
    subs : {
        [topic in string]: {
            [uuid in string]: (p:UpdateType)=>any
        }
    }= {}

    RPCs = () => [
        this.unsubscribe,
        {
            name: 'subscribe',
            hook: this.subscribe,
            onClose: (subres, rpcName) => {
                this.unsubscribe(subres.uuid)
            }
        }
    ]

    publish = async (topic: string, msg: UpdateType ) => {
        if(!this.subs[topic]) this.subs[topic] = {}
        await Promise.all(Object.entries(this.subs[topic]).map(async ([uuid, callback]:[string, Function]) => {
            try{
                await callback(msg)
            }catch(e){
                delete this.subs[topic][uuid]
            }
        }))
    }

    subscribe = async (topic:string, callback:(p:UpdateType)=>any) : Promise<string> => {
        const uuid = uuidv4()
        if(!this.subs[topic]) this.subs[topic] = {}
        this.subs[topic][uuid] = callback
        return uuid
    }

    private unsubscribe = async (uuid: string) => {
        Object.entries(this.subs).forEach(([topic, submap]) => {           
            delete submap[uuid]
            if(Object.keys(submap).length === 0)
                delete this.subs[topic]
        })
    }
}