/**
 * ErrorBoundary - React Error Boundary for catching unhandled errors
 *
 * Catches JavaScript errors anywhere in child component tree,
 * logs them, and displays a fallback UI.
 */

import React, { Component, ErrorInfo, ReactNode } from 'react';
import {
  View,
  Text,
  TouchableOpacity,
  StyleSheet,
  ScrollView,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';

interface ErrorBoundaryProps {
  children: ReactNode;
  /** Custom fallback component */
  fallback?: ReactNode;
  /** Called when an error is caught */
  onError?: (error: Error, errorInfo: ErrorInfo) => void;
  /** Reset key - when this changes, the error state is reset */
  resetKey?: string | number;
  /** Show error details in development mode */
  showDetails?: boolean;
  /** Custom retry handler */
  onRetry?: () => void;
}

interface ErrorBoundaryState {
  hasError: boolean;
  error: Error | null;
  errorInfo: ErrorInfo | null;
}

/**
 * ErrorBoundary component - catches React errors and displays fallback UI
 *
 * @example
 * <ErrorBoundary
 *   fallback={<CustomErrorScreen />}
 *   onError={(error, info) => logToService(error, info)}
 * >
 *   <App />
 * </ErrorBoundary>
 */
export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
  constructor(props: ErrorBoundaryProps) {
    super(props);
    this.state = {
      hasError: false,
      error: null,
      errorInfo: null,
    };
  }

  static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
    // Log to console in development
    if (__DEV__) {
      console.error('ErrorBoundary caught an error:', error);
      console.error('Component stack:', errorInfo.componentStack);
    }

    // Update state with error info
    this.setState({ errorInfo });

    // Call onError callback if provided
    this.props.onError?.(error, errorInfo);

    // TODO: Log to error reporting service (Sentry, Bugsnag, etc.)
    // errorReportingService.captureException(error, { extra: errorInfo });
  }

  componentDidUpdate(prevProps: ErrorBoundaryProps): void {
    // Reset error state when resetKey changes
    if (
      this.state.hasError &&
      prevProps.resetKey !== this.props.resetKey
    ) {
      this.resetError();
    }
  }

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

  handleRetry = (): void => {
    this.resetError();
    this.props.onRetry?.();
  };

  render(): ReactNode {
    if (this.state.hasError) {
      // Render custom fallback if provided
      if (this.props.fallback) {
        return this.props.fallback;
      }

      // Render default error UI
      return (
        <DefaultErrorFallback
          error={this.state.error}
          errorInfo={this.state.errorInfo}
          onRetry={this.handleRetry}
          showDetails={this.props.showDetails ?? __DEV__}
        />
      );
    }

    return this.props.children;
  }
}

// ==================== Default Fallback Component ====================

interface DefaultErrorFallbackProps {
  error: Error | null;
  errorInfo: ErrorInfo | null;
  onRetry?: () => void;
  showDetails?: boolean;
}

function DefaultErrorFallback({
  error,
  errorInfo,
  onRetry,
  showDetails = false,
}: DefaultErrorFallbackProps) {
  const [detailsExpanded, setDetailsExpanded] = React.useState(false);

  return (
    <View style={styles.container}>
      <View style={styles.content}>
        {/* Icon */}
        <View style={styles.iconContainer}>
          <Ionicons name="bug" size={64} color="#EF4444" />
        </View>

        {/* Title */}
        <Text style={styles.title}>Oops! Something went wrong</Text>

        {/* Message */}
        <Text style={styles.message}>
          The app encountered an unexpected error. We apologize for the inconvenience.
        </Text>

        {/* Retry Button */}
        {onRetry && (
          <TouchableOpacity
            style={styles.retryButton}
            onPress={onRetry}
            activeOpacity={0.8}
          >
            <Ionicons name="refresh" size={20} color="#fff" style={styles.buttonIcon} />
            <Text style={styles.retryButtonText}>Try Again</Text>
          </TouchableOpacity>
        )}

        {/* Error Details (Development Only) */}
        {showDetails && error && (
          <View style={styles.detailsContainer}>
            <TouchableOpacity
              style={styles.detailsHeader}
              onPress={() => setDetailsExpanded(!detailsExpanded)}
              activeOpacity={0.7}
            >
              <Text style={styles.detailsHeaderText}>
                {detailsExpanded ? 'Hide' : 'Show'} Error Details
              </Text>
              <Ionicons
                name={detailsExpanded ? 'chevron-up' : 'chevron-down'}
                size={16}
                color="#6B7280"
              />
            </TouchableOpacity>

            {detailsExpanded && (
              <ScrollView style={styles.detailsContent}>
                <Text style={styles.errorName}>{error.name}</Text>
                <Text style={styles.errorMessage}>{error.message}</Text>
                {errorInfo?.componentStack && (
                  <>
                    <Text style={styles.stackTitle}>Component Stack:</Text>
                    <Text style={styles.stackTrace}>
                      {errorInfo.componentStack}
                    </Text>
                  </>
                )}
                {error.stack && (
                  <>
                    <Text style={styles.stackTitle}>Error Stack:</Text>
                    <Text style={styles.stackTrace}>{error.stack}</Text>
                  </>
                )}
              </ScrollView>
            )}
          </View>
        )}
      </View>
    </View>
  );
}

// ==================== Wrapper HOC ====================

/**
 * Higher-order component to wrap any component with ErrorBoundary
 *
 * @example
 * const SafeScreen = withErrorBoundary(MyScreen, {
 *   fallback: <CustomError />,
 *   onError: logError,
 * });
 */
export function withErrorBoundary<P extends object>(
  WrappedComponent: React.ComponentType<P>,
  errorBoundaryProps?: Omit<ErrorBoundaryProps, 'children'>
) {
  const displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component';

  const ComponentWithErrorBoundary = (props: P) => (
    <ErrorBoundary {...errorBoundaryProps}>
      <WrappedComponent {...props} />
    </ErrorBoundary>
  );

  ComponentWithErrorBoundary.displayName = `withErrorBoundary(${displayName})`;

  return ComponentWithErrorBoundary;
}

// ==================== Styles ====================

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#fff',
    padding: 24,
  },
  content: {
    alignItems: 'center',
    maxWidth: 320,
    width: '100%',
  },
  iconContainer: {
    width: 120,
    height: 120,
    borderRadius: 60,
    backgroundColor: '#FEE2E2',
    justifyContent: 'center',
    alignItems: 'center',
    marginBottom: 24,
  },
  title: {
    fontSize: 20,
    fontWeight: '700',
    color: '#1F2937',
    textAlign: 'center',
    marginBottom: 8,
  },
  message: {
    fontSize: 15,
    color: '#6B7280',
    textAlign: 'center',
    lineHeight: 22,
    marginBottom: 24,
  },
  retryButton: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#3B82F6',
    paddingVertical: 14,
    paddingHorizontal: 24,
    borderRadius: 12,
    width: '100%',
    marginBottom: 24,
  },
  buttonIcon: {
    marginRight: 8,
  },
  retryButtonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '600',
  },
  detailsContainer: {
    width: '100%',
    borderTopWidth: 1,
    borderTopColor: '#E5E7EB',
    paddingTop: 16,
  },
  detailsHeader: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
    paddingVertical: 8,
  },
  detailsHeaderText: {
    fontSize: 13,
    color: '#6B7280',
    marginRight: 4,
  },
  detailsContent: {
    maxHeight: 300,
    backgroundColor: '#F9FAFB',
    borderRadius: 8,
    padding: 12,
    marginTop: 12,
  },
  errorName: {
    fontSize: 14,
    fontWeight: '600',
    color: '#EF4444',
    marginBottom: 4,
  },
  errorMessage: {
    fontSize: 13,
    color: '#374151',
    marginBottom: 12,
  },
  stackTitle: {
    fontSize: 12,
    fontWeight: '600',
    color: '#6B7280',
    marginTop: 8,
    marginBottom: 4,
  },
  stackTrace: {
    fontSize: 10,
    color: '#9CA3AF',
    fontFamily: 'monospace',
    lineHeight: 14,
  },
});

export default ErrorBoundary;
