import { RPCExporter, SubscriptionResponse, ErrorResponse, SuccessResponse } from "rpclibrary";
import { FrontworkAdmin } from "../Admin/Admin";
import { TableDefinitionExporter } from "../Types/Interfaces";
import { getLogger } from 'frontblock-generic/Types';

import * as uuid from 'uuid/v4'
import { TableDefiniton } from "../Types/Types";

export type NotificationSeverity = 'Info' | 'Important' | 'Error'

export type Notification = { 
    ID?:number, 
    severity: NotificationSeverity, 
    topic: string,
    message:string,
    time?: number
}

export type EventbusIfc = {
    Eventbus: {
        getNotificationLog: () => Promise<Notification[]>
        pushNotification: (notification:Notification) => Promise<void>
        subscribeNotifications: (callback:Function) => Promise<SubscriptionResponse | ErrorResponse>
    }
} 

const logger = getLogger("Eventbus", 'debug') 

export class FrontworkEventBus 
implements RPCExporter<EventbusIfc, "Eventbus">, TableDefinitionExporter {
    name = "Eventbus" as "Eventbus" 
    private subscriptions : { [uid in string]:Function } = {}

    constructor(private admin: FrontworkAdmin){
    }

    async subscribeNotifications(callback) : Promise<SubscriptionResponse>{
        const uid = uuid()
        this.subscriptions[uid] = callback
        return { result: 'Success', uuid: uid }
    }

    async getNotificationLog() : Promise<Notification[]>{
        try{
            return await this.admin.knex.select('*').from('notifications')
        }catch(e){
            logger.error(e)
            throw e
        }
    }

    async unsubscribeNotifications(uid:string) : Promise<SuccessResponse | ErrorResponse>{
        if(!this.subscriptions[uid]) return { result: 'Error', message: "Unknown subscription" }
        delete this.subscriptions[uid]
        return { result: 'Success' }
    }

    async pushNotification(notification: Notification){
        if(!notification.time) notification.time = Date.now()
        logger.debug("inserting into notifications", notification)
        try{
            await this.admin.knex('notifications').insert(notification)
        }catch(e){
            logger.error(e)
            throw e
        }
 
        Object.values(this.subscriptions).forEach(callback => {
            callback(notification)
        })       
    }

    exportRPCs(){
        return [{
            name: 'getNotificationLog' as 'getNotificationLog',
            call: this.getNotificationLog
        },{
            name: 'pushNotification' as 'pushNotification',
            call: this.pushNotification
        },{
            name: 'subscribeNotificaitons' as 'subscribeNotifications',
            hook: async (callback: Function) =>  await this.subscribeNotifications(callback)
        }]
    }

    getTableDefinitions(): TableDefiniton[]{
        return [{
            name: 'notifications',
            tableBuilder: (table) => {
                table.increments('ID').primary();
                table.string('severity');
                table.string('topic');
                table.string('message');
                table.timestamp('time');
            }
        }]
    }
}