"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.takeDomCapture = void 0;
const req_1 = require("@applitools/req");
const { getCaptureDomPoll, getPollResult, getCaptureDomPollForIE, getPollResultForIE, } = require('@applitools/dom-capture');
async function takeDomCapture({ driver, settings, logger, }) {
    var _a;
    const environment = await driver.getEnvironment();
    const features = await driver.getFeatures();
    const isLegacyBrowser = environment.isIE || environment.isEdgeLegacy;
    const arg = {
        chunkByteLength: (_a = settings === null || settings === void 0 ? void 0 : settings.chunkByteLength) !== null && _a !== void 0 ? _a : (Number(process.env.APPLITOOLS_SCRIPT_RESULT_MAX_BYTE_LENGTH) ||
            (environment.isIOS ? 100000 : 250 * 1024 * 1024)),
    };
    const scripts = {
        main: features.canExecuteOnlyFunctionScripts
            ? require('@applitools/dom-capture').captureDomPoll
            : `return (${isLegacyBrowser ? await getCaptureDomPollForIE() : await getCaptureDomPoll()}).apply(null, arguments);`,
        poll: features.canExecuteOnlyFunctionScripts
            ? require('@applitools/dom-capture').pollResult
            : `return (${isLegacyBrowser ? await getPollResultForIE() : await getPollResult()}).apply(null, arguments);`,
    };
    const url = await driver.getUrl();
    const dom = await captureContextDom(driver.mainContext);
    // TODO save debug DOM like we have for debug screenshots
    return dom;
    async function captureContextDom(context) {
        var _a, _b;
        const capture = await context.executePoll(scripts, {
            main: arg,
            poll: arg,
            executionTimeout: (_a = settings === null || settings === void 0 ? void 0 : settings.executionTimeout) !== null && _a !== void 0 ? _a : 5 * 60 * 1000,
            pollTimeout: (_b = settings === null || settings === void 0 ? void 0 : settings.pollTimeout) !== null && _b !== void 0 ? _b : 200,
        });
        if (!capture)
            return '';
        const raws = capture.split('\n');
        const tokens = JSON.parse(raws[0]);
        const cssEndIndex = raws.indexOf(tokens.separator);
        const frameEndIndex = raws.indexOf(tokens.separator, cssEndIndex + 1);
        let dom = raws[frameEndIndex + 1];
        const cssResources = await Promise.all(raws.slice(1, cssEndIndex).reduce((cssResources, href) => {
            return href ? cssResources.concat(fetchCssResource(new URL(href, url).href)) : cssResources;
        }, []));
        for (const { url, css } of cssResources) {
            dom = dom.replace(`${tokens.cssStartToken}${url}${tokens.cssEndToken}`, css);
        }
        const framePaths = raws.slice(cssEndIndex + 1, frameEndIndex);
        for (const xpaths of framePaths) {
            if (!xpaths)
                continue;
            const references = xpaths.split(',').reduce((parent, selector) => {
                return { reference: { type: 'xpath', selector }, parent };
            }, null);
            let contextDom;
            try {
                const frame = await context.context(references);
                contextDom = await captureContextDom(frame);
            }
            catch (ignored) {
                logger.log('Switching to frame failed');
                contextDom = '';
            }
            dom = dom.replace(`${tokens.iframeStartToken}${xpaths}${tokens.iframeEndToken}`, contextDom);
        }
        return dom;
    }
    async function fetchCssResource(url) {
        var _a;
        logger.log(`Request to download css will be sent to the address "[GET]${url}"`);
        try {
            const response = await (0, req_1.req)(url, {
                connectionTimeout: (_a = settings === null || settings === void 0 ? void 0 : settings.fetchTimeout) !== null && _a !== void 0 ? _a : 60000,
                retry: {
                    limit: 1,
                    validate: ({ response, error }) => !!error || !response.ok,
                },
                proxy: settings === null || settings === void 0 ? void 0 : settings.proxy,
                fetch: settings === null || settings === void 0 ? void 0 : settings.fetch,
            });
            const css = await response.text();
            logger.log(`Request to download css that was sent to the address "[GET]${url}" respond with ${response.statusText}(${response.status})`, response.ok ? `and css of length ${css.length} chars` : '');
            return { url, css: response.ok ? encodeJSON(css) : '' };
        }
        catch (error) {
            logger.error(`Request to download css that was sent to the address "[GET]${url}" failed with error`, error);
            return { url, css: '' };
        }
    }
    function encodeJSON(str) {
        if (!str)
            return '';
        return Array.from(str).reduce((result, char) => {
            switch (char) {
                case '\\':
                case '"':
                case '/':
                    return result + '\\' + char;
                case '\b':
                    return result + '\\b';
                case '\t':
                    return result + '\\t';
                case '\n':
                    return result + '\\n';
                case '\f':
                    return result + '\\f';
                case '\r':
                    return result + '\\r';
                default:
                    if (char < ' ') {
                        const tmp = '000' + char.charCodeAt(0).toString(16);
                        return result + '\\u' + tmp.substring(tmp.length - 4);
                    }
                    else {
                        return result + char;
                    }
            }
        }, '');
    }
}
exports.takeDomCapture = takeDomCapture;
