"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.onHMRServerConnection = exports.onHMRClientConnection = void 0;
const tslib_1 = require("tslib");
const fs_1 = tslib_1.__importDefault(require("fs"));
const path_1 = tslib_1.__importDefault(require("path"));
// import { throttle } from '@/utils/throttle';
const enum_1 = require("@/@types/enum");
const url_1 = require("@/utils/url");
const log_1 = require("@/utils/log");
const pub_sub_channel_1 = require("@/utils/pub-sub-channel");
const db_1 = require("@/db");
const config_1 = require("@/config");
const buffer_1 = require("@/utils/buffer");
const cos_1 = require("@/utils/cos");
const aegis_1 = require("@/utils/aegis");
const log = new log_1.Logger('hmr-controller', enum_1.WinstonColor.Blue);
const hmrCloseEvent = 'HMR_SERVER_CLOSED';
const onHMRClientConnection = (ws, wsUrlParams) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const { hash } = wsUrlParams;
    log.info('HMR client connected, hash: %s', hash);
    const { Subscriber, DB } = db_1.getDBOperator();
    const bundle = yield new DB(config_1.config.redis.bundleTable).get(hash);
    if (!bundle) {
        const reason = 'Hippy dev server not started, not support HMR!';
        ws.close(enum_1.WSCode.InvalidRequestParams, reason);
        return log.warn(reason);
    }
    aegis_1.aegis.reportEvent({
        name: enum_1.ReportEvent.RemoteHMR,
        ext1: hash,
        ext2: enum_1.HMRReportExt2.Client,
    });
    const subscriber = new Subscriber(pub_sub_channel_1.createHMRChannel(hash));
    subscriber.subscribe((msg) => {
        if (msg === hmrCloseEvent) {
            subscriber.unsubscribe();
            subscriber.disconnect();
            const reason = 'Hippy dev server closed, stop HMR!';
            ws.close(enum_1.WSCode.HMRServerClosed, reason);
            log.warn(reason);
        }
        else {
            log.info('receive HMR msg from redis: %s', msg);
            const msgObj = JSON.parse(msg.toString());
            if (msgObj.performance) {
                msgObj.performance.serverToApp = Date.now();
            }
            ws.send(JSON.stringify(msgObj));
        }
    });
    ws.on('close', (code, reason) => onClose(code, reason));
    ws.on('error', (e) => onClose(null, null, e));
    function onClose(code, reason, error) {
        if (error)
            log.error('HMR client ws error: %s', error.stack || error);
        log.warn('HMR client ws closed, code: %s, reason: %s', code, reason);
        subscriber.unsubscribe();
        subscriber.disconnect();
    }
});
exports.onHMRClientConnection = onHMRClientConnection;
const onHMRServerConnection = (ws, wsUrlParams) => {
    const { hash } = wsUrlParams;
    log.info('HMR server connected, hash: %s', hash);
    const { Publisher, DB } = db_1.getDBOperator();
    const model = new DB(config_1.config.redis.bundleTable);
    model.upsert(hash, { hash });
    const publisher = new Publisher(pub_sub_channel_1.createHMRChannel(hash));
    aegis_1.aegis.reportEvent({
        name: enum_1.ReportEvent.RemoteHMR,
        ext1: hash,
        ext2: enum_1.HMRReportExt2.Server,
    });
    ws.on('message', (msg) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
        var _a, _b;
        try {
            const serverReceive = Date.now();
            const _c = buffer_1.decodeHMRData(msg), { emitList, publicPath } = _c, emitJSON = tslib_1.__rest(_c, ["emitList", "publicPath"]);
            if (emitJSON.performance) {
                const { hadSyncBundleResource } = emitJSON;
                const { pcToServer } = emitJSON.performance;
                const hmrSize = `${Math.ceil(msg.length / 1024)}KB`;
                const reportData = {
                    name: enum_1.ReportEvent.HMRPCToServer,
                    duration: serverReceive - pcToServer,
                    ext1: hmrSize,
                    ext2: '',
                };
                if ('hadSyncBundleResource' in emitJSON)
                    reportData.ext2 = hadSyncBundleResource ? enum_1.HMRSyncType.Patch : enum_1.HMRSyncType.FirstTime;
                aegis_1.aegis.reportTime(reportData);
            }
            yield saveHMRFiles(url_1.getBaseFolderOfPublicPath(publicPath), emitList);
            const msgStr = JSON.stringify(emitJSON);
            log.info('receive HMR msg from PC: %s', msgStr);
            if ((_a = emitJSON.messages) === null || _a === void 0 ? void 0 : _a.length)
                publisher.publish(msgStr);
        }
        catch (e) {
            log.error('decodeHMRData failed: ', ((_b = e) === null || _b === void 0 ? void 0 : _b.stack) || e);
        }
    }));
    ws.on('close', (code, reason) => onClose(code, reason));
    ws.on('error', (e) => onClose(null, null, e));
    function onClose(code, reason, error) {
        if (error)
            log.error('HMR server ws error: %s', error.stack || error);
        log.warn('HMR server ws closed, code: %s, reason: %s', code, reason);
        publisher.publish(hmrCloseEvent);
        process.nextTick(() => {
            publisher.disconnect();
        });
        model.delete(hash);
    }
};
exports.onHMRServerConnection = onHMRServerConnection;
function saveHMRFiles(folder, emitList) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        return Promise.all(emitList.map(({ name, content }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            const saveFn = {
                [enum_1.StaticFileStorage.COS]: saveHMRFileToCOS,
                [enum_1.StaticFileStorage.Local]: saveHMRFileToLocal,
            }[config_1.config.staticFileStorage];
            return saveFn(folder, name, content);
        })));
    });
}
function saveHMRFileToLocal(folder, name, content) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const fullFname = path_1.default.join(config_1.config.hmrStaticPath, folder, name);
        const cacheFolder = path_1.default.dirname(fullFname);
        yield fs_1.default.promises.mkdir(cacheFolder, { recursive: true });
        return fs_1.default.promises.writeFile(fullFname, content);
    });
}
function saveHMRFileToCOS(folder, name, content) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const key = path_1.default.join(folder, name);
        cos_1.cosUpload(key, content);
    });
}
