import { chromium } from 'playwright';
import type { BrowserContext, Page, Browser as PlaywrightBrowser } from 'playwright';
import type { BrowserOptions, NetworkEvent, NetworkEventHandler } from './types';

export class Browser {
  private browser?: PlaywrightBrowser;
  private contexts: Map<string, BrowserContext> = new Map();
  private logger: any;
  private options: BrowserOptions;
  private initialized = false;

  constructor(logger?: any, defaultOptions?: BrowserOptions) {
    this.logger = logger || console;
    this.options = {
      headless: true,
      timeout: 30000,
      blockResources: false,
      enableNetworkLogging: false,
      ...defaultOptions,
    };
  }

  async initialize(options: BrowserOptions = {}): Promise<void> {
    if (this.initialized) {
      return;
    }

    // Merge options
    this.options = {
      ...this.options,
      ...options,
    };

    this.logger.info('Initializing browser...');

    try {
      this.browser = await chromium.launch({
        headless: this.options.headless,
        timeout: this.options.timeout,
        args: [
          // Security and sandbox
          '--no-sandbox',
          // '--disable-setuid-sandbox',
          // '--disable-dev-shm-usage',
          // '--disable-web-security',
          // '--disable-features=VizDisplayCompositor',
          // '--disable-blink-features=AutomationControlled',

          // // Performance optimizations
          // '--disable-gpu',
          // '--disable-gpu-sandbox',
          // '--disable-software-rasterizer',
          // '--disable-background-timer-throttling',
          // '--disable-renderer-backgrounding',
          // '--disable-backgrounding-occluded-windows',
          // '--disable-field-trial-config',
          // '--disable-back-forward-cache',
          // '--disable-hang-monitor',
          // '--disable-ipc-flooding-protection',

          // // Extensions and plugins
          // '--disable-extensions',
          // '--disable-plugins',
          // '--disable-component-extensions-with-background-pages',
          // '--disable-component-update',
          // '--disable-plugins-discovery',
          // '--disable-bundled-ppapi-flash',

          // // Features we don't need
          // '--disable-default-apps',
          // '--disable-sync',
          // '--disable-translate',
          // '--disable-client-side-phishing-detection',
          // '--disable-domain-reliability',
          // '--disable-features=TranslateUI',
          // '--disable-features=Translate',
          // '--disable-breakpad',
          // '--disable-preconnect',
          // '--disable-print-preview',
          // '--disable-password-generation',
          // '--disable-password-manager-reauthentication',
          // '--disable-save-password-bubble',
          // '--disable-single-click-autofill',
          // '--disable-autofill',
          // '--disable-autofill-keyboard-accessory-view',
          // '--disable-full-form-autofill-ios',

          // // Audio/Video/Media
          // '--mute-audio',
          // '--disable-audio-output',
          // '--autoplay-policy=user-gesture-required',
          // '--disable-background-media-playback',

          // // Networking
          // '--disable-background-networking',
          // '--disable-sync',
          // '--aggressive-cache-discard',
          // '--disable-default-apps',

          // // UI/UX optimizations
          // '--no-first-run',
          // '--disable-infobars',
          // '--disable-notifications',
          // '--disable-desktop-notifications',
          // '--disable-prompt-on-repost',
          // '--disable-logging',
          // '--disable-file-system',
          // '--hide-scrollbars',

          // // Memory optimizations
          // '--memory-pressure-off',
          // '--max_old_space_size=4096',
          // '--js-flags="--max-old-space-size=4096"',
          // '--media-cache-size=1',
          // '--disk-cache-size=1',

          // // Process management
          // '--use-mock-keychain',
          // '--password-store=basic',
          // '--enable-automation',
          // '--no-pings',
          // '--no-service-autorun',
          // '--metrics-recording-only',
          // '--safebrowsing-disable-auto-update',

          // // Disable unnecessary features for headless mode
          // '--disable-speech-api',
          // '--disable-gesture-typing',
          // '--disable-voice-input',
          // '--disable-wake-on-wifi',
          // '--disable-webgl',
          // '--disable-webgl2',
          // '--disable-3d-apis',
          // '--disable-accelerated-2d-canvas',
          // '--disable-accelerated-jpeg-decoding',
          // '--disable-accelerated-mjpeg-decode',
          // '--disable-accelerated-video-decode',
          // '--disable-canvas-aa',
          // '--disable-2d-canvas-clip-aa',
          // '--disable-gl-drawing-for-tests',
        ],
      });

      this.initialized = true;
      this.logger.info('Browser initialized successfully');
    } catch (error) {
      this.logger.error('Failed to initialize browser', { error });
      throw error;
    }
  }

  async createPageWithProxy(
    url: string,
    proxy?: string
  ): Promise<{
    page: Page & {
      onNetworkEvent: (handler: NetworkEventHandler) => void;
      offNetworkEvent: (handler: NetworkEventHandler) => void;
      clearNetworkListeners: () => void;
    };
    contextId: string;
  }> {
    if (!this.browser) {
      throw new Error('Browser not initialized. Call Browser.initialize() first.');
    }

    const contextId = `ctx-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;

    const contextOptions: Record<string, unknown> = {
      ignoreHTTPSErrors: true,
      bypassCSP: true,
    };

    if (proxy) {
      const [protocol, rest] = proxy.split('://');
      if (!rest) {
        throw new Error(
          'Invalid proxy format. Expected protocol://host:port or protocol://user:pass@host:port'
        );
      }

      const [auth, hostPort] = rest.includes('@') ? rest.split('@') : [null, rest];
      const finalHostPort = hostPort || rest;
      const [host, port] = finalHostPort.split(':');

      contextOptions['proxy'] = {
        server: `${protocol}://${host}:${port}`,
        username: auth?.split(':')[0] || '',
        password: auth?.split(':')[1] || '',
      };
    }

    const context = await this.browser.newContext(contextOptions);

    // Block resources for performance
    if (this.options.blockResources) {
      await context.route('**/*.{png,jpg,jpeg,gif,svg,ico,woff,woff2,ttf,css}', route => {
        route.abort();
      });
    }

    this.contexts.set(contextId, context);

    const page = await context.newPage();
    page.setDefaultTimeout(this.options.timeout || 30000);
    page.setDefaultNavigationTimeout(this.options.timeout || 30000);

    // Create network event handlers for this page
    const networkEventHandlers: Set<NetworkEventHandler> = new Set();

    // Add network monitoring methods to the page
    const enhancedPage = page as Page & {
      onNetworkEvent: (handler: NetworkEventHandler) => void;
      offNetworkEvent: (handler: NetworkEventHandler) => void;
      clearNetworkListeners: () => void;
    };

    enhancedPage.onNetworkEvent = (handler: NetworkEventHandler) => {
      networkEventHandlers.add(handler);

      // Set up network monitoring on first handler
      if (networkEventHandlers.size === 1) {
        this.setupNetworkMonitoring(page, networkEventHandlers);
      }
    };

    enhancedPage.offNetworkEvent = (handler: NetworkEventHandler) => {
      networkEventHandlers.delete(handler);
    };

    enhancedPage.clearNetworkListeners = () => {
      networkEventHandlers.clear();
    };

    if (url) {
      await page.goto(url, {
        waitUntil: 'domcontentloaded',
        timeout: this.options.timeout,
      });
    }

    return { page: enhancedPage, contextId };
  }

  private setupNetworkMonitoring(page: Page, handlers: Set<NetworkEventHandler>): void {
    // Listen to requests
    page.on('request', async request => {
      const event: NetworkEvent = {
        url: request.url(),
        method: request.method(),
        type: 'request',
        timestamp: Date.now(),
        headers: request.headers(),
      };

      // Capture request data for POST/PUT/PATCH requests
      if (['POST', 'PUT', 'PATCH'].includes(request.method())) {
        try {
          const postData = request.postData();
          if (postData) {
            event.requestData = postData;
          }
        } catch {
          // Some requests might not have accessible post data
        }
      }

      this.emitNetworkEvent(event, handlers);
    });

    // Listen to responses
    page.on('response', async response => {
      const event: NetworkEvent = {
        url: response.url(),
        method: response.request().method(),
        status: response.status(),
        type: 'response',
        timestamp: Date.now(),
        headers: response.headers(),
      };

      // Capture response data for GET/POST requests with JSON content
      const contentType = response.headers()['content-type'] || '';
      if (contentType.includes('application/json') || contentType.includes('text/')) {
        try {
          const responseData = await response.text();
          event.responseData = responseData;
        } catch {
          // Response might be too large or not accessible
        }
      }

      this.emitNetworkEvent(event, handlers);
    });

    // Listen to failed requests
    page.on('requestfailed', request => {
      const event: NetworkEvent = {
        url: request.url(),
        method: request.method(),
        type: 'failed',
        timestamp: Date.now(),
        headers: request.headers(),
      };

      // Try to capture request data for failed requests too
      if (['POST', 'PUT', 'PATCH'].includes(request.method())) {
        try {
          const postData = request.postData();
          if (postData) {
            event.requestData = postData;
          }
        } catch {
          // Ignore errors when accessing post data
        }
      }

      this.emitNetworkEvent(event, handlers);
    });
  }

  private emitNetworkEvent(event: NetworkEvent, handlers: Set<NetworkEventHandler>): void {
    for (const handler of handlers) {
      try {
        handler(event);
      } catch (error) {
        this.logger.error('Network event handler error', { error });
      }
    }
  }

  async evaluate<T>(page: Page, fn: () => T): Promise<T> {
    return page.evaluate(fn);
  }

  async closeContext(contextId: string): Promise<void> {
    const context = this.contexts.get(contextId);
    if (context) {
      await context.close();
      this.contexts.delete(contextId);
    }
  }

  async close(): Promise<void> {
    // Close all contexts
    for (const [, context] of this.contexts) {
      await context.close();
    }
    this.contexts.clear();

    // Close browser
    if (this.browser) {
      await this.browser.close();
      this.browser = undefined;
    }

    this.initialized = false;
    this.logger.info('Browser closed');
  }

  get isInitialized(): boolean {
    return this.initialized;
  }
}

// Export default for backward compatibility
export default Browser;
