import { Injectable } from "../../Injector/ServiceDecorator";
import { SubscriptionResponse, makeSubResponse, RPCExporter } from "rpclibrary";
import { IPubSub } from "./Interface";

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

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

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

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

    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]
        })
    }
}