import { Page } from 'playwright';

export interface MetaFrameworkInfo {
  framework: 'remix' | 'astro' | 'nuxt' | 'qwik' | 'solidjs' | 'sveltekit' | null;
  version?: string;
  buildTool?: 'vite' | 'webpack' | 'esbuild' | 'rollup';
  features: string[];
}

export interface ViteHMREvent {
  type: 'update' | 'full-reload' | 'error' | 'custom';
  timestamp: Date;
  files?: string[];
  error?: string;
}

export interface RemixLoaderData {
  route: string;
  loader: string;
  duration: number;
  size: number;
  cached: boolean;
}

export interface AstroIsland {
  component: string;
  props: any;
  hydrationDirective?: 'load' | 'idle' | 'visible' | 'media' | 'only';
  loading: 'eager' | 'lazy';
}

export interface QwikResumabilityInfo {
  serializedState: number;
  qrlCount: number;
  symbolsLoaded: number;
  resumeTime: number;
}

export class MetaFrameworkDebugEngine {
  private page?: Page;
  private viteHMREvents: ViteHMREvent[] = [];
  private remixLoaderData: RemixLoaderData[] = [];
  private astroIslands: AstroIsland[] = [];
  private frameworkInfo?: MetaFrameworkInfo;
  
  async attachToPage(page: Page): Promise<void> {
    this.page = page;
    
    // Detect framework first
    this.frameworkInfo = await this.detectMetaFramework(page);
    
    // Inject framework-specific monitoring
    if (this.frameworkInfo) {
      await this.injectFrameworkMonitoring(this.frameworkInfo.framework!);
    }
    
    // Setup Vite HMR monitoring if using Vite
    if (this.frameworkInfo?.buildTool === 'vite') {
      await this.setupViteHMRMonitoring();
    }
  }
  
  async detectMetaFramework(page: Page): Promise<MetaFrameworkInfo> {
    return await page.evaluate(() => {
      const info: MetaFrameworkInfo = {
        framework: null,
        features: []
      };
      
      // Remix detection
      if ((window as any).__remixContext || (window as any).__remixManifest) {
        info.framework = 'remix';
        info.features.push('loader', 'action', 'error-boundaries');
        if ((window as any).__remixContext?.future) {
          info.features.push(...Object.keys((window as any).__remixContext.future));
        }
      }
      
      // Astro detection
      else if (document.querySelector('astro-island') || (window as any).__ASTRO__) {
        info.framework = 'astro';
        info.features.push('islands', 'partial-hydration');
        if ((window as any).__ASTRO_DEV_TOOLBAR__) {
          info.features.push('dev-toolbar');
        }
      }
      
      // Nuxt 3 detection
      else if ((window as any).__NUXT__ || (window as any).$nuxt) {
        info.framework = 'nuxt';
        info.version = (window as any).__NUXT__?.version || '3.x';
        info.features.push('auto-imports', 'file-routing', 'nitro-server');
        if ((window as any).__NUXT__?.isHydrating === false) {
          info.features.push('hydrated');
        }
      }
      
      // Qwik detection
      else if ((window as any).qwikloader || document.querySelector('[q\\:base]')) {
        info.framework = 'qwik';
        info.features.push('resumability', 'lazy-loading', 'fine-grained-reactivity');
      }
      
      // SolidJS detection
      else if ((window as any)._$HY || document.querySelector('[data-hk]')) {
        info.framework = 'solidjs';
        info.features.push('fine-grained-reactivity', 'no-virtual-dom');
      }
      
      // SvelteKit detection
      else if ((window as any).__sveltekit__ || document.querySelector('[data-sveltekit]')) {
        info.framework = 'sveltekit';
        info.features.push('file-routing', 'load-functions', 'form-actions');
      }
      
      // Detect build tool
      if ((window as any).__vite_plugin_react_preamble_installed__ || 
          document.querySelector('script[type="module"][src*="/@vite"]')) {
        info.buildTool = 'vite';
      } else if ((window as any).webpackChunkName) {
        info.buildTool = 'webpack';
      }
      
      return info;
    });
  }
  
  private async injectFrameworkMonitoring(framework: string): Promise<void> {
    if (!this.page) return;
    
    switch (framework) {
      case 'remix':
        await this.injectRemixMonitoring();
        break;
      case 'astro':
        await this.injectAstroMonitoring();
        break;
      case 'nuxt':
        await this.injectNuxtMonitoring();
        break;
      case 'qwik':
        await this.injectQwikMonitoring();
        break;
      case 'solidjs':
        await this.injectSolidJSMonitoring();
        break;
      case 'sveltekit':
        await this.injectSvelteKitMonitoring();
        break;
    }
  }
  
  private async injectRemixMonitoring(): Promise<void> {
    if (!this.page) return;
    
    await this.page.addInitScript(() => {
      (window as any).__REMIX_DEBUG__ = {
        loaderData: [],
        actionData: [],
        transitions: [],
        errors: [],
        
        init: function() {
          // Monitor loader execution
          if ((window as any).__remixContext) {
            const originalFetch = window.fetch;
            window.fetch = async function(...args) {
              const startTime = Date.now();
              const url = typeof args[0] === 'string' ? args[0] : (args[0] as Request).url;
              
              // Track data requests
              if (url.includes('_data=')) {
                const response = await originalFetch.apply(this, args);
                const duration = Date.now() - startTime;
                
                (window as any).__REMIX_DEBUG__.loaderData.push({
                  url,
                  duration,
                  status: response.status,
                  timestamp: new Date().toISOString()
                });
                
                return response;
              }
              
              return originalFetch.apply(this, args);
            };
          }
          
          // Monitor navigation
          if ((window as any).RemixBrowser) {
            console.log('Remix navigation monitoring enabled');
          }
        }
      };
      
      (window as any).__REMIX_DEBUG__.init();
    });
  }
  
  private async injectAstroMonitoring(): Promise<void> {
    if (!this.page) return;
    
    await this.page.addInitScript(() => {
      (window as any).__ASTRO_DEBUG__ = {
        islands: [],
        hydrationEvents: [],
        
        init: function() {
          // Monitor Astro islands
          const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
              mutation.addedNodes.forEach((node) => {
                if (node instanceof Element && node.tagName === 'ASTRO-ISLAND') {
                  const island = {
                    uid: node.getAttribute('uid'),
                    component: node.getAttribute('component-url'),
                    props: node.getAttribute('props'),
                    hydrate: node.getAttribute('client:load') !== null ? 'load' :
                            node.getAttribute('client:idle') !== null ? 'idle' :
                            node.getAttribute('client:visible') !== null ? 'visible' :
                            node.getAttribute('client:media') !== null ? 'media' : 'none',
                    timestamp: new Date().toISOString()
                  };
                  
                  this.islands.push(island);
                }
              });
            });
          });
          
          observer.observe(document.body, {
            childList: true,
            subtree: true
          });
          
          // Monitor hydration
          const originalLog = console.log;
          console.log = function(...args) {
            const message = args.join(' ');
            if (message.includes('[astro]') || message.includes('hydrat')) {
              (window as any).__ASTRO_DEBUG__.hydrationEvents.push({
                message,
                timestamp: new Date().toISOString()
              });
            }
            return originalLog.apply(console, args);
          };
        }
      };
      
      (window as any).__ASTRO_DEBUG__.init();
    });
  }
  
  private async injectNuxtMonitoring(): Promise<void> {
    if (!this.page) return;
    
    await this.page.addInitScript(() => {
      (window as any).__NUXT_DEBUG__ = {
        payloads: [],
        errors: [],
        composables: new Set(),
        
        init: function() {
          const nuxt = (window as any).__NUXT__ || (window as any).$nuxt;
          if (!nuxt) return;
          
          // Monitor payloads
          if (nuxt.payload) {
            this.payloads.push({
              data: Object.keys(nuxt.payload.data || {}),
              state: Object.keys(nuxt.payload.state || {}),
              timestamp: new Date().toISOString()
            });
          }
          
          // Monitor composable usage
          const composableNames = ['useState', 'useFetch', 'useAsyncData', 'useLazyFetch'];
          composableNames.forEach(name => {
            if ((window as any)[name]) {
              this.composables.add(name);
            }
          });
          
          // Monitor errors
          if (nuxt.error) {
            this.errors.push({
              error: nuxt.error,
              timestamp: new Date().toISOString()
            });
          }
        }
      };
      
      // Initialize after Nuxt is ready
      if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
          setTimeout(() => (window as any).__NUXT_DEBUG__.init(), 100);
        });
      } else {
        setTimeout(() => (window as any).__NUXT_DEBUG__.init(), 100);
      }
    });
  }
  
  private async injectQwikMonitoring(): Promise<void> {
    if (!this.page) return;
    
    await this.page.addInitScript(() => {
      (window as any).__QWIK_DEBUG__ = {
        resumabilityInfo: null,
        qrlLoads: [],
        
        init: function() {
          // Monitor QRL loading
          const observer = new PerformanceObserver((list) => {
            for (const entry of list.getEntries()) {
              if (entry.name.includes('.js') && entry.name.includes('q-')) {
                this.qrlLoads.push({
                  url: entry.name,
                  duration: entry.duration,
                  size: (entry as any).transferSize || 0,
                  timestamp: new Date().toISOString()
                });
              }
            }
          });
          
          observer.observe({ entryTypes: ['resource'] });
          
          // Check resumability state
          const checkResumability = () => {
            const container = document.querySelector('[q\\:base]');
            if (container) {
              const state = container.getAttribute('q:state');
              this.resumabilityInfo = {
                hasState: !!state,
                stateSize: state ? state.length : 0,
                qrlCount: document.querySelectorAll('[on\\:click]').length,
                timestamp: new Date().toISOString()
              };
            }
          };
          
          setTimeout(checkResumability, 1000);
        }
      };
      
      (window as any).__QWIK_DEBUG__.init();
    });
  }
  
  private async injectSolidJSMonitoring(): Promise<void> {
    if (!this.page) return;
    
    await this.page.addInitScript(() => {
      (window as any).__SOLID_DEBUG__ = {
        reactivityUpdates: [],
        componentCount: 0,
        
        init: function() {
          // Count hydrated components
          this.componentCount = document.querySelectorAll('[data-hk]').length;
          
          // Monitor reactivity if dev mode
          if ((window as any)._$HY) {
            console.log('SolidJS reactivity monitoring enabled');
          }
        }
      };
      
      (window as any).__SOLID_DEBUG__.init();
    });
  }
  
  private async injectSvelteKitMonitoring(): Promise<void> {
    if (!this.page) return;
    
    await this.page.addInitScript(() => {
      (window as any).__SVELTEKIT_DEBUG__ = {
        navigations: [],
        forms: [],
        
        init: function() {
          // Monitor client-side navigation
          if ((window as any).__sveltekit__) {
            console.log('SvelteKit navigation monitoring enabled');
          }
        }
      };
      
      (window as any).__SVELTEKIT_DEBUG__.init();
    });
  }
  
  private async setupViteHMRMonitoring(): Promise<void> {
    if (!this.page) return;
    
    await this.page.addInitScript(() => {
      (window as any).__VITE_HMR_DEBUG__ = {
        events: [],
        
        init: function() {
          // Listen for Vite HMR events
          if ((window as any).__vite_plugin_react_preamble_installed__) {
            console.log('Vite HMR monitoring enabled');
            
            // Monitor import.meta.hot
            if ((import.meta as any).hot) {
              (import.meta as any).hot.on('vite:beforeUpdate', (payload: any) => {
                this.events.push({
                  type: 'update',
                  payload,
                  timestamp: new Date().toISOString()
                });
              });
              
              (import.meta as any).hot.on('vite:error', (payload: any) => {
                this.events.push({
                  type: 'error',
                  payload,
                  timestamp: new Date().toISOString()
                });
              });
            }
          }
        }
      };
      
      // Vite may not be ready immediately
      setTimeout(() => (window as any).__VITE_HMR_DEBUG__.init(), 100);
    });
  }
  
  // Remix specific methods
  async getRemixLoaderData(): Promise<RemixLoaderData[]> {
    if (!this.page || this.frameworkInfo?.framework !== 'remix') return [];
    
    const data = await this.page.evaluate(() => {
      return (window as any).__REMIX_DEBUG__?.loaderData || [];
    });
    
    return data.map((d: any) => ({
      ...d,
      timestamp: new Date(d.timestamp)
    }));
  }
  
  async getRemixRouteModules(): Promise<any> {
    if (!this.page || this.frameworkInfo?.framework !== 'remix') return null;
    
    return await this.page.evaluate(() => {
      const context = (window as any).__remixContext;
      if (!context) return null;
      
      return {
        routes: Object.keys(context.routeModules || {}),
        currentRoute: context.matches?.[context.matches.length - 1]?.route.id,
        manifest: context.manifest ? {
          version: context.manifest.version,
          routes: Object.keys(context.manifest.routes || {})
        } : null
      };
    });
  }
  
  // Astro specific methods
  async getAstroIslands(): Promise<AstroIsland[]> {
    if (!this.page || this.frameworkInfo?.framework !== 'astro') return [];
    
    const islands = await this.page.evaluate(() => {
      const islandElements = document.querySelectorAll('astro-island');
      return Array.from(islandElements).map(island => ({
        uid: island.getAttribute('uid'),
        component: island.getAttribute('component-url') || '',
        props: JSON.parse(island.getAttribute('props') || '{}'),
        hydrationDirective: (
          island.hasAttribute('client:load') ? 'load' :
          island.hasAttribute('client:idle') ? 'idle' :
          island.hasAttribute('client:visible') ? 'visible' :
          island.hasAttribute('client:media') ? 'media' :
          island.hasAttribute('client:only') ? 'only' : undefined
        ) as 'load' | 'idle' | 'visible' | 'media' | 'only' | undefined,
        loading: island.hasAttribute('client:load') ? 'eager' : 'lazy' as 'eager' | 'lazy'
      }));
    });
    
    return islands;
  }
  
  // Nuxt specific methods
  async getNuxtPayload(): Promise<any> {
    if (!this.page || this.frameworkInfo?.framework !== 'nuxt') return null;
    
    return await this.page.evaluate(() => {
      const nuxt = (window as any).__NUXT__ || (window as any).$nuxt;
      if (!nuxt) return null;
      
      return {
        isHydrating: nuxt.isHydrating,
        data: nuxt.payload?.data ? Object.keys(nuxt.payload.data) : [],
        state: nuxt.payload?.state ? Object.keys(nuxt.payload.state) : [],
        error: nuxt.error,
        serverRendered: nuxt.serverRendered !== false
      };
    });
  }
  
  // Qwik specific methods
  async getQwikResumability(): Promise<QwikResumabilityInfo | null> {
    if (!this.page || this.frameworkInfo?.framework !== 'qwik') return null;
    
    return await this.page.evaluate(() => {
      const container = document.querySelector('[q\\:base]');
      if (!container) return null;
      
      const state = container.getAttribute('q:state');
      const info = (window as any).__QWIK_DEBUG__?.resumabilityInfo;
      
      return {
        serializedState: state ? state.length : 0,
        qrlCount: document.querySelectorAll('[on\\:click]').length,
        symbolsLoaded: (window as any).__QWIK_DEBUG__?.qrlLoads?.length || 0,
        resumeTime: 0 // Would need performance timing
      };
    });
  }
  
  // Vite HMR methods
  async getViteHMREvents(): Promise<ViteHMREvent[]> {
    if (!this.page || this.frameworkInfo?.buildTool !== 'vite') return [];
    
    const events = await this.page.evaluate(() => {
      return (window as any).__VITE_HMR_DEBUG__?.events || [];
    });
    
    return events.map((e: any) => ({
      ...e,
      timestamp: new Date(e.timestamp)
    }));
  }
  
  // Generic problem detection
  async detectMetaFrameworkProblems(): Promise<Array<{
    problem: string;
    severity: 'low' | 'medium' | 'high';
    description: string;
    solution: string;
  }>> {
    const problems: Array<{
      problem: string;
      severity: 'low' | 'medium' | 'high';
      description: string;
      solution: string;
    }> = [];
    
    if (!this.frameworkInfo?.framework) {
      return problems;
    }
    
    switch (this.frameworkInfo.framework) {
      case 'remix':
        const loaderData = await this.getRemixLoaderData();
        const slowLoaders = loaderData.filter(l => l.duration > 1000);
        if (slowLoaders.length > 0) {
          problems.push({
            problem: 'Slow Remix Loaders',
            severity: 'medium' as const,
            description: `${slowLoaders.length} loader functions take over 1 second`,
            solution: 'Optimize database queries, add caching, or use defer() for non-critical data'
          });
        }
        break;
        
      case 'astro':
        const islands = await this.getAstroIslands();
        const eagerIslands = islands.filter(i => i.hydrationDirective === 'load');
        if (eagerIslands.length > 5) {
          problems.push({
            problem: 'Too Many Eager Islands',
            severity: 'medium' as const,
            description: `${eagerIslands.length} components hydrate immediately on page load`,
            solution: 'Use client:idle or client:visible for non-critical components'
          });
        }
        break;
        
      case 'nuxt':
        const payload = await this.getNuxtPayload();
        if (payload?.data?.length > 20) {
          problems.push({
            problem: 'Large Nuxt Payload',
            severity: 'medium' as const,
            description: `Payload contains ${payload.data.length} data keys`,
            solution: 'Consider lazy-loading data or paginating large datasets'
          });
        }
        break;
        
      case 'qwik':
        const resumability = await this.getQwikResumability();
        if (resumability && resumability.serializedState > 100000) {
          problems.push({
            problem: 'Large Qwik Serialized State',
            severity: 'high' as const,
            description: `Serialized state is ${(resumability.serializedState / 1024).toFixed(2)}KB`,
            solution: 'Reduce component state or move data to server-side stores'
          });
        }
        break;
    }
    
    // Vite HMR issues
    if (this.frameworkInfo.buildTool === 'vite') {
      const hmrEvents = await this.getViteHMREvents();
      const errors = hmrEvents.filter(e => e.type === 'error');
      if (errors.length > 0) {
        problems.push({
          problem: 'Vite HMR Errors',
          severity: 'high' as const,
          description: `${errors.length} HMR errors detected during development`,
          solution: 'Check console for specific errors and ensure proper HMR boundaries'
        });
      }
    }
    
    return problems;
  }
  
  getFrameworkInfo(): MetaFrameworkInfo | undefined {
    return this.frameworkInfo;
  }
}