"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.onAppConnection = exports.onDevtoolsConnection = void 0;
const aegis_1 = require("@debug-server-next/utils/aegis");
const enum_1 = require("@debug-server-next/@types/enum");
const db_1 = require("@debug-server-next/db");
const client_1 = require("@debug-server-next/client");
const pub_sub_manager_1 = require("@debug-server-next/controller/pub-sub-manager");
const log_1 = require("@debug-server-next/utils/log");
const pub_sub_channel_1 = require("@debug-server-next/utils/pub-sub-channel");
const debug_target_1 = require("@debug-server-next/utils/debug-target");
const reload_adapter_1 = require("@debug-server-next/utils/reload-adapter");
const log_protocol_1 = require("@debug-server-next/utils/log-protocol");
const log = new log_1.Logger('chrome-devtools', enum_1.WinstonColor.Cyan);
/**
 * pipe devtools ws message to redis pub/sub
 */
const onDevtoolsConnection = (ws, wsUrlParams) => {
    const { Subscriber, Publisher } = db_1.getDBOperator();
    const { extensionName, clientId } = wsUrlParams;
    const downwardChannelId = pub_sub_channel_1.createDownwardChannel(clientId, extensionName);
    const upwardChannelId = pub_sub_channel_1.createUpwardChannel(clientId, extensionName);
    const internalChannelId = pub_sub_channel_1.createInternalChannel(clientId, '');
    log.verbose('devtools connected, subscribe channel: %s, publish channel: %s', downwardChannelId, upwardChannelId);
    const downwardSubscriber = new Subscriber(downwardChannelId);
    // internal channel used to listen message between nodes, such as when app ws closed, notify devtools ws close
    const internalSubscriber = new Subscriber(internalChannelId);
    const internalPublisher = new Publisher(internalChannelId);
    const publisher = new Publisher(upwardChannelId);
    const downwardHandler = (msg) => {
        ws.send(msg);
        try {
            const msgStr = msg;
            const msgObj = JSON.parse(msgStr);
            const { ts: start } = msgObj;
            if (start) {
                aegis_1.aegis.reportTime({
                    name: enum_1.ReportEvent.PubSub,
                    duration: Date.now() - start,
                    ext1: `${Math.ceil(msgStr.length / 1024)}KB`,
                    ext2: msgObj.method,
                });
            }
        }
        catch (e) {
            log.error('%s channel message are invalid JSON, %s', downwardChannelId, msg);
        }
    };
    const internalHandler = (msg) => {
        if (msg === enum_1.InternalChannelEvent.AppWSClose) {
            log.warn('close devtools ws connection');
            ws.close(enum_1.WSCode.ClosePage, 'the target page is closed');
            internalSubscriber.unsubscribe(internalHandler);
            internalSubscriber.disconnect();
        }
    };
    downwardSubscriber.subscribe(downwardHandler);
    internalSubscriber.subscribe(internalHandler);
    internalPublisher.publish(enum_1.InternalChannelEvent.DevtoolsConnected);
    // for iOS, must invoke Debugger.disable before devtools frontend connected, otherwise couldn't
    // receive Debugger.scriptParsed event.
    // for Android, both way is okay
    reload_adapter_1.resumeCommands.map(publisher.publish.bind(publisher));
    ws.on('message', (msg) => {
        publisher.publish(msg.toString());
    });
    ws.on('close', (code, reason) => {
        log.warn('devtools ws client close code %s, reason: %s, clientId: %s', code, reason, clientId);
        reload_adapter_1.resumeCommands.map(publisher.publish.bind(publisher));
        // wait for publish finished
        process.nextTick(() => {
            publisher.disconnect();
            downwardSubscriber.unsubscribe(downwardHandler);
            downwardSubscriber.disconnect();
        });
    });
    ws.on('error', (e) => log.error('devtools ws client error: %j', e));
};
exports.onDevtoolsConnection = onDevtoolsConnection;
const onAppConnection = async (ws, wsUrlParams, host) => {
    const { clientId, clientRole } = wsUrlParams;
    log.verbose('WSAppClient connected. %s', clientId);
    const platform = {
        [enum_1.ClientRole.Android]: enum_1.DevicePlatform.Android,
        [enum_1.ClientRole.IOS]: enum_1.DevicePlatform.IOS,
    }[clientRole];
    const useWS = client_1.appClientManager.shouldUseAppClientType(platform, "WSAppClient" /* WS */);
    if (!useWS)
        return log.warn('current env is %s, ignore ws connection', global.debugAppArgv.env);
    let debugTarget = debug_target_1.createTargetByWsUrlParams(wsUrlParams, host);
    debugTarget = await debug_target_1.patchRefAndSave(debugTarget);
    process.nextTick(() => {
        pub_sub_manager_1.subscribeCommand(debugTarget, ws);
    });
    // when reload, iOS will create a new JSContext, so the debug protocol should resend
    if (debugTarget.platform === enum_1.DevicePlatform.IOS)
        reload_adapter_1.publishReloadCommand(debugTarget);
    ws.on('close', (code, reason) => {
        log.warn('WSAppClient closed: %j, reason: %s, clientId: %s', code, reason, clientId);
        log_protocol_1.clearLogProtocol(clientId);
        // when reload page, keep frontend open to debug lifecycle of created
        const closeDevtools = code === enum_1.WSCode.ClosePage;
        pub_sub_manager_1.cleanDebugTarget(clientId, closeDevtools);
    });
    ws.on('error', (e) => log.error('WSAppClient error %j', e));
};
exports.onAppConnection = onAppConnection;
