import React from 'react';
import { NativeModules, ViewStyle, StyleProp } from 'react-native';
import { MiniAppContext } from '../Context';

export interface ViewBoundaryProps {
  children: React.ReactNode;
  fallback?: React.ReactNode;
  fallbackStyle?: StyleProp<ViewStyle>;
  onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
}

interface ViewBoundaryState {
  hasError: boolean;
  error: Error | null;
}

class ViewBoundary extends React.Component<
  ViewBoundaryProps,
  ViewBoundaryState
> {
  static contextType = MiniAppContext;

  constructor(props: ViewBoundaryProps) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error: Error): ViewBoundaryState {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    this.props.onError?.(error, errorInfo);

    const context = (this as any).context ?? {};
    // check exist native module method onJSBoundaryCrash
    const nativeMethod = NativeModules?.MiniAppModule?.onJSBoundaryCrash;
    if (typeof nativeMethod === 'function') {
      try {
        nativeMethod({
          hostId: context.hostId ?? '',
          appId: context.appId ?? '',
          code: context.code ?? '',
          buildNumber: context.buildNumber ?? '',
          tag: 'ViewBoundary',
          error: {
            errorName: error.name,
            errorMessage: error.message,
            componentStack: errorInfo?.componentStack?.substring(0, 500) ?? '',
          },
        });
        console.info('[ViewBoundary] onJSBoundaryCrash success!');
      } catch (_e) {
        console.warn('[ViewBoundary] onJSBoundaryCrash failed:', _e);
      }
    } else {
      console.warn(
        '[ViewBoundary] onJSBoundaryCrash not available on this platform build',
      );
    }
  }

  render() {
    if (this.state.hasError) {
      if (this.props.fallback !== undefined) {
        return this.props.fallback;
      }
      return null;
    }
    return this.props.children;
  }
}

export { ViewBoundary };
