"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PuppeteerPageHooker = void 0;
const debug_1 = __importDefault(require("debug"));
const loggerError = debug_1.default('root-cause:error');
const ommittedPageMethods = [
    'then',
    'setViewport',
    'mainFrame',
    'screenshot',
    'close',
    'removeListener',
    'off',
    'constructor',
    'viewport',
];
const proxiedTypes = ['frames', 'keyboard', 'mouse', '$', '$$', 'waitForSelector'];
class PuppeteerPageHooker {
    constructor(testContext, rootPage) {
        this.testContext = testContext;
        this.rootPage = rootPage;
        this.paused = false;
        this.beforeAllHooks = [];
        this.afterAllHooks = [];
        this.beforeHooks = [];
        this.afterHooks = [];
    }
    pause() {
        this.paused = true;
    }
    resume() {
        this.paused = false;
    }
    registerBeforeAllHook(hook) {
        this.beforeAllHooks.push(hook);
    }
    registerAfterAllHook(hook) {
        this.afterAllHooks.push(hook);
    }
    registerBeforeHook(hook) {
        this.beforeHooks.push(hook);
    }
    registerAfterHook(hook) {
        this.afterHooks.push(hook);
    }
    wrapWithProxy(proxiedObject) {
        const { beforeHooks, afterHooks, testContext, rootPage } = this;
        const pauseStateHolder = this;
        const pageHookerThis = this;
        const handler = {
            get(target, prop, receiver) {
                const reflectedProperty = Reflect.get(target, prop, receiver);
                if (prop.toString().startsWith('_') ||
                    ommittedPageMethods.includes(prop.toString()) ||
                    pauseStateHolder.paused) {
                    return reflectedProperty;
                }
                if (proxiedTypes.includes(prop.toString())) {
                    if (typeof reflectedProperty === 'function') {
                        return (...args) => {
                            const method = reflectedProperty;
                            const result = method.apply(target, args);
                            if (!result) {
                                return result;
                            }
                            if (result.constructor && result.constructor.name === 'Promise') {
                                return result.then((resultOfPromiseReturnedByTopLevelFunction) => {
                                    if (!resultOfPromiseReturnedByTopLevelFunction) {
                                        return resultOfPromiseReturnedByTopLevelFunction;
                                    }
                                    if (resultOfPromiseReturnedByTopLevelFunction.length) {
                                        const proxiedResult = resultOfPromiseReturnedByTopLevelFunction.map((a) => pageHookerThis.wrapWithProxy(a));
                                        return proxiedResult;
                                    }
                                    return pageHookerThis.wrapWithProxy(resultOfPromiseReturnedByTopLevelFunction);
                                });
                            }
                            if (result.length) {
                                const proxiedResult = result.map((a) => pageHookerThis.wrapWithProxy(a));
                                return proxiedResult;
                            }
                            return pageHookerThis.wrapWithProxy(result);
                        };
                    }
                    return pageHookerThis.wrapWithProxy(reflectedProperty);
                }
                if (typeof reflectedProperty !== 'function') {
                    return reflectedProperty;
                }
                return async function rootCauseWrappedFunction(...args) {
                    testContext.stepStarted();
                    for (const beforeHook of beforeHooks) {
                        try {
                            await beforeHook(testContext, prop.toString(), proxiedObject, rootPage, args);
                        }
                        catch (err) {
                            loggerError(err);
                        }
                    }
                    try {
                        const method = reflectedProperty;
                        const result = await method.apply(target, args);
                        for (const afterHook of afterHooks) {
                            try {
                                await afterHook(testContext, prop.toString(), proxiedObject, rootPage, args, {
                                    success: true,
                                    data: result,
                                });
                            }
                            catch (err) {
                                loggerError(err);
                            }
                        }
                        return result;
                    }
                    catch (err) {
                        for (const afterHook of afterHooks) {
                            try {
                                await afterHook(testContext, prop.toString(), proxiedObject, rootPage, args, {
                                    success: false,
                                    error: err,
                                });
                            }
                            catch (err) {
                                loggerError(err);
                            }
                        }
                        throw err;
                    }
                    finally {
                        await testContext.stepEnded();
                    }
                };
            },
        };
        return new Proxy(proxiedObject, handler);
    }
    async start() {
        for (const beforeAllHook of this.beforeAllHooks) {
            try {
                await beforeAllHook(this.testContext, this.rootPage, this.rootPage);
            }
            catch (e) {
                loggerError(e);
            }
        }
    }
    async end(endStatus) {
        for (const afterAllHook of this.afterAllHooks) {
            try {
                await afterAllHook(this.testContext, endStatus);
            }
            catch (e) {
                loggerError(e);
            }
        }
        await this.testContext.testEnded();
    }
}
exports.PuppeteerPageHooker = PuppeteerPageHooker;
