"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 report_1 = require("@debug-server-next/utils/report");
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 reload_adapter_1 = require("@debug-server-next/utils/reload-adapter");
const history_event_protocol_1 = require("@debug-server-next/utils/history-event-protocol");
const debug_target_1 = require("@debug-server-next/utils/debug-target");
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 } = (0, db_1.getDBOperator)();
    const { extensionName, clientId, hash } = wsUrlParams;
    const downwardChannelId = (0, pub_sub_channel_1.createDownwardChannel)(clientId, extensionName);
    const upwardChannelId = (0, pub_sub_channel_1.createUpwardChannel)(clientId, extensionName);
    const internalChannelId = (0, pub_sub_channel_1.createInternalChannel)(clientId, '');
    report_1.report.event({
        name: enum_1.ReportEvent.ConnectFrontend,
        ext1: clientId,
    });
    const connectTime = Date.now();
    log.info('devtools connected');
    log.verbose('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) {
                report_1.report.time(Date.now() - start, {
                    name: enum_1.ReportEvent.PubSub,
                    ext1: `${Math.ceil(msgStr.length / 1024)}KB`,
                    ext2: msgObj.method,
                });
            }
            // evaluate response
            if (msgObj.id === reload_adapter_1.runtimeEvaluateCommandId) {
                try {
                    const { result } = msgObj.result;
                    const isVue = result.value.split('+')[0] !== 'undefined';
                    const isReact = result.value.split('+')[1] !== 'undefined';
                    log.info('receive evaluate response, welcome to use Hippy-%s', isVue ? 'Vue' : isReact ? 'React' : 'Other');
                    // get debug target
                    const { clientId } = wsUrlParams;
                    try {
                        (0, debug_target_1.updateDebugTarget)(clientId, {
                            driverType: isVue ? enum_1.AppDriverType.Vue : isReact ? enum_1.AppDriverType.React : enum_1.AppDriverType.Other,
                        }).then((debugTarget) => {
                            log.info('debug target updated for %s', debugTarget.title);
                            // 数据上报
                            report_1.report.event({
                                name: enum_1.ReportEvent.ConnectFrontend2,
                                ext1: debugTarget.title,
                                ext2: debugTarget.driverType,
                                ext3: debugTarget.platform,
                                bundleId: debugTarget.bundleId,
                                hostVersion: debugTarget.hostVersion,
                                sdkVersion: debugTarget.sdkVersion,
                            });
                        });
                    }
                    catch (e) {
                        log.error('update DebugTarget contextName fail', e.stack);
                    }
                }
                catch (e) {
                    log.error('evaluate response parse fail', e.stack);
                }
            }
        }
        catch (e) {
            log.error('%s channel message are invalid JSON, %s', downwardChannelId, msg);
        }
    };
    const internalHandler = (msg) => {
        if (msg === enum_1.InternalChannelEvent.AppWSClose) {
            log.verbose('close devtools ws connection');
            ws.close(enum_1.WSCode.ClosePage, 'the target page is closed');
            internalSubscriber.disconnect();
            internalPublisher.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', async (code, reason) => {
        log.info('devtools closed. code: %s, reason: %s', code, reason);
        await Promise.all(reload_adapter_1.resumeCommands.map(publisher.publish.bind(publisher)));
        // wait for publish finished
        publisher.disconnect();
        downwardSubscriber.disconnect();
        internalSubscriber.disconnect();
        internalPublisher.disconnect();
        report_1.report.event({
            name: enum_1.ReportEvent.DisConnectFrontend,
            ext1: String(code),
            ext2: String(Date.now() - connectTime),
        });
    });
    ws.on('error', (e) => log.error('devtools ws client error: %j', e));
    // send evaluate command to get driver type
    // liulipeng 2023-10-08 注释以下代码,解决调试崩溃的问题
    // DebugTargetManager.findDebugTarget(clientId, hash).then((debugTarget: DebugTarget) => {
    //   if (debugTarget.driverType === undefined) {
    //     setTimeout(() => {
    //       log.info('send evaluate command to get driver type...');
    //       publishEvaluateCommand(clientId, "typeof __VUE_ROOT_INSTANCES__ + '+' + typeof __REACT_DEVTOOLS_GLOBAL_HOOK__");
    //     }, 2000);
    //   } else {
    //     log.info('reopen frontend, debugTarget.driverType is %s', debugTarget.driverType);
    //   }
    // });
    // liulipeng 2023-10-08 注释以上代码,解决调试崩溃的问题
};
exports.onDevtoolsConnection = onDevtoolsConnection;
const onAppConnection = async (ws, wsUrlParams, debugTarget) => {
    const { clientId, clientRole, contextName } = wsUrlParams;
    log.info('app connected. %s', contextName);
    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" /* AppClientType.WS */);
    if (!useWS)
        return log.verbose('current env is %s, ignore ws connection', global.debugAppArgv.env);
    (0, pub_sub_manager_1.subscribeCommand)(debugTarget, ws);
    report_1.report.event({
        name: enum_1.ReportEvent.RemoteDebug,
        ext1: contextName,
        ext2: platform,
    });
    // when reload, iOS will create a new JSContext, so the debug protocol should resend
    if (debugTarget.platform === enum_1.DevicePlatform.IOS)
        (0, reload_adapter_1.publishReloadCommand)(debugTarget);
    const connectTime = Date.now();
    ws.on('close', (code, reason) => {
        log.info('app closed. code: %j, reason: %s, contextName: %s', code, reason, contextName);
        (0, history_event_protocol_1.clearHistoryProtocol)(clientId);
        // when reload page, keep frontend open to debug lifecycle of created
        const closeDevtools = code === enum_1.WSCode.ClosePage;
        (0, pub_sub_manager_1.cleanDebugTarget)(clientId, closeDevtools);
        report_1.report.event({
            name: enum_1.ReportEvent.DisRemoteDebug,
            ext1: String(code),
            ext2: String(Date.now() - connectTime),
        });
    });
    ws.on('error', (e) => log.error('WSAppClient error %j', e));
    // log.info(`debug target is: ${JSON.stringify(debugTarget)}`);
};
exports.onAppConnection = onAppConnection;
