'use client';

import React, { Component, ReactNode, ErrorInfo, useState, version } from 'react';
import { ApiError } from '@/lib/api/base/ApiError';
import { getGlobalErrorHandler } from '@/lib/infrastructure/GlobalErrorHandler';
import { DevErrorPanel } from '@/components/shared/DevErrorPanel';
import { ErrorDisplay } from '@/components/shared/ErrorDisplay';

interface Props {
  children: ReactNode;
  fallback?: ReactNode;
  onError?: (error: Error, errorInfo: ErrorInfo) => void;
  onReset?: () => void;
  /**
   * Whether to show the enhanced dev overlay
   */
  enableDevOverlay?: boolean;
  /**
   * Additional context to include with errors
   */
  context?: Record<string, unknown>;
}

interface State {
  hasError: boolean;
  error: Error | ApiError | null;
  errorInfo: ErrorInfo | null;
  isDev: boolean;
}

interface GridPilotWindow extends Window {
  __GRIDPILOT_REACT_ERRORS__?: Array<{
    error: Error;
    errorInfo: ErrorInfo;
    timestamp: string;
    componentStack?: string;
  }>;
}

/**
 * Enhanced React Error Boundary with maximum developer transparency
 * Integrates with GlobalErrorHandler and provides detailed debugging info
 */
export class EnhancedErrorBoundary extends Component<Props, State> {
  private globalErrorHandler: ReturnType<typeof getGlobalErrorHandler>;

  constructor(props: Props) {
    super(props);
    this.state = {
      hasError: false,
      error: null,
      errorInfo: null,
      isDev: process.env.NODE_ENV === 'development',
    };
    this.globalErrorHandler = getGlobalErrorHandler();
  }

  static getDerivedStateFromError(error: Error): State {
    // Don't catch Next.js navigation errors (redirect, notFound, etc.)
    if (error && typeof error === 'object' && 'digest' in error) {
      const digest = (error as Record<string, unknown>).digest;
      if (typeof digest === 'string' && (
        digest.startsWith('NEXT_REDIRECT') ||
        digest.startsWith('NEXT_NOT_FOUND')
      )) {
        // Re-throw Next.js navigation errors so they can be handled properly
        throw error;
      }
    }
    
    return {
      hasError: true,
      error,
      errorInfo: null,
      isDev: process.env.NODE_ENV === 'development',
    };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
    // Don't catch Next.js navigation errors (redirect, notFound, etc.)
    if (error && typeof error === 'object' && 'digest' in error) {
      const digest = (error as Record<string, unknown>).digest;
      if (typeof digest === 'string' && (
        digest.startsWith('NEXT_REDIRECT') ||
        digest.startsWith('NEXT_NOT_FOUND')
      )) {
        // Re-throw Next.js navigation errors so they can be handled properly
        throw error;
      }
    }
    
    // Add to React error history
    if (typeof window !== 'undefined') {
      const gpWindow = window as unknown as GridPilotWindow;
      const reactErrors = gpWindow.__GRIDPILOT_REACT_ERRORS__ || [];
      gpWindow.__GRIDPILOT_REACT_ERRORS__ = [
        ...reactErrors,
        {
          error,
          errorInfo,
          timestamp: new Date().toISOString(),
          componentStack: errorInfo.componentStack || undefined,
        }
      ];
    }

    // Report to global error handler with enhanced context
    const enhancedContext = {
      ...this.props.context,
      source: 'react_error_boundary',
      componentStack: errorInfo.componentStack,
      reactVersion: version,
      componentName: this.getComponentName(errorInfo),
    };

    // Use global error handler for maximum transparency
    this.globalErrorHandler.report(error, enhancedContext);

    // Call custom error handler if provided
    if (this.props.onError) {
      this.props.onError(error, errorInfo);
    }

    // Log to console with maximum detail
    if (this.state.isDev) {
      this.logReactErrorWithMaximumDetail(error, errorInfo);
    }
  }

  componentDidMount(): void {
    // Initialize global error handler if not already done
    if (this.props.enableDevOverlay && this.state.isDev) {
      this.globalErrorHandler.initialize();
    }
  }

  /**
   * Extract component name from error info
   */
  private getComponentName(errorInfo: ErrorInfo): string | undefined {
    try {
      const stack = errorInfo.componentStack;
      if (stack) {
        const match = stack.match(/at (\w+)/);
        return match ? match[1] : undefined;
      }
    } catch {
      // Ignore
    }
    return undefined;
  }

  /**
   * Log React error with maximum detail
   */
  private logReactErrorWithMaximumDetail(error: Error, errorInfo: ErrorInfo): void {
    console.groupCollapsed('%c[REACT ERROR BOUNDARY] Component Rendering Failed', 
      'color: #ff6600; font-weight: bold; font-size: 14px;'
    );

    console.log('Error Details:', {
      message: error.message,
      name: error.name,
      stack: error.stack,
    });

    console.log('Component Stack:', errorInfo.componentStack);

    console.log('React Context:', {
      reactVersion: version,
      component: this.getComponentName(errorInfo),
      timestamp: new Date().toISOString(),
    });

    console.log('Props:', this.props);
    console.log('State:', this.state);

    console.groupEnd();
  }

  resetError = (): void => {
    this.setState({ hasError: false, error: null, errorInfo: null });
    if (this.props.onReset) {
      this.props.onReset();
    }
  };

  render(): ReactNode {
    if (this.state.hasError && this.state.error) {
      if (this.props.fallback) {
        return this.props.fallback;
      }

      // Show different UI based on environment
      if (this.state.isDev) {
        return (
          <DevErrorPanel 
            error={this.state.error instanceof ApiError ? this.state.error : new ApiError(
              this.state.error.message,
              'UNKNOWN_ERROR',
              {
                timestamp: new Date().toISOString(),
                source: 'react_error_boundary',
              },
              this.state.error
            )}
            onReset={this.resetError}
          />
        );
      }

      return (
        <ErrorDisplay 
          error={this.state.error instanceof ApiError ? this.state.error : new ApiError(
            this.state.error.message,
            'UNKNOWN_ERROR',
            {
              timestamp: new Date().toISOString(),
              source: 'react_error_boundary',
            },
            this.state.error
          )}
          onRetry={this.resetError}
        />
      );
    }

    return this.props.children;
  }
}

/**
 * Hook-based alternative for functional components
 */
export function useEnhancedErrorBoundary() {
  const [error, setError] = useState<Error | ApiError | null>(null);
  const [errorInfo, setErrorInfo] = useState<ErrorInfo | null>(null);
  const [isDev] = useState(process.env.NODE_ENV === 'development');

  const handleError = (err: Error, info: ErrorInfo) => {
    setError(err);
    setErrorInfo(info);
    
    // Report to global handler
    const globalHandler = getGlobalErrorHandler();
    globalHandler.report(err, {
      source: 'react_hook_boundary',
      componentStack: info.componentStack,
    });
  };

  const reset = () => {
    setError(null);
    setErrorInfo(null);
  };

  return {
    error,
    errorInfo,
    isDev,
    handleError,
    reset,
    ErrorBoundary: ({ children, enableDevOverlay }: { children: ReactNode; enableDevOverlay?: boolean }) => (
      <EnhancedErrorBoundary 
        onError={handleError} 
        enableDevOverlay={enableDevOverlay}
      >
        {children}
      </EnhancedErrorBoundary>
    ),
  };
}

/**
 * Higher-order component wrapper for easy usage
 */
export function withEnhancedErrorBoundary<P extends object>(
  Component: React.ComponentType<P>,
  options: Omit<Props, 'children'> = {}
): React.FC<P> {
  const WrappedComponent = (props: P) => (
    <EnhancedErrorBoundary {...options}>
      <Component {...props} />
    </EnhancedErrorBoundary>
  );
  WrappedComponent.displayName = `withEnhancedErrorBoundary(${Component.displayName || Component.name || 'Component'})`;
  return WrappedComponent;
}
