import { EventEmitter } from 'events';
import { CdpClient } from './cdp-client.js';
import { annotateWindowKind } from './cursor-window-kind.js';
import { detectForegroundTargetId } from './focused-window.js';
export class CDPBridge extends EventEmitter {
    config;
    client = null;
    reconnectTimer = null;
    refreshTimer = null;
    backoffMs = 1000;
    focusSyncSuppressedUntil = 0;
    windows = [];
    activeTargetId = '';
    constructor(config) {
        super();
        this.config = config;
    }
    getClient() {
        return this.client;
    }
    isConnected() {
        return this.client?.isConnected() ?? false;
    }
    suppressFocusSync(ms) {
        const until = Date.now() + Math.max(0, ms);
        if (until > this.focusSyncSuppressedUntil)
            this.focusSyncSuppressedUntil = until;
    }
    isFocusSyncSuppressed() {
        return Date.now() < this.focusSyncSuppressedUntil;
    }
    async start() {
        await this.refreshWindows();
        if (this.windows.length > 0 && !this.activeTargetId) {
            this.activeTargetId = this.windows[0].id;
        }
        await this.connectActive();
        this.refreshTimer = setInterval(() => void this.refreshWindows(), 10_000);
    }
    stop() {
        if (this.reconnectTimer)
            clearTimeout(this.reconnectTimer);
        if (this.refreshTimer)
            clearInterval(this.refreshTimer);
        this.client?.disconnect();
        this.client = null;
    }
    async refreshWindows() {
        try {
            const res = await fetch(`${this.config.cdpUrl}/json`);
            const targets = (await res.json());
            this.windows = targets
                .filter((t) => t.type === 'page' && t.url.includes('workbench'))
                .map((t) => annotateWindowKind({
                id: t.id,
                title: parseWindowTitle(t.title),
                workspace: parseWindowTitle(t.title),
                wsUrl: t.webSocketDebuggerUrl,
            }));
            if (this.activeTargetId && !this.windows.some((w) => w.id === this.activeTargetId)) {
                this.activeTargetId = this.windows[0]?.id ?? '';
            }
        }
        catch (err) {
            console.warn('[cdp-bridge] refreshWindows:', err.message);
        }
    }
    async switchWindow(targetId) {
        if (targetId === this.activeTargetId && this.client?.isConnected())
            return;
        // Prevent background focus-follow from racing window switches.
        this.suppressFocusSync(2000);
        this.activeTargetId = targetId;
        await this.connectActive();
    }
    /** Switch home CDP connection to the Cursor window that currently has focus. */
    async syncToForegroundWindow() {
        if (this.windows.length <= 1)
            return false;
        if (this.isFocusSyncSuppressed())
            return false;
        const nextId = await detectForegroundTargetId(this.windows, this.activeTargetId);
        if (!nextId || nextId === this.activeTargetId)
            return false;
        const title = this.windows.find((w) => w.id === nextId)?.title ?? nextId;
        console.log(`[cdp-bridge] Follow Cursor focus → "${title}"`);
        await this.switchWindow(nextId);
        return true;
    }
    async connectActive() {
        if (!this.activeTargetId)
            return;
        try {
            const res = await fetch(`${this.config.cdpUrl}/json`);
            const targets = (await res.json());
            const target = targets.find((t) => t.id === this.activeTargetId);
            if (!target?.webSocketDebuggerUrl) {
                throw new Error('Target not found');
            }
            this.client?.disconnect();
            const client = new CdpClient();
            client.on('disconnected', () => {
                this.emit('disconnected');
                this.scheduleReconnect();
            });
            await client.connect(target.webSocketDebuggerUrl);
            this.client = client;
            this.backoffMs = 1000;
            this.emit('connected');
            console.log(`[cdp-bridge] Connected: ${target.title}`);
        }
        catch (err) {
            console.warn('[cdp-bridge] connect failed:', err.message);
            this.emit('disconnected');
            this.scheduleReconnect();
        }
    }
    scheduleReconnect() {
        if (this.reconnectTimer)
            return;
        this.reconnectTimer = setTimeout(() => {
            this.reconnectTimer = null;
            void this.connectActive();
        }, this.backoffMs);
        this.backoffMs = Math.min(this.backoffMs * 2, 30_000);
    }
}
function parseWindowTitle(title) {
    return title.replace(/\s*-\s*Cursor\s*$/i, '').trim();
}
