import { createLogger } from "../../utils/logger/index.js";
import { init_logger } from "../../utils/logger.js";
//#region src/browser/providers/extension.ts
init_logger();
const log = createLogger("ExtensionProvider");
const DEFAULT_PORT = 19820;
const DEFAULT_HOST = "127.0.0.1";
const DEFAULT_CONNECTION_TIMEOUT = 3e4;
const DEFAULT_COMMAND_TIMEOUT = 3e4;
/**
* Manages a WebSocket server that the Chrome Extension connects to.
* Sends commands and receives results over the connection.
*/
var ExtensionBrowserProvider = class {
	name = "extension";
	server = null;
	wss = null;
	clientWs = null;
	pending = /* @__PURE__ */ new Map();
	commandCounter = 0;
	connected = false;
	config;
	connectionWaiters = [];
	constructor(config) {
		this.config = {
			port: config?.port ?? DEFAULT_PORT,
			host: config?.host ?? DEFAULT_HOST,
			connectionTimeout: config?.connectionTimeout ?? DEFAULT_CONNECTION_TIMEOUT,
			commandTimeout: config?.commandTimeout ?? DEFAULT_COMMAND_TIMEOUT
		};
	}
	/** Start the WebSocket server and wait for the extension to connect. */
	async start() {
		if (this.server) return;
		const wsModule = await import("ws");
		let WssClass = wsModule.WebSocketServer;
		if (typeof WssClass !== "function") {
			const def = wsModule.default;
			WssClass = def?.WebSocketServer ?? def;
		}
		if (typeof WssClass !== "function") throw new Error("Failed to resolve WebSocketServer from \"ws\" package");
		const WebSocketServer = WssClass;
		const http = await import("node:http");
		this.server = http.createServer((_req, res) => {
			res.writeHead(200, { "Content-Type": "application/json" });
			res.end(JSON.stringify({
				ok: true,
				connected: this.connected
			}));
		});
		this.wss = new WebSocketServer({
			server: this.server,
			path: "/browser-ext"
		});
		this.wss.on("connection", (ws) => {
			log.info("Chrome Extension connected");
			this.clientWs = ws;
			this.connected = true;
			for (const waiter of this.connectionWaiters) waiter.resolve();
			this.connectionWaiters = [];
			ws.on("message", (data) => {
				this._handleMessage(data.toString());
			});
			ws.on("close", () => {
				log.warn("Chrome Extension disconnected");
				this.clientWs = null;
				this.connected = false;
				for (const [id, req] of this.pending) {
					clearTimeout(req.timer);
					req.reject(/* @__PURE__ */ new Error("Extension disconnected"));
					this.pending.delete(id);
				}
			});
		});
		const wssEmitter = this.wss;
		await new Promise((resolve, reject) => {
			let settled = false;
			const disposeFailedStart = async () => {
				if (this.wss) {
					try {
						this.wss.close();
					} catch {}
					this.wss = null;
				}
				if (this.server) {
					await new Promise((r) => {
						this.server.close(() => r());
					});
					this.server = null;
				}
			};
			const onStartError = (err) => {
				if (settled) return;
				settled = true;
				this.server.removeListener("error", onStartError);
				wssEmitter.removeListener("error", onStartError);
				disposeFailedStart().then(() => reject(err));
			};
			const onListening = () => {
				if (settled) return;
				settled = true;
				this.server.removeListener("error", onStartError);
				wssEmitter.removeListener("error", onStartError);
				log.info({
					port: this.config.port,
					host: this.config.host
				}, "Extension WS server started");
				const onRuntimeError = (err) => {
					log.error({ err }, "Extension WS bridge runtime error");
				};
				this.server.on("error", onRuntimeError);
				wssEmitter.on("error", onRuntimeError);
				resolve();
			};
			this.server.on("error", onStartError);
			wssEmitter.on("error", onStartError);
			this.server.listen(this.config.port, this.config.host, onListening);
		});
	}
	/** Wait for the Chrome Extension to connect. */
	async waitForConnection(timeoutMs) {
		if (this.connected) return;
		const timeout = timeoutMs ?? this.config.connectionTimeout;
		return new Promise((resolve, reject) => {
			const timer = setTimeout(() => {
				const idx = this.connectionWaiters.findIndex((w) => w.resolve === resolve);
				if (idx >= 0) this.connectionWaiters.splice(idx, 1);
				reject(/* @__PURE__ */ new Error(`Extension connection timeout after ${timeout}ms. Is the Chrome Extension installed and enabled?`));
			}, timeout);
			this.connectionWaiters.push({
				resolve: () => {
					clearTimeout(timer);
					resolve();
				},
				reject: (e) => {
					clearTimeout(timer);
					reject(e);
				}
			});
		});
	}
	/** Send a command to the extension and wait for the result. */
	async sendCommand(action, args, options) {
		if (!this.connected || !this.clientWs) throw new Error("Extension not connected. Ensure the Chrome Extension is installed and connected.");
		const id = `cmd_${++this.commandCounter}_${Date.now()}`;
		const cmd = {
			id,
			action,
			args,
			tabId: options?.tabId,
			timeout: options?.timeout ?? this.config.commandTimeout
		};
		return new Promise((resolve, reject) => {
			const timer = setTimeout(() => {
				this.pending.delete(id);
				reject(/* @__PURE__ */ new Error(`Command timeout: ${action} (${cmd.timeout}ms)`));
			}, cmd.timeout);
			this.pending.set(id, {
				resolve,
				reject,
				timer
			});
			try {
				this.clientWs.send(JSON.stringify(cmd));
			} catch (e) {
				clearTimeout(timer);
				this.pending.delete(id);
				reject(e instanceof Error ? e : new Error(String(e)));
			}
		});
	}
	/** Whether the extension is currently connected. */
	isConnected() {
		return this.connected;
	}
	/** Close the Chrome Extension client without stopping the WebSocket server. */
	disconnectClient() {
		if (!this.clientWs) return;
		try {
			this.clientWs.close();
		} catch {}
	}
	/** Shutdown the WebSocket server. */
	async shutdown() {
		for (const waiter of this.connectionWaiters) waiter.reject(/* @__PURE__ */ new Error("Provider shutting down"));
		this.connectionWaiters = [];
		for (const [id, req] of this.pending) {
			clearTimeout(req.timer);
			req.reject(/* @__PURE__ */ new Error("Provider shutting down"));
			this.pending.delete(id);
		}
		if (this.clientWs) {
			try {
				this.clientWs.close();
			} catch {}
			this.clientWs = null;
		}
		if (this.wss) {
			try {
				this.wss.close();
			} catch {}
			this.wss = null;
		}
		if (this.server) {
			await new Promise((resolve) => {
				this.server.close(() => resolve());
			});
			this.server = null;
		}
		this.connected = false;
		log.info("Extension provider shut down");
	}
	_handleMessage(raw) {
		try {
			const msg = JSON.parse(raw);
			if (msg.type === "status") {
				log.debug(msg, "Extension status");
				return;
			}
			if (msg.type === "network_event") {
				log.debug({
					listenerId: msg.listenerId,
					eventType: msg.eventType
				}, "Network event");
				return;
			}
			const id = msg.id;
			const pending = this.pending.get(id);
			if (pending) {
				clearTimeout(pending.timer);
				this.pending.delete(id);
				pending.resolve(msg);
			}
		} catch (e) {
			log.error({ err: e }, "Failed to parse extension message");
		}
	}
};
//#endregion
export { ExtensionBrowserProvider };

//# sourceMappingURL=extension.js.map