import React, { Component, ErrorInfo, ReactNode } from 'react';

interface Props {
  children: ReactNode;
  fallback?: ReactNode;
  onError?: (error: Error, errorInfo: ErrorInfo) => void;
}

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

/**
 * 全局错误边界组件
 * 
 * 功能：
 * 1. 捕获组件树中的 JavaScript 错误
 * 2. 记录错误信息
 * 3. 显示降级 UI
 * 4. 防止整个应用崩溃白屏
 * 
 * 使用方式：
 * <ErrorBoundary>
 *   <App />
 * </ErrorBoundary>
 */
class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      hasError: false,
      error: null,
      errorInfo: null,
    };
  }

  static getDerivedStateFromError(error: Error): State {
    // 更新 state 使下一次渲染能够显示降级后的 UI
    return {
      hasError: true,
      error,
      errorInfo: null,
    };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    // 记录错误信息
    console.error('ErrorBoundary 捕获到错误:', error);
    console.error('错误组件栈:', errorInfo.componentStack);

    // 更新状态
    this.setState({
      error,
      errorInfo,
    });

    // 调用自定义错误处理函数
    if (this.props.onError) {
      this.props.onError(error, errorInfo);
    }

    // 在生产环境中，可以将错误发送到错误监控服务
    if (import.meta.env.PROD) {
      // 示例：发送到错误监控服务
      // reportErrorToService(error, errorInfo);
    }
  }

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

  render() {
    if (this.state.hasError) {
      // 如果提供了自定义降级 UI，使用它
      if (this.props.fallback) {
        return this.props.fallback;
      }

      // 默认降级 UI
      return (
        <div className="flex h-screen w-screen flex-col items-center justify-center bg-slate-50 p-8">
          <div className="max-w-2xl rounded-lg bg-white p-8 shadow-lg">
            <div className="mb-6 flex items-center gap-3">
              <svg
                className="h-8 w-8 text-red-500"
                fill="none"
                stroke="currentColor"
                viewBox="0 0 24 24"
              >
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
                />
              </svg>
              <h1 className="text-2xl font-bold text-slate-800">应用出现错误</h1>
            </div>

            <div className="mb-6">
              <p className="mb-4 text-slate-600">
                很抱歉，应用遇到了一个意外错误。您可以尝试刷新页面或返回首页。
              </p>

              {/* 开发环境显示错误详情 */}
              {import.meta.env.DEV && this.state.error && (
                <div className="mt-4 rounded-md bg-red-50 p-4">
                  <p className="mb-2 font-semibold text-red-800">错误详情：</p>
                  <pre className="overflow-auto text-sm text-red-700">
                    {this.state.error.toString()}
                  </pre>
                  {this.state.errorInfo && (
                    <details className="mt-3">
                      <summary className="cursor-pointer font-semibold text-red-800">
                        组件堆栈
                      </summary>
                      <pre className="mt-2 overflow-auto text-xs text-red-600">
                        {this.state.errorInfo.componentStack}
                      </pre>
                    </details>
                  )}
                </div>
              )}
            </div>

            <div className="flex gap-3">
              <button
                onClick={() => window.location.href = '/'}
                className="rounded-md bg-blue-600 px-4 py-2 text-white transition-colors hover:bg-blue-700"
              >
                返回首页
              </button>
              <button
                onClick={() => window.location.reload()}
                className="rounded-md bg-slate-600 px-4 py-2 text-white transition-colors hover:bg-slate-700"
              >
                刷新页面
              </button>
              {import.meta.env.DEV && (
                <button
                  onClick={this.handleReset}
                  className="rounded-md border border-slate-300 px-4 py-2 text-slate-700 transition-colors hover:bg-slate-100"
                >
                  重置错误状态
                </button>
              )}
            </div>
          </div>
        </div>
      );
    }

    return this.props.children;
  }
}

export default ErrorBoundary;

