import { CDPSession } from '../api/CDPSession.js';
import { TargetCloseError, UnsupportedOperation } from '../common/Errors.js';
import { debugError } from '../common/util.js';
import { Deferred } from '../util/Deferred.js';
import { BidiRealm } from './Realm.js';
/**
 * @internal
 */
export const cdpSessions = new Map();
/**
 * @internal
 */
export class CdpSessionWrapper extends CDPSession {
    #context;
    #sessionId = Deferred.create();
    #detached = false;
    constructor(context, sessionId) {
        super();
        this.#context = context;
        if (!this.#context.supportsCdp()) {
            return;
        }
        if (sessionId) {
            this.#sessionId.resolve(sessionId);
            cdpSessions.set(sessionId, this);
        }
        else {
            context.connection
                .send('cdp.getSession', {
                context: context.id,
            })
                .then(session => {
                this.#sessionId.resolve(session.result.session);
                cdpSessions.set(session.result.session, this);
            })
                .catch(err => {
                this.#sessionId.reject(err);
            });
        }
    }
    connection() {
        return undefined;
    }
    async send(method, ...paramArgs) {
        if (!this.#context.supportsCdp()) {
            throw new UnsupportedOperation('CDP support is required for this feature. The current browser does not support CDP.');
        }
        if (this.#detached) {
            throw new TargetCloseError(`Protocol error (${method}): Session closed. Most likely the page has been closed.`);
        }
        const session = await this.#sessionId.valueOrThrow();
        const { result } = await this.#context.connection.send('cdp.sendCommand', {
            method: method,
            params: paramArgs[0],
            session,
        });
        return result.result;
    }
    async detach() {
        cdpSessions.delete(this.id());
        if (!this.#detached && this.#context.supportsCdp()) {
            await this.#context.cdpSession.send('Target.detachFromTarget', {
                sessionId: this.id(),
            });
        }
        this.#detached = true;
    }
    id() {
        const val = this.#sessionId.value();
        return val instanceof Error || val === undefined ? '' : val;
    }
}
/**
 * Internal events that the BrowsingContext class emits.
 *
 * @internal
 */
// eslint-disable-next-line @typescript-eslint/no-namespace
export var BrowsingContextEvent;
(function (BrowsingContextEvent) {
    /**
     * Emitted on the top-level context, when a descendant context is created.
     */
    BrowsingContextEvent.Created = Symbol('BrowsingContext.created');
    /**
     * Emitted on the top-level context, when a descendant context or the
     * top-level context itself is destroyed.
     */
    BrowsingContextEvent.Destroyed = Symbol('BrowsingContext.destroyed');
})(BrowsingContextEvent || (BrowsingContextEvent = {}));
/**
 * @internal
 */
export class BrowsingContext extends BidiRealm {
    #id;
    #url;
    #cdpSession;
    #parent;
    #browserName = '';
    constructor(connection, info, browserName) {
        super(connection);
        this.#id = info.context;
        this.#url = info.url;
        this.#parent = info.parent;
        this.#browserName = browserName;
        this.#cdpSession = new CdpSessionWrapper(this, undefined);
        this.on('browsingContext.domContentLoaded', this.#updateUrl.bind(this));
        this.on('browsingContext.fragmentNavigated', this.#updateUrl.bind(this));
        this.on('browsingContext.load', this.#updateUrl.bind(this));
    }
    supportsCdp() {
        return !this.#browserName.toLowerCase().includes('firefox');
    }
    #updateUrl(info) {
        this.#url = info.url;
    }
    createRealmForSandbox() {
        return new BidiRealm(this.connection);
    }
    get url() {
        return this.#url;
    }
    get id() {
        return this.#id;
    }
    get parent() {
        return this.#parent;
    }
    get cdpSession() {
        return this.#cdpSession;
    }
    async sendCdpCommand(method, ...paramArgs) {
        return await this.#cdpSession.send(method, ...paramArgs);
    }
    dispose() {
        this.removeAllListeners();
        this.connection.unregisterBrowsingContexts(this.#id);
        void this.#cdpSession.detach().catch(debugError);
    }
}
//# sourceMappingURL=BrowsingContext.js.map