"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ContextList = exports.Context = void 0;
const uuid_1 = require("uuid");
const exception_js_1 = require("../../exception.js");
const element_js_1 = require("../element.js");
const util = __importStar(require("../util/index.js"));
const action_js_1 = require("./action.js");
const entity_js_1 = require("./entity.js");
const list_js_1 = require("./list.js");
const matcher_js_1 = require("./matcher.js");
class Context extends entity_js_1.Entity {
    constructor(owner, spec = {}) {
        super(owner);
        this.id = (0, uuid_1.v4)();
        const { id = (0, uuid_1.v4)(), name = 'New Context', type = 'context', flowType = 'normal', errorCode = null, limit = 1, matchMode = 'fast', resolve3dsecure = true, } = spec;
        this.id = id;
        this.name = type === 'context' ? name : `<${type}>`;
        this.type = type;
        this.flowType = flowType;
        this.errorCode = errorCode;
        this.limit = limit;
        this.matchMode = matchMode;
        this.resolve3dsecure = resolve3dsecure;
        this.matchers = new action_js_1.MatcherList(this, 'matchers', spec.matchers || []);
        this.definitions = new action_js_1.DefinitionList(this, 'definitions', spec.definitions || []);
        this.children = new action_js_1.ActionList(this, 'children', spec.actions || spec.children || []);
        this.reset();
        if (spec.finalType) {
            this.flowType = spec.finalType;
        }
    }
    get $entityType() { return 'context'; }
    get $index() { return this.$owner.indexOf(this); }
    get $script() { return this.$owner.$script; }
    get $context() { return this; }
    get $browser() { return this.$script.$browser; }
    get $page() { return this.$script.$page; }
    get $key() { return `items/${this.$index}`; }
    get $logger() {
        return this.$script.$logger;
    }
    reset() {
        this.$runtime = {
            startedAt: null,
            finishedAt: null,
            visited: 0,
        };
        this.resetMatchers();
        this.resetDescendants();
    }
    resetDescendants() {
        for (const action of this.children) {
            action.resetSubtree();
        }
    }
    resetMatchers() {
        for (const matcher of this.matchers) {
            matcher.resetSubtree();
        }
    }
    getDepth() {
        return 0;
    }
    hasChildren() {
        return true;
    }
    *descendentActions() {
        for (const matcher of this.matchers) {
            yield matcher;
        }
        for (const child of this.children) {
            yield child;
            yield* child.descendentActions();
        }
        for (const definition of this.definitions) {
            yield definition;
        }
    }
    getActionById(id) {
        for (const action of this.descendentActions()) {
            if (action.id === id) {
                return action;
            }
        }
        return null;
    }
    async resolveChildrenScope() {
        const document = await this.$page.document();
        const el = new element_js_1.Element(document, {});
        return [el];
    }
    isRunning() {
        const { startedAt, finishedAt } = this.$runtime;
        return startedAt && !finishedAt;
    }
    isFinished() {
        const { startedAt, finishedAt } = this.$runtime;
        return startedAt && finishedAt;
    }
    getDuration() {
        const startedAt = this.$runtime.startedAt || 0;
        const finishedAt = this.$runtime.finishedAt || 0;
        return Math.max(0, Number(finishedAt - startedAt) || 0);
    }
    isLimitReached() {
        return this.$runtime.visited >= this.limit;
    }
    async runMatch() {
        try {
            const promises = [];
            for (const action of this.matchers) {
                if (action instanceof matcher_js_1.MatcherAction) {
                    promises.push(action.performMatch());
                }
            }
            await Promise.all(promises);
            return true;
        }
        catch (err) {
            return false;
        }
    }
    enter() {
        this.$script.$events.emit('context.enter', this);
        this.$runtime.visited += 1;
        if (this.$runtime.visited > this.limit) {
            throw new exception_js_1.ScriptException({
                name: this.errorCode ? this.errorCode : 'ContextLimitError',
                message: 'Context limit exceeded',
                retry: false,
                scriptError: this.errorCode != null,
                details: {
                    id: this.id,
                    name: this.name,
                },
            });
        }
        this.$runtime.startedAt = Date.now();
        this.resetDescendants();
        const firstAction = this.children.first;
        if (firstAction) {
            this.$script.setPlayhead(firstAction);
        }
        else {
            this.leave();
        }
    }
    leave(finalizeContext = true) {
        this.$script.$events.emit('context.leave', this);
        this.$script.setPlayhead(null);
        this.$runtime.finishedAt = Date.now();
        if (finalizeContext) {
            this.finalize();
        }
    }
    finalize() {
        const isSingleMain = this.type === 'main' && this.$script.contexts.length === 1 && this.$script.contexts.get(0) === this;
        const success = isSingleMain || this.flowType === 'success';
        if (success) {
            this.$script.setStatus('success');
        }
        else if (this.flowType === 'fail') {
            const code = this.errorCode || 'failureContext';
            throw new exception_js_1.ScriptException({
                name: code,
                message: `Expected failure: ${code}`,
                retry: false,
                scriptError: true,
            });
        }
    }
}
exports.Context = Context;
class ContextList extends list_js_1.EntityList {
    create(_spec) {
        const spec = util.cloneWithoutIdsCollision(_spec, this.$script.$ids);
        for (const type of ['main', 'checkpoint']) {
            if (spec.type === type && this.items.some(_ => _.type === type)) {
                spec.type = 'context';
            }
        }
        return new Context(this, spec);
    }
    insert(spec, index = this.items.length) {
        if (spec.type === 'main') {
            index = 0;
        }
        if (spec.type === 'checkpoint') {
            index = 1;
        }
        return super.insert(spec, index);
    }
    get $entityType() { return 'context-list'; }
    get $script() { return this.$owner; }
    get $engine() { return this.$owner.$engine; }
    get $idDatabase() { return this.$script; }
}
exports.ContextList = ContextList;
//# sourceMappingURL=context.js.map