import { Page } from 'playwright';

export interface HydrationMismatch {
  element: string;
  serverHTML: string;
  clientHTML: string;
  timestamp: Date;
  stackTrace?: string;
}

export interface BundleInfo {
  url: string;
  size: number;
  gzipSize?: number;
  loadTime: number;
  parseTime?: number;
  cacheStatus: 'hit' | 'miss' | 'none';
  isLazyLoaded: boolean;
  coverage?: number; // Percentage of code used
}

export interface RouteChange {
  from: string;
  to: string;
  duration: number;
  timestamp: Date;
  type: 'push' | 'replace' | 'pop';
  dataFetched?: number; // bytes
}

export class ProblemDebugEngine {
  private hydrationMismatches: HydrationMismatch[] = [];
  private bundles: Map<string, BundleInfo> = new Map();
  private routeChanges: RouteChange[] = [];
  private page?: Page;
  
  async attachToPage(page: Page): Promise<void> {
    this.page = page;
    
    // Inject problem detection scripts
    await this.injectHydrationDetection();
    await this.setupBundleMonitoring();
    await this.setupRouteMonitoring();
  }
  
  private async injectHydrationDetection(): Promise<void> {
    if (!this.page) return;
    
    await this.page.addInitScript(() => {
      // Create hydration mismatch detector
      (window as any).__HYDRATION_DEBUG__ = {
        mismatches: [],
        originalError: console.error,
        
        detectMismatch: function(args: any[]) {
          const errorStr = args.join(' ');
          
          // Common hydration error patterns
          const patterns = [
            /Text content does not match/i,
            /Hydration failed/i,
            /did not match/i,
            /Warning:.*?during hydration/i,
            /Prop `.*?` did not match/i,
            /Expected server HTML/i
          ];
          
          if (patterns.some(p => p.test(errorStr))) {
            // Try to extract details
            const stack = new Error().stack;
            this.mismatches.push({
              message: errorStr,
              timestamp: new Date().toISOString(),
              stack: stack,
              url: window.location.href
            });
          }
        }
      };
      
      // Override console.error to catch hydration warnings
      console.error = function(...args) {
        (window as any).__HYDRATION_DEBUG__.detectMismatch(args);
        return (window as any).__HYDRATION_DEBUG__.originalError.apply(console, args);
      };
      
      // Also catch React specific hydration errors
      if ((window as any).React) {
        const originalCreateElement = (window as any).React.createElement;
        (window as any).React.createElement = function(...args: any[]) {
          try {
            return originalCreateElement.apply(this, args);
          } catch (error: any) {
            if (error.message?.includes('hydrat')) {
              (window as any).__HYDRATION_DEBUG__.mismatches.push({
                message: error.message,
                timestamp: new Date().toISOString(),
                component: args[0]?.name || 'Unknown'
              });
            }
            throw error;
          }
        };
      }
    });
  }
  
  private async setupBundleMonitoring(): Promise<void> {
    if (!this.page) return;
    
    // Monitor resource timing for bundles
    this.page.on('response', async (response) => {
      const url = response.url();
      const contentType = response.headers()['content-type'] || '';
      
      // Track JavaScript bundles
      if (contentType.includes('javascript') || url.endsWith('.js')) {
        const request = response.request();
        const timing = (response as any).timing?.();
        
        const size = parseInt(response.headers()['content-length'] || '0');
        const gzipSize = response.headers()['content-encoding'] === 'gzip' ? size : undefined;
        
        const bundleInfo: BundleInfo = {
          url,
          size,
          gzipSize,
          loadTime: timing ? timing.responseEnd - timing.requestStart : 0,
          cacheStatus: this.getCacheStatus(response),
          isLazyLoaded: this.isLazyLoaded(request)
        };
        
        this.bundles.set(url, bundleInfo);
      }
    });
    
    // Inject coverage tracking
    await this.page.coverage.startJSCoverage();
  }
  
  private getCacheStatus(response: any): 'hit' | 'miss' | 'none' {
    const cacheControl = response.headers()['cache-control'];
    const etag = response.headers()['etag'];
    const status = response.status();
    
    if (status === 304) return 'hit';
    if (cacheControl?.includes('no-cache')) return 'none';
    if (etag) return 'miss'; // Has etag but not 304
    return 'none';
  }
  
  private isLazyLoaded(request: any): boolean {
    // Check if loaded after initial page load
    const timing = request.timing();
    if (!timing) return false;
    
    // If request started more than 1s after navigation, likely lazy loaded
    return timing.requestStart > 1000;
  }
  
  private async setupRouteMonitoring(): Promise<void> {
    if (!this.page) return;
    
    await this.page.addInitScript(() => {
      (window as any).__ROUTE_DEBUG__ = {
        changes: [],
        lastUrl: window.location.href,
        startTime: Date.now(),
        
        recordChange: function(from: string, to: string, type: string) {
          this.changes.push({
            from,
            to,
            type,
            timestamp: new Date().toISOString(),
            duration: Date.now() - this.startTime
          });
          this.lastUrl = to;
          this.startTime = Date.now();
        }
      };
      
      // Monitor pushState/replaceState
      const originalPushState = history.pushState;
      const originalReplaceState = history.replaceState;
      
      history.pushState = function(...args) {
        const from = window.location.href;
        originalPushState.apply(history, args);
        const to = window.location.href;
        (window as any).__ROUTE_DEBUG__.recordChange(from, to, 'push');
      };
      
      history.replaceState = function(...args) {
        const from = window.location.href;
        originalReplaceState.apply(history, args);
        const to = window.location.href;
        (window as any).__ROUTE_DEBUG__.recordChange(from, to, 'replace');
      };
      
      // Monitor popstate (back/forward)
      window.addEventListener('popstate', () => {
        const from = (window as any).__ROUTE_DEBUG__.lastUrl;
        const to = window.location.href;
        (window as any).__ROUTE_DEBUG__.recordChange(from, to, 'pop');
      });
    });
  }
  
  async getHydrationIssues(): Promise<HydrationMismatch[]> {
    if (!this.page) return [];
    
    const issues = await this.page.evaluate(() => {
      return (window as any).__HYDRATION_DEBUG__?.mismatches || [];
    });
    
    return issues.map((issue: any) => ({
      element: this.extractElementFromError(issue.message),
      serverHTML: this.extractServerHTML(issue.message),
      clientHTML: this.extractClientHTML(issue.message),
      timestamp: new Date(issue.timestamp),
      stackTrace: issue.stack
    }));
  }
  
  private extractElementFromError(message: string): string {
    // Try to extract element info from common error messages
    const patterns = [
      /Warning:.*?<(\w+).*?>/,
      /component:?\s*(\w+)/i,
      /element\s+(\w+)/i
    ];
    
    for (const pattern of patterns) {
      const match = message.match(pattern);
      if (match) return match[1];
    }
    
    return 'Unknown element';
  }
  
  private extractServerHTML(message: string): string {
    const match = message.match(/server:?\s*(.+?)(?:client:|$)/i);
    return match ? match[1].trim() : 'N/A';
  }
  
  private extractClientHTML(message: string): string {
    const match = message.match(/client:?\s*(.+?)$/i);
    return match ? match[1].trim() : 'N/A';
  }
  
  async getBundleAnalysis(): Promise<{
    bundles: BundleInfo[];
    totalSize: number;
    lazyLoadedSize: number;
    cacheHitRate: number;
    coverage?: { used: number; total: number; percentage?: number };
  }> {
    const bundles = Array.from(this.bundles.values());
    
    // Get JS coverage if available
    let coverage;
    if (this.page) {
      try {
        const jsCoverage = await this.page.coverage.stopJSCoverage();
        const totalBytes = jsCoverage.reduce((sum, entry) => sum + (entry as any).source.length, 0);
        const usedBytes = jsCoverage.reduce((sum, entry) => {
          return sum + (entry.functions || []).reduce((s: number, func: any) => {
            return s + func.ranges.reduce((rs: number, range: any) => rs + (range.end - range.start), 0);
          }, 0);
        }, 0);
        
        coverage = {
          used: usedBytes,
          total: totalBytes
        };
        
        // Update bundle coverage
        jsCoverage.forEach(entry => {
          const bundle = this.bundles.get(entry.url);
          if (bundle) {
            const usedInBundle = (entry.functions || []).reduce((s: number, func: any) => {
              return s + func.ranges.reduce((rs: number, range: any) => rs + (range.end - range.start), 0);
            }, 0);
            bundle.coverage = (usedInBundle / (entry as any).source.length) * 100;
          }
        });
        
        // Restart coverage
        await this.page.coverage.startJSCoverage();
      } catch (error) {
        console.error('Coverage analysis failed:', error);
      }
    }
    
    const totalSize = bundles.reduce((sum, b) => sum + b.size, 0);
    const lazyLoadedSize = bundles.filter(b => b.isLazyLoaded).reduce((sum, b) => sum + b.size, 0);
    const cacheHits = bundles.filter(b => b.cacheStatus === 'hit').length;
    const cacheHitRate = bundles.length > 0 ? (cacheHits / bundles.length) * 100 : 0;
    
    return {
      bundles: bundles.sort((a, b) => b.size - a.size), // Largest first
      totalSize,
      lazyLoadedSize,
      cacheHitRate,
      coverage
    };
  }
  
  async getRouteChanges(): Promise<RouteChange[]> {
    if (!this.page) return this.routeChanges;
    
    const changes = await this.page.evaluate(() => {
      return (window as any).__ROUTE_DEBUG__?.changes || [];
    });
    
    // Merge with any we've tracked
    const allChanges = [...this.routeChanges];
    
    changes.forEach((change: any) => {
      if (!allChanges.some(c => c.timestamp.toISOString() === change.timestamp)) {
        allChanges.push({
          from: change.from,
          to: change.to,
          duration: change.duration,
          timestamp: new Date(change.timestamp),
          type: change.type as 'push' | 'replace' | 'pop'
        });
      }
    });
    
    return allChanges.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
  }
  
  async detectCommonProblems(): Promise<{
    problem: string;
    severity: 'low' | 'medium' | 'high';
    description: string;
    solution: string;
  }[]> {
    const problems = [];
    
    // Check hydration issues
    const hydrationIssues = await this.getHydrationIssues();
    if (hydrationIssues.length > 0) {
      problems.push({
        problem: 'Hydration Mismatches',
        severity: 'high' as const,
        description: `Found ${hydrationIssues.length} hydration errors. Server and client HTML don't match.`,
        solution: 'Ensure consistent rendering between server and client. Check for browser-only APIs, random values, or date/time differences.'
      });
    }
    
    // Check bundle sizes
    const bundleAnalysis = await this.getBundleAnalysis();
    const largeBundles = bundleAnalysis.bundles.filter(b => b.size > 500 * 1024); // 500KB
    if (largeBundles.length > 0) {
      problems.push({
        problem: 'Large JavaScript Bundles',
        severity: 'medium' as const,
        description: `${largeBundles.length} bundles exceed 500KB. Largest: ${(largeBundles[0].size / 1024 / 1024).toFixed(2)}MB`,
        solution: 'Enable code splitting, lazy load routes, and tree-shake unused code.'
      });
    }
    
    // Check code coverage
    if (bundleAnalysis.coverage) {
      const percentage = bundleAnalysis.coverage.total > 0 
        ? (bundleAnalysis.coverage.used / bundleAnalysis.coverage.total) * 100 
        : 0;
      if (percentage < 50) {
        problems.push({
          problem: 'Low Code Usage',
          severity: 'medium' as const,
          description: `Only ${percentage.toFixed(1)}% of loaded JavaScript is being used.`,
          solution: 'Remove unused dependencies, enable tree-shaking, and lazy load features.'
        });
      }
    }
    
    // Check route performance
    const routeChanges = await this.getRouteChanges();
    const slowRoutes = routeChanges.filter(r => r.duration > 1000); // 1s
    if (slowRoutes.length > 0) {
      problems.push({
        problem: 'Slow Route Transitions',
        severity: 'medium' as const,
        description: `${slowRoutes.length} route changes took over 1 second.`,
        solution: 'Optimize data fetching, add loading states, and consider prefetching.'
      });
    }
    
    // Check cache efficiency
    if (bundleAnalysis.cacheHitRate < 50 && bundleAnalysis.bundles.length > 5) {
      problems.push({
        problem: 'Poor Cache Utilization',
        severity: 'low' as const,
        description: `Only ${bundleAnalysis.cacheHitRate.toFixed(1)}% cache hit rate for JavaScript bundles.`,
        solution: 'Configure proper cache headers and use content hashing for static assets.'
      });
    }
    
    return problems;
  }
}