import { chromium } from 'playwright';
import { Logger } from '../utils/logger.js';
export class BrowserManager {
    static instance;
    browser = null;
    launchPromise = null;
    jobCount = 0;
    activeJobs = 0;
    MAX_JOBS_PER_BROWSER = parseInt(process.env.BROWSER_RECYCLE_COUNT || '500');
    constructor() { }
    static getInstance() {
        if (!BrowserManager.instance) {
            BrowserManager.instance = new BrowserManager();
        }
        return BrowserManager.instance;
    }
    /**
     * Returns a new isolated browser context.
     * Launches the browser if it doesn't exist or needs recycling.
     */
    async getNewContext(options = {}) {
        await this.ensureBrowser(options);
        const isHeadless = options.headless !== false;
        const viewport = isHeadless ? { width: 1920, height: 1080 } : null;
        const context = await this.browser.newContext({
            viewport,
            userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
        });
        this.jobCount++;
        this.activeJobs++;
        return context;
    }
    /**
     * Decrements active job count. Should be called when a job finishes.
     */
    decrementActiveJobs() {
        this.activeJobs = Math.max(0, this.activeJobs - 1);
    }
    /**
     * Ensures a browser instance is running with optimized flags.
     */
    async ensureBrowser(options) {
        if (this.launchPromise) {
            return this.launchPromise;
        }
        // Only recycle if we have no active jobs to avoid killing running tasks
        if (this.browser && this.jobCount >= this.MAX_JOBS_PER_BROWSER && this.activeJobs === 0) {
            Logger.log('[BrowserManager] Recycling idle browser...');
            await this.cleanup();
        }
        if (!this.browser) {
            this.launchPromise = (async () => {
                Logger.log('[BrowserManager] Launching shared browser instance...');
                const launchOptions = {
                    headless: options.headless !== false,
                    proxy: options.proxy,
                    args: [
                        '--disable-gpu',
                        '--disable-software-rasterizer',
                        '--disable-dev-shm-usage',
                        '--disable-background-networking',
                        '--disable-background-timer-throttling',
                        '--disable-backgrounding-occluded-windows',
                        '--disable-breakpad',
                        '--disable-client-side-phishing-detection',
                        '--disable-component-update',
                        '--disable-default-apps',
                        '--disable-extensions',
                        '--disable-sync',
                        '--disable-translate',
                        '--metrics-recording-only',
                        '--mute-audio',
                        '--no-first-run',
                        '--no-sandbox',
                        '--disable-setuid-sandbox',
                        '--start-maximized'
                    ]
                };
                this.browser = await chromium.launch(launchOptions);
                this.jobCount = 0;
                this.browser.on('disconnected', () => {
                    Logger.warn('[BrowserManager] Browser disconnected unexpectedly.');
                    this.browser = null;
                    this.launchPromise = null;
                });
            })();
            try {
                await this.launchPromise;
            }
            finally {
                this.launchPromise = null;
            }
        }
    }
    /**
     * Closes the shared browser instance.
     */
    async cleanup() {
        if (this.browser) {
            try {
                await this.browser.close();
            }
            catch (err) {
                Logger.error('[BrowserManager] Error during browser cleanup', err);
            }
            this.browser = null;
        }
    }
    /**
     * Setup resource blocking for a page.
     */
    async setupResourceBlocking(page) {
        await page.route('**/*', (route) => {
            const type = route.request().resourceType();
            const blockedTypes = ['image', 'media', 'font', 'stylesheet'];
            if (blockedTypes.includes(type)) {
                return route.abort();
            }
            return route.continue();
        });
    }
    /**
     * Setup popup and extra tab control.
     */
    setupPageProtection(context, mainPage) {
        context.on('page', async (page) => {
            if (page !== mainPage) {
                Logger.debug('[BrowserManager] Closing unexpected extra page/popup');
                await page.close().catch(() => { });
            }
        });
    }
}
