import { Page } from 'playwright';

export interface NextJSPageInfo {
  path: string;
  type: 'static' | 'ssr' | 'ssg' | 'isr';
  dataFetching: ('getStaticProps' | 'getServerSideProps' | 'getStaticPaths')[];
  hasLayout: boolean;
  isAppDir: boolean;
}

export interface NextJSBuildInfo {
  buildId: string;
  runtimeConfig?: any;
  assetPrefix?: string;
  basePath?: string;
}

export interface RSCPayload {
  componentName: string;
  props: any;
  timestamp: Date;
  size: number;
}

export interface NextJSImageOptimization {
  unoptimizedImages: Array<{
    src: string;
    format: string;
    displayed: { width: number; height: number };
    actual: { width: number; height: number };
    improvement: string;
  }>;
  lazyLoadFailures: string[];
  nextImageUsage: number;
}

export class NextJSDebugEngine {
  private page?: Page;
  private rscPayloads: RSCPayload[] = [];
  private pageTransitions: Array<{
    from: string;
    to: string; 
    type: 'client' | 'server';
    duration: number;
    timestamp: Date;
  }> = [];
  
  async attachToPage(page: Page): Promise<void> {
    this.page = page;
    
    await this.injectNextJSMonitoring();
    await this.setupRSCMonitoring();
    await this.setupImageOptimizationTracking();
  }
  
  async detectNextJS(page: Page): Promise<boolean> {
    return await page.evaluate(() => {
      // Check for Next.js specific globals
      const hasNextData = !!(window as any).__NEXT_DATA__;
      const hasNextRouter = !!(window as any).next?.router;
      const hasNextHydrated = !!(window as any).__NEXT_HYDRATED;
      
      // Check for Next.js specific elements
      const hasNextRoot = document.getElementById('__next') !== null;
      const hasNextBuildId = document.querySelector('meta[name="next-head-count"]') !== null;
      
      return hasNextData || hasNextRouter || hasNextHydrated || hasNextRoot || hasNextBuildId;
    });
  }
  
  private async injectNextJSMonitoring(): Promise<void> {
    if (!this.page) return;
    
    await this.page.addInitScript(() => {
      (window as any).__NEXTJS_DEBUG__ = {
        pageTransitions: [],
        errors: [],
        buildInfo: null,
        
        captureTransition: function(from: string, to: string, type: string, startTime: number) {
          this.pageTransitions.push({
            from,
            to,
            type,
            duration: Date.now() - startTime,
            timestamp: new Date().toISOString()
          });
        },
        
        captureError: function(error: any, errorInfo: any) {
          this.errors.push({
            message: error.message || String(error),
            stack: error.stack,
            componentStack: errorInfo?.componentStack,
            timestamp: new Date().toISOString(),
            isChunkLoadError: error.message?.includes('ChunkLoadError'),
            is404: error.message?.includes('404')
          });
        }
      };
      
      // Capture build info
      if ((window as any).__NEXT_DATA__) {
        (window as any).__NEXTJS_DEBUG__.buildInfo = {
          buildId: (window as any).__NEXT_DATA__.buildId,
          page: (window as any).__NEXT_DATA__.page,
          query: (window as any).__NEXT_DATA__.query,
          assetPrefix: (window as any).__NEXT_DATA__.assetPrefix,
          runtimeConfig: (window as any).__NEXT_DATA__.runtimeConfig,
          dynamicIds: (window as any).__NEXT_DATA__.dynamicIds,
          gssp: (window as any).__NEXT_DATA__.gssp,
          gip: (window as any).__NEXT_DATA__.gip,
          gsp: (window as any).__NEXT_DATA__.gsp,
          isPreview: (window as any).__NEXT_DATA__.isPreview
        };
      }
      
      // Monitor Next.js router
      if ((window as any).next?.router) {
        const router = (window as any).next.router;
        const originalPush = router.push;
        const originalReplace = router.replace;
        
        router.push = async function(...args: any[]) {
          const startTime = Date.now();
          const from = window.location.pathname;
          const result = await originalPush.apply(this, args);
          const to = window.location.pathname;
          (window as any).__NEXTJS_DEBUG__.captureTransition(from, to, 'client', startTime);
          return result;
        };
        
        router.replace = async function(...args: any[]) {
          const startTime = Date.now();
          const from = window.location.pathname;
          const result = await originalReplace.apply(this, args);
          const to = window.location.pathname;
          (window as any).__NEXTJS_DEBUG__.captureTransition(from, to, 'client', startTime);
          return result;
        };
      }
      
      // Monitor for hydration errors
      const originalError = console.error;
      console.error = function(...args) {
        const errorStr = args.join(' ');
        if (errorStr.includes('Hydration') || 
            errorStr.includes('Text content does not match') ||
            errorStr.includes('did not match. Server:')) {
          (window as any).__NEXTJS_DEBUG__.captureError(
            new Error(errorStr),
            { source: 'hydration' }
          );
        }
        return originalError.apply(console, args);
      };
    });
  }
  
  private async setupRSCMonitoring(): Promise<void> {
    if (!this.page) return;
    
    // Monitor React Server Component payloads
    this.page.on('response', async (response) => {
      const url = response.url();
      const contentType = response.headers()['content-type'] || '';
      
      // Detect RSC payloads (they have a specific content type)
      if (contentType.includes('text/x-component') || 
          url.includes('_rsc') || 
          response.headers()['x-nextjs-data']) {
        
        try {
          const text = await response.text();
          const size = text.length;
          
          // Parse RSC payload format
          const componentMatch = text.match(/^0:"([^"]+)"/m);
          const componentName = componentMatch ? componentMatch[1] : 'Unknown';
          
          this.rscPayloads.push({
            componentName,
            props: {}, // Would need more parsing for actual props
            timestamp: new Date(),
            size
          });
        } catch (error) {
          // Ignore parsing errors
        }
      }
    });
  }
  
  private async setupImageOptimizationTracking(): Promise<void> {
    if (!this.page) return;
    
    await this.page.addInitScript(() => {
      (window as any).__NEXTJS_IMAGE_DEBUG__ = {
        unoptimizedImages: [],
        lazyLoadObserver: null,
        
        init: function() {
          // Track all images
          const checkImage = (img: HTMLImageElement) => {
            const src = img.src;
            const isNextImage = img.hasAttribute('data-nimg');
            
            // Check if using next/image
            if (!isNextImage && src && !src.includes('_next/image')) {
              const displayed = {
                width: img.clientWidth,
                height: img.clientHeight
              };
              
              // Get natural dimensions when loaded
              if (img.complete) {
                this.recordUnoptimized(img, displayed);
              } else {
                img.addEventListener('load', () => {
                  this.recordUnoptimized(img, displayed);
                });
              }
            }
          };
          
          // Check existing images
          document.querySelectorAll('img').forEach(checkImage);
          
          // Monitor new images
          const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
              mutation.addedNodes.forEach((node) => {
                if (node.nodeName === 'IMG') {
                  checkImage(node as HTMLImageElement);
                } else if (node instanceof Element) {
                  node.querySelectorAll('img').forEach(checkImage);
                }
              });
            });
          });
          
          observer.observe(document.body, {
            childList: true,
            subtree: true
          });
        },
        
        recordUnoptimized: function(img: HTMLImageElement, displayed: any) {
          const actual = {
            width: img.naturalWidth,
            height: img.naturalHeight
          };
          
          // Calculate potential improvement
          let improvement = 'None';
          if (actual.width > displayed.width * 2 || actual.height > displayed.height * 2) {
            improvement = 'High - Image is 2x+ larger than displayed size';
          } else if (actual.width > displayed.width * 1.5 || actual.height > displayed.height * 1.5) {
            improvement = 'Medium - Image is 1.5x larger than displayed size';
          }
          
          // Detect format
          const format = img.src.split('.').pop()?.split('?')[0] || 'unknown';
          
          this.unoptimizedImages.push({
            src: img.src,
            format,
            displayed,
            actual,
            improvement
          });
        }
      };
      
      // Initialize after DOM is ready
      if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
          (window as any).__NEXTJS_IMAGE_DEBUG__.init();
        });
      } else {
        (window as any).__NEXTJS_IMAGE_DEBUG__.init();
      }
    });
  }
  
  async getPageInfo(): Promise<NextJSPageInfo | null> {
    if (!this.page) return null;
    
    return await this.page.evaluate(() => {
      const nextData = (window as any).__NEXT_DATA__;
      if (!nextData) return null;
      
      const pageInfo: NextJSPageInfo = {
        path: nextData.page,
        type: 'static',
        dataFetching: [],
        hasLayout: false,
        isAppDir: nextData.page.includes('/app/') || !!(window as any).__next_app__
      };
      
      // Determine page type
      if (nextData.gssp) {
        pageInfo.type = 'ssr';
        pageInfo.dataFetching.push('getServerSideProps');
      } else if (nextData.gsp) {
        pageInfo.type = 'ssg';
        pageInfo.dataFetching.push('getStaticProps');
      } else if (nextData.isFallback) {
        pageInfo.type = 'isr';
      }
      
      if (nextData.gip) {
        pageInfo.dataFetching.push('getStaticPaths');
      }
      
      // Check for layout
      pageInfo.hasLayout = document.querySelector('[data-nextjs-layout]') !== null;
      
      return pageInfo;
    });
  }
  
  async getBuildInfo(): Promise<NextJSBuildInfo | null> {
    if (!this.page) return null;
    
    return await this.page.evaluate(() => {
      const debug = (window as any).__NEXTJS_DEBUG__;
      return debug?.buildInfo || null;
    });
  }
  
  async getHydrationErrors(): Promise<any[]> {
    if (!this.page) return [];
    
    const errors = await this.page.evaluate(() => {
      const debug = (window as any).__NEXTJS_DEBUG__;
      return debug?.errors.filter((e: any) => e.source === 'hydration') || [];
    });
    
    return errors;
  }
  
  async getPageTransitions(): Promise<any[]> {
    if (!this.page) return [];
    
    return await this.page.evaluate(() => {
      const debug = (window as any).__NEXTJS_DEBUG__;
      return debug?.pageTransitions || [];
    });
  }
  
  async getRSCPayloads(): Promise<RSCPayload[]> {
    return this.rscPayloads;
  }
  
  async getImageOptimizationIssues(): Promise<NextJSImageOptimization> {
    if (!this.page) return {
      unoptimizedImages: [],
      lazyLoadFailures: [],
      nextImageUsage: 0
    };
    
    const data = await this.page.evaluate(() => {
      const imageDebug = (window as any).__NEXTJS_IMAGE_DEBUG__;
      const allImages = document.querySelectorAll('img').length;
      const nextImages = document.querySelectorAll('img[data-nimg]').length;
      
      return {
        unoptimizedImages: imageDebug?.unoptimizedImages || [],
        lazyLoadFailures: [], // Would need IntersectionObserver tracking
        nextImageUsage: allImages > 0 ? (nextImages / allImages) * 100 : 0
      };
    });
    
    return data;
  }
  
  async detectNextJSProblems(): Promise<Array<{
    problem: string;
    severity: 'low' | 'medium' | 'high';
    description: string;
    solution: string;
  }>> {
    const problems = [];
    
    // Check for hydration errors
    const hydrationErrors = await this.getHydrationErrors();
    if (hydrationErrors.length > 0) {
      problems.push({
        problem: 'Next.js Hydration Errors',
        severity: 'high' as const,
        description: `Found ${hydrationErrors.length} hydration errors. This breaks interactivity.`,
        solution: 'Ensure consistent rendering between server and client. Check for browser-only APIs in SSR/SSG pages.'
      });
    }
    
    // Check RSC payload sizes
    const rscPayloads = await this.getRSCPayloads();
    const largePayloads = rscPayloads.filter(p => p.size > 50 * 1024); // 50KB
    if (largePayloads.length > 0) {
      problems.push({
        problem: 'Large Server Component Payloads',
        severity: 'medium' as const,
        description: `${largePayloads.length} RSC payloads exceed 50KB. Largest: ${(largePayloads[0]?.size / 1024).toFixed(2)}KB`,
        solution: 'Reduce data fetching in Server Components, use pagination, or move large data to client components.'
      });
    }
    
    // Check image optimization
    const imageIssues = await this.getImageOptimizationIssues();
    if (imageIssues.unoptimizedImages.length > 0) {
      const highImpact = imageIssues.unoptimizedImages.filter(img => 
        img.improvement.includes('High')
      ).length;
      
      problems.push({
        problem: 'Unoptimized Images',
        severity: highImpact > 0 ? 'high' as const : 'medium' as const,
        description: `${imageIssues.unoptimizedImages.length} images not using next/image. ${highImpact} have high optimization potential.`,
        solution: 'Use next/image component for automatic optimization, lazy loading, and responsive images.'
      });
    }
    
    // Check page type efficiency
    const pageInfo = await this.getPageInfo();
    if (pageInfo?.type === 'ssr' && !pageInfo.dataFetching.includes('getServerSideProps')) {
      problems.push({
        problem: 'Unnecessary SSR',
        severity: 'low' as const,
        description: 'Page uses SSR but has no data fetching. Could be static.',
        solution: 'Convert to static generation (SSG) for better performance and caching.'
      });
    }
    
    // Check for slow page transitions
    const transitions = await this.getPageTransitions();
    const slowTransitions = transitions.filter(t => t.duration > 2000);
    if (slowTransitions.length > 0) {
      problems.push({
        problem: 'Slow Page Transitions',
        severity: 'medium' as const,
        description: `${slowTransitions.length} page transitions took over 2 seconds.`,
        solution: 'Use next/link for prefetching, optimize getServerSideProps, or consider static generation.'
      });
    }
    
    // Check Next.js image usage
    if (imageIssues.nextImageUsage < 50 && imageIssues.unoptimizedImages.length > 5) {
      problems.push({
        problem: 'Low next/image Adoption',
        severity: 'medium' as const,
        description: `Only ${imageIssues.nextImageUsage.toFixed(1)}% of images use next/image optimization.`,
        solution: 'Migrate img tags to next/image for automatic optimization and better Core Web Vitals.'
      });
    }
    
    return problems;
  }
}