import { Component } from 'react';
import { View, Text } from '@tarojs/components';
import './index.scss';

interface Props {
  children: React.ReactNode;
  fallback?: React.ReactNode;
}

interface State {
  hasError: boolean;
  retryCount: number;
  errorCategory: ErrorCategory;
}

type ErrorCategory = 'network' | 'render' | 'unknown';

const MAX_RETRIES = 3;

function classifyError(error: Error): ErrorCategory {
  const msg = error.message?.toLowerCase() || '';
  if (
    msg.includes('network') ||
    msg.includes('fetch') ||
    msg.includes('timeout') ||
    msg.includes('request:fail')
  ) {
    return 'network';
  }
  if (
    msg.includes('cannot read properties') ||
    msg.includes('is not defined') ||
    msg.includes('is not a function') ||
    msg.includes('render')
  ) {
    return 'render';
  }
  return 'unknown';
}

function logError(error: Error, info: React.ErrorInfo, category: ErrorCategory): void {
  const isDev = process.env.NODE_ENV === 'development';
  const entry = {
    ts: new Date().toISOString(),
    category,
    message: error.message,
    stack: isDev ? error.stack : undefined,
    componentStack: isDev ? info.componentStack : undefined,
  };

  if (isDev) {
    console.error('[ErrorBoundary]', JSON.stringify(entry, null, 2));
  } else {
    console.error('[ErrorBoundary]', entry.ts, entry.category, entry.message);
  }
}

export default class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false, retryCount: 0, errorCategory: 'unknown' };
  }

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

  componentDidCatch(error: Error, info: React.ErrorInfo) {
    const category = classifyError(error);
    logError(error, info, category);
    this.setState((prev) => ({
      retryCount: prev.retryCount + 1,
      errorCategory: category,
    }));
  }

  handleRetry = () => {
    this.setState({ hasError: false });
  };

  render() {
    if (this.state.hasError) {
      if (this.props.fallback) {
        return this.props.fallback;
      }

      const exceeded = this.state.retryCount >= MAX_RETRIES;
      const isNetwork = this.state.errorCategory === 'network';
      const title = isNetwork ? '网络连接失败' : '页面出了点问题';
      const desc = exceeded
        ? '请重启小程序后重试'
        : isNetwork
          ? '请检查网络后重试'
          : '请返回重试';

      return (
        <View className='error-boundary'>
          <View className='error-icon-wrap'>
            <Text className='error-icon-text'>!</Text>
          </View>
          <Text className='error-title'>{title}</Text>
          <Text className='error-desc'>{desc}</Text>
          {!exceeded && (
            <View className='error-retry-btn' onClick={this.handleRetry}>
              <Text className='error-retry-text'>重新加载</Text>
            </View>
          )}
        </View>
      );
    }
    return this.props.children;
  }
}
