import { Page } from '@playwright/test';

export interface CapturedError {
  type: 'console' | 'page';
  message: string;
  stack?: string;
  timestamp: number;
}


export class ConsoleErrorCapture {
  private errors: CapturedError[] = [];
  private allowlist: (string | RegExp)[] = [];

  constructor(private page: Page) {
    this.setupCapture();
  }

  public setAllowlist(patterns: (string | RegExp)[]): void {
    this.allowlist = patterns;
  }

  private isAllowed(message: string): boolean {
    return this.allowlist.some(pattern => 
      typeof pattern === 'string' ? message.includes(pattern) : pattern.test(message)
    );
  }

  private setupCapture(): void {
    this.page.on('console', (msg) => {
      if (msg.type() === 'error') {
        this.errors.push({
          type: 'console',
          message: msg.text(),
          timestamp: Date.now(),
        });
      }
    });

    this.page.on('pageerror', (error) => {
      this.errors.push({
        type: 'page',
        message: error.message,
        stack: error.stack ?? '',
        timestamp: Date.now(),
      });
    });
  }

  public getErrors(): CapturedError[] {
    return this.errors;
  }

  public getUnexpectedErrors(): CapturedError[] {
    return this.errors.filter(e => !this.isAllowed(e.message));
  }

  public format(): string {
    if (this.errors.length === 0) return 'No console errors captured.';

    const unexpected = this.getUnexpectedErrors();
    const allowed = this.errors.filter(e => this.isAllowed(e.message));

    let output = '--- Console Error Capture ---\n';
    
    if (unexpected.length > 0) {
      output += `UNEXPECTED ERRORS (${unexpected.length}):\n`;
      unexpected.forEach((e, i) => {
        output += `[${i + 1}] ${e.type.toUpperCase()}: ${e.message}\n`;
        if (e.stack) output += `Stack: ${e.stack}\n`;
      });
    }

    if (allowed.length > 0) {
      output += `\nALLOWED ERRORS (${allowed.length}):\n`;
      allowed.forEach((e, i) => {
        output += `[${i + 1}] ${e.type.toUpperCase()}: ${e.message}\n`;
      });
    }

    return output;
  }

  public hasErrors(): boolean {
    return this.errors.length > 0;
  }

  public hasUnexpectedErrors(): boolean {
    return this.getUnexpectedErrors().length > 0;
  }

  public clear(): void {
    this.errors = [];
  }

  public async waitForErrors(timeout: number = 1000): Promise<boolean> {
    await this.page.waitForTimeout(timeout);
    return this.hasErrors();
  }
}