import 'zone.js';
import { v4 } from 'uuid'
import { IWsService, logger } from "mslib";
import { ServiceRouters } from "../routers/ServiceRouters";

export class WsServiceProxy {

    private serviceRouter: ServiceRouters

    constructor(serviceRouter: ServiceRouters) {
        this.serviceRouter = serviceRouter;
    }

    async doService(globalZone: Zone, sid: string, connection: any, req: any) {

        await this.handleOpen(globalZone, sid, connection, req).catch(error => {
            logger.error(`[${sid}] Websocket服务 open 事件异常...`, error)
        })


        connection.socket.on('close', async () => {
            await this.handleClose(globalZone, sid, connection, req).catch(error => {
                logger.error(`[${sid}] Websocket服务 open 事件异常...`, error)
            })
        })

        connection.socket.on('message', async (message) => {
            if (message && message.toUpperCase() === 'PING') {
                connection.socket.send('PONG');
                return
            }
            await this.handleMessage(globalZone, sid, connection, req, message).catch(error => {
                logger.error(`[${sid}] Websocket服务 open 事件异常...`, error)
            })
        })
    }

    private async handleOpen(globalZone: Zone, sid: string, connection: any, req: any) {
        return new Promise((resolver, reject) => {
            try {
                const service: IWsService = this.serviceRouter.matchWS(sid);
                const contextProperties = {}
                let currentZone = globalZone.fork({ name: v4(), properties: contextProperties });
                currentZone.run(async () => {
                    try {
                        const context = (req.query || {});
                        const sessionId = context.sessionId || v4()
                        await service.addSessionSocket(sessionId, connection.socket)
                        await service.addSessionContext(sessionId, context)
                        await service.onConnected(sessionId, context)
                        resolver(0);
                    } catch (error) {
                        reject(error);
                    }
                })
            } catch (error) {
                reject(error);
            }
        })
    }


    private async handleClose(globalZone: Zone, sid: string, connection: any, req: any) {
        return new Promise((resolver, reject) => {
            try {
                const service: IWsService = this.serviceRouter.matchWS(sid);
                const contextProperties = {}
                let currentZone = globalZone.fork({ name: v4(), properties: contextProperties });
                currentZone.run(async () => {
                    try {
                        const context = (req.query || {});
                        const sessionId = context.sessionId || v4()
                        await service.onClosed(sessionId, context)
                        await service.removeSessionContext(sessionId)
                        await service.removeSessionSocket(sessionId)
                        resolver(0);
                    } catch (error) {
                        reject(error);
                    }
                })
            } catch (error) {
                reject(error);
            }
        })
    }


    private async handleMessage(globalZone: Zone, sid: string, connection: any, req: any, message: any) {
        return new Promise((resolver, reject) => {
            try {
                const service: IWsService = this.serviceRouter.matchWS(sid);
                const contextProperties = {}
                let currentZone = globalZone.fork({ name: v4(), properties: contextProperties });
                currentZone.run(async () => {
                    try {
                        const context = (req.query || {});
                        const sessionId = context.sessionId || v4()
                        await service.onMessage(message, sessionId, context)
                        resolver(0);
                    } catch (error) {
                        reject(error);
                    }
                })
            } catch (error) {
                reject(error);
            }
        })
    }

}