import { Browser, Page, chromium } from 'playwright';
import { VendorListing, ScrapeResult, ScraperConfig } from './types';

export abstract class BaseScraper {
  protected config: ScraperConfig;
  protected browser: Browser | null = null;

  constructor(config: Partial<ScraperConfig> = {}) {
    this.config = {
      maxRetries: 3,
      retryDelay: 2000,
      timeout: 30000,
      headless: true,
      ...config,
    };
  }

  abstract getVendorName(): 'eldorado' | 'playerauctions';
  abstract getTargetUrl(): string;
  abstract extractListings(page: Page): Promise<VendorListing[]>;

  async scrape(): Promise<ScrapeResult> {
    const vendor = this.getVendorName();
    let lastError: string | undefined;

    for (let attempt = 1; attempt <= this.config.maxRetries; attempt++) {
      try {
        const listings = await this.performScrape();
        
        return {
          success: true,
          vendor,
          listings,
          scrapedAt: new Date(),
        };
      } catch (error) {
        lastError = error instanceof Error ? error.message : String(error);
        
        if (attempt < this.config.maxRetries) {
          await this.delay(this.config.retryDelay);
        }
      }
    }

    return {
      success: false,
      vendor,
      listings: [],
      error: lastError,
      scrapedAt: new Date(),
    };
  }

  private async performScrape(): Promise<VendorListing[]> {
    this.browser = await chromium.launch({ headless: this.config.headless });
    
    try {
      const context = await this.browser.newContext({
        userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
      });
      
      const page = await context.newPage();
      page.setDefaultTimeout(this.config.timeout);
      
      await page.goto(this.getTargetUrl(), { waitUntil: 'networkidle' });
      
      const listings = await this.extractListings(page);
      
      await context.close();
      
      return listings;
    } finally {
      await this.browser?.close();
      this.browser = null;
    }
  }

  protected calculatePricePerMillion(amountAUEC: number, priceUSD: number): number {
    return (priceUSD / amountAUEC) * 1_000_000;
  }

  protected delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  async close(): Promise<void> {
    if (this.browser) {
      await this.browser.close();
      this.browser = null;
    }
  }
}
