"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeCoreServer = void 0;
const socket_1 = require("@applitools/socket");
const spec_driver_webdriver_1 = __importDefault(require("@applitools/spec-driver-webdriver"));
const spec_driver_1 = require("./spec-driver");
const ws_server_1 = require("./ws-server");
const logger_1 = require("@applitools/logger");
const core_1 = require("../core");
const refer_1 = require("./refer");
const history_1 = require("./history");
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
async function makeCoreServer({ environment: defaultEnvironment, agentIdPrefix = `eyes-universal/${require('../../package.json').version}`, debug = false, shutdownMode = 'lazy', idleTimeout = 900000, // 15min
printStdout = false, isProcess = false, ...handlerOptions } = {}) {
    var _a, _b;
    const logDirname = (_a = process.env.APPLITOOLS_LOG_DIR) !== null && _a !== void 0 ? _a : path_1.default.resolve(os_1.default.tmpdir(), `applitools-logs`);
    const baseLogger = (0, logger_1.makeLogger)({
        handler: { type: 'rolling file', name: 'universal', dirname: logDirname },
        level: 'info',
        format: { label: 'core-universal', colors: false },
    });
    baseLogger.log('Core universal is going to be initialized with options', {
        debug,
        shutdownMode,
        idleTimeout,
        printStdout,
        defaultEnvironment,
        ...handlerOptions,
    });
    const { server, port } = await (0, ws_server_1.makeServer)({ ...handlerOptions, debug });
    if (isProcess) {
        // eslint-disable-next-line no-console
        console.log(port.toString()); // NOTE: this is a part of the generic protocol
        (_b = process.send) === null || _b === void 0 ? void 0 : _b.call(process, { name: 'port', payload: { port } }); // NOTE: this is a part of the js specific protocol
    }
    if (!server) {
        baseLogger.console.log(`You are trying to spawn a duplicated server, use the server on port ${port} instead`);
        return { port };
    }
    if (!printStdout)
        process.stdout.write = () => true; // NOTE: prevent any write to stdout
    baseLogger.log('Core universal is started on port', port);
    let idle;
    let serverClosed = false;
    if (shutdownMode === 'stdin') {
        process.stdin.resume();
        process.stdin.on('end', () => {
            server.close();
        });
    }
    else if (shutdownMode === 'lazy') {
        if (idleTimeout) {
            idle = setTimeout(() => server.close(), idleTimeout);
        }
    }
    server.on('close', () => {
        clearTimeout(idle);
        serverClosed = true;
    });
    server.on('connection', async (client) => {
        const refer = (0, refer_1.makeRefer)();
        const socket = (0, socket_1.makeSocket)(client, {
            transport: 'ws',
            logger: baseLogger,
        });
        const getHistory = debug ? (0, history_1.withHistory)(socket) : () => null;
        if (shutdownMode === 'lazy' && idleTimeout) {
            clearTimeout(idle);
            socket.on('close', () => {
                if (server.clients.size > 0 || serverClosed)
                    return;
                idle = setTimeout(() => server.close(), idleTimeout);
            });
        }
        const logger = baseLogger.extend({
            console: {
                log: (message) => socket.emit('Logger.log', { level: 'info', message }),
                warn: (message) => socket.emit('Logger.log', { level: 'warn', message }),
                error: (message) => socket.emit('Logger.log', { level: 'error', message }),
                fatal: (message) => socket.emit('Logger.log', { level: 'fatal', message }),
            },
        });
        logger.console.log(`Logs saved in: ${logDirname}`);
        const corePromise = socket.wait('Core.makeCore', ({ spec, agentId, environment, cwd }) => {
            return (0, core_1.makeCore)({
                spec: spec === 'webdriver' ? spec_driver_webdriver_1.default : (0, spec_driver_1.makeSpec)({ socket, spec }),
                agentId: `${agentIdPrefix}/${agentId}`,
                cwd,
                environment: {
                    ...defaultEnvironment,
                    ...environment,
                    versions: { ...defaultEnvironment === null || defaultEnvironment === void 0 ? void 0 : defaultEnvironment.versions, ...environment === null || environment === void 0 ? void 0 : environment.versions },
                    universal: true,
                },
                logger,
            });
        });
        socket.command('Core.getAccountInfo', async (options) => {
            const core = await corePromise;
            return core.getAccountInfo(options);
        });
        socket.command('Core.getViewportSize', async (options) => {
            var _a;
            const core = await corePromise;
            return (_a = core.getViewportSize) === null || _a === void 0 ? void 0 : _a.call(core, options);
        });
        socket.command('Core.setViewportSize', async (options) => {
            var _a;
            const core = await corePromise;
            return (_a = core.setViewportSize) === null || _a === void 0 ? void 0 : _a.call(core, options);
        });
        socket.command('Core.closeBatch', async (options) => {
            const core = await corePromise;
            return core.closeBatch(options);
        });
        socket.command('Core.deleteTest', async (options) => {
            const core = await corePromise;
            return core.deleteTest(options);
        });
        socket.command('Core.logEvent', async ({ settings }) => {
            const core = await corePromise;
            return core.logEvent({ settings });
        });
        socket.command('Core.locate', async (options) => {
            const core = await corePromise;
            return core.locate(options);
        });
        socket.command('Core.locateText', async (options) => {
            const core = await corePromise;
            return core.locateText(options);
        });
        socket.command('Core.extractText', async (options) => {
            const core = await corePromise;
            return core.extractText(options);
        });
        socket.command('Core.getECClient', async (options) => {
            const core = await corePromise;
            const client = await core.getECClient(options);
            return { url: client.url };
        });
        socket.command('Core.makeManager', async (options) => {
            var _a;
            const core = await corePromise;
            if ((_a = options.settings) === null || _a === void 0 ? void 0 : _a.agentId) {
                options.settings.agentId = `${agentIdPrefix}/${options.settings.agentId}`;
            }
            return refer.ref(await core.makeManager(options));
        });
        socket.command('EyesManager.openEyes', async ({ manager, ...options }) => {
            var _a;
            const eyes = await ((_a = refer.deref(manager)) === null || _a === void 0 ? void 0 : _a.openEyes(options));
            const eyesRef = refer.ref(eyes, manager);
            return eyesRef;
        });
        socket.command('EyesManager.getResults', async ({ manager, ...options }) => {
            var _a;
            return (_a = refer.deref(manager)) === null || _a === void 0 ? void 0 : _a.getResults(options);
        });
        socket.command('Eyes.check', async ({ eyes, ...options }) => {
            var _a;
            return (_a = refer.deref(eyes)) === null || _a === void 0 ? void 0 : _a.check(options);
        });
        socket.command('Eyes.checkAndClose', async ({ eyes, ...options }) => {
            var _a;
            return (_a = refer.deref(eyes)) === null || _a === void 0 ? void 0 : _a.checkAndClose(options);
        });
        socket.command('Eyes.close', async ({ eyes, ...options }) => {
            var _a;
            return (_a = refer.deref(eyes)) === null || _a === void 0 ? void 0 : _a.close(options);
        });
        socket.command('Eyes.abort', async ({ eyes, ...options }) => {
            var _a;
            return (_a = refer.deref(eyes)) === null || _a === void 0 ? void 0 : _a.abort(options);
        });
        socket.command('Eyes.getResults', async ({ eyes, ...options }) => {
            var _a;
            return (_a = refer.deref(eyes)) === null || _a === void 0 ? void 0 : _a.getResults(options);
        });
        socket.command('Debug.getHistory', async () => {
            return getHistory();
        });
    });
    process.on('unhandledRejection', error => {
        baseLogger.error('Unhandled promise rejection', error);
        /* eslint-disable-next-line no-console */
        console.error('Unhandled promise rejection', error);
    });
    process.on('uncaughtException', error => {
        baseLogger.error('Uncaught error terminating the process', error);
        /* eslint-disable-next-line no-console */
        console.error('Uncaught error terminating the process', error);
        process.exit(1);
    });
    return { port, close: () => server.close() };
}
exports.makeCoreServer = makeCoreServer;
