"use strict";
/*
 * Tencent is pleased to support the open source community by making
 * Hippy available.
 *
 * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.AppClient = void 0;
const events_1 = require("events");
const types_1 = require("@hippy/devtools-protocol/dist/types");
const enum_1 = require("@debug-server-next/@types/enum");
const middlewares_1 = require("@debug-server-next/middlewares");
const cdp_1 = require("@debug-server-next/utils/cdp");
const log_1 = require("@debug-server-next/utils/log");
const middleware_1 = require("@debug-server-next/utils/middleware");
const report_1 = require("@debug-server-next/utils/report");
const config_1 = require("@debug-server-next/config");
// ignore log the following method, because of high frequency
const ignoreMethods = [types_1.ChromeCommand.PageScreencastFrame, types_1.ChromeCommand.PageScreencastFrameAck];
const downwardLog = new log_1.Logger('↓↓↓', enum_1.WinstonColor.BrightRed);
const upwardLog = new log_1.Logger('↑↑↑', enum_1.WinstonColor.BrightGreen);
/**
 * app client message tunnel
 **/
class AppClient extends events_1.EventEmitter {
    get middleWareManager() {
        return {
            [enum_1.DevicePlatform.Android]: middlewares_1.androidMiddleWareManager,
            [enum_1.DevicePlatform.IOS]: middlewares_1.iOSMiddleWareManager,
        }[this.platform];
    }
    constructor(id, { useAllDomain = true, acceptDomains, ignoreDomains = [], urlParsedContext, platform }) {
        super();
        this.cacheContext = {};
        this.acceptDomains = cdp_1.CDP_DOMAIN_LIST;
        this.ignoreDomains = [];
        this.useAllDomain = true;
        this.msgIdMap = new Map();
        this.id = id;
        this.useAllDomain = useAllDomain;
        this.acceptDomains = acceptDomains || [];
        this.ignoreDomains = ignoreDomains || [];
        this.urlParsedContext = urlParsedContext;
        this.platform = platform;
    }
    /**
     * send debug protocol to app side
     */
    sendToApp(msg) {
        if (!this.filter(msg))
            return Promise.reject(1 /* ErrorCode.DomainFiltered */);
        const { id, method } = msg;
        this.msgIdMap.set(id, {
            method,
            performance: (0, report_1.createCDPPerformance)({
                ...(msg.performance || {}),
                debugServerReceiveFromDevtools: Date.now(),
            }),
        });
        const middlewareList = this.getMiddlewareList(enum_1.MiddlewareType.Upward, method);
        return this.middlewareMessageHandler(middlewareList, msg);
    }
    destroy() { }
    /**
     * receive debug protocol from app side
     */
    downwardMessageHandler(msg) {
        try {
            if ('id' in msg) {
                const cache = this.msgIdMap.get(msg.id);
                if (cache === null || cache === void 0 ? void 0 : cache.method)
                    msg.method = cache.method;
            }
            const { method } = msg;
            const middlewareList = this.getMiddlewareList(enum_1.MiddlewareType.Downward, method);
            return this.middlewareMessageHandler(middlewareList, msg);
        }
        catch (e) {
            downwardLog.error(`app client on message error: %s`, e === null || e === void 0 ? void 0 : e.stack);
            return Promise.reject(e);
        }
    }
    /**
     * use middleware process debug protocol
     */
    middlewareMessageHandler(middlewareList, msgBeforeAdapter) {
        const middlewareContext = {
            ...this.urlParsedContext,
            ...this.cacheContext,
            msg: msgBeforeAdapter,
            sendToApp: (msg) => {
                if (!msg.id) {
                    msg.id = middlewares_1.requestId.create();
                }
                // eslint-disable-next-line @typescript-eslint/no-unused-vars
                const { performance, ...msgWithoutPerf } = msg;
                if (!ignoreMethods.includes(msg.method))
                    upwardLog.verbose('%s sendToApp %j', this.constructor.name, msgWithoutPerf);
                return this.sendHandler(msgWithoutPerf);
            },
            sendToDevtools: (msg) => {
                if (!ignoreMethods.includes(msg.method))
                    downwardLog.verbose('%s sendToDevtools %s %s %s', this.constructor.name, msg.id || '', msg.method, 'error' in msg ? 'not support' : '');
                let performance;
                if ('id' in msg) {
                    const cache = this.msgIdMap.get(msg.id);
                    performance = cache === null || cache === void 0 ? void 0 : cache.performance;
                    if (performance)
                        performance.debugServerToDevtools = Date.now();
                }
                if (config_1.config.showPerformance)
                    msg.performance = performance;
                return this.emitMessageToDevtools(msg);
            },
            setContext: (key, value) => {
                this.cacheContext[key] = value;
            },
        };
        return (0, middleware_1.composeMiddlewares)(middlewareList)(middlewareContext);
    }
    /**
     * emit debug protocol to devtools frontend
     */
    emitMessageToDevtools(msg) {
        if (!msg)
            return Promise.reject(3 /* ErrorCode.EmptyCommand */);
        this.emit("message" /* AppClientEvent.Message */, msg);
        if ('id' in msg) {
            this.msgIdMap.delete(msg.id);
        }
        return Promise.resolve(msg);
    }
    /**
     * filter upward msg by protocol domain
     * whiteList - blackList = acceptDomain - ignoreDomain
     */
    filter(msg) {
        if (this.useAllDomain)
            return true;
        const { method } = msg;
        const domain = (0, cdp_1.getDomain)(method);
        let isIgnoreDomain;
        let isAcceptDomain;
        if (this.ignoreDomains.length) {
            isIgnoreDomain = this.ignoreDomains.indexOf(domain) !== -1 || this.ignoreDomains.indexOf(method) !== -1;
        }
        if (this.acceptDomains.length) {
            isAcceptDomain = this.acceptDomains.indexOf(domain) !== -1 || this.acceptDomains.indexOf(method) !== -1;
        }
        else {
            isAcceptDomain = true;
        }
        if (isAcceptDomain)
            return !isIgnoreDomain;
        return false;
    }
    /**
     * get registered protocol middlewares by protocol method
     */
    getMiddlewareList(type, method) {
        let middlewareList = {
            [enum_1.MiddlewareType.Upward]: this.middleWareManager.upwardMiddleWareListMap,
            [enum_1.MiddlewareType.Downward]: this.middleWareManager.downwardMiddleWareListMap,
        }[type][method];
        if (!middlewareList)
            middlewareList = [];
        if (!Array.isArray(middlewareList))
            middlewareList = [middlewareList];
        if (type === enum_1.MiddlewareType.Upward) {
            return [...middlewareList, middlewares_1.defaultUpwardMiddleware];
        }
        return [middlewares_1.errorDownwardMiddleware, ...middlewareList, middlewares_1.defaultDownwardMiddleware];
    }
}
exports.AppClient = AppClient;
