"use client";

/**
 * 全局错误边界组件
 *
 * - 捕获子树中的 React 渲染错误，防止整个页面白屏
 * - 在开发环境输出详细错误栈到 console
 * - 预留 Sentry 集成点（搜索 TODO:SENTRY）
 * - 提供可重置的友好错误 UI
 */

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

interface Props {
  children: ReactNode;
  /** 自定义 fallback UI；不传则使用内置样式 */
  fallback?: ReactNode;
}

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

export class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false, error: null, errorInfo: null };
  }

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

  componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
    this.setState({ errorInfo });

    // 开发环境：完整错误栈
    if (process.env.NODE_ENV !== "production") {
      console.group("[ErrorBoundary] 捕获到未处理的渲染错误");
      console.error("Error:", error);
      console.error("Component Stack:", errorInfo.componentStack);
      console.groupEnd();
    } else {
      console.error("[ErrorBoundary]", error.message);
    }

    // TODO:SENTRY — 生产环境错误上报
    // import * as Sentry from "@sentry/nextjs";
    // Sentry.captureException(error, { extra: { componentStack: errorInfo.componentStack } });
  }

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

  render(): ReactNode {
    if (!this.state.hasError) {
      return this.props.children;
    }

    // 使用自定义 fallback
    if (this.props.fallback) {
      return this.props.fallback;
    }

    // 内置友好错误页面
    return (
      <div className="min-h-screen flex items-center justify-center bg-gray-50 px-4">
        <div className="max-w-md w-full bg-white rounded-2xl shadow-lg p-8 text-center">
          {/* 图标 */}
          <div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-red-50">
            <svg
              className="h-8 w-8 text-red-500"
              fill="none"
              viewBox="0 0 24 24"
              strokeWidth={1.5}
              stroke="currentColor"
              aria-hidden="true"
            >
              <path
                strokeLinecap="round"
                strokeLinejoin="round"
                d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"
              />
            </svg>
          </div>

          <h1 className="text-xl font-semibold text-gray-900 mb-2">
            页面出现了错误
          </h1>
          <p className="text-sm text-gray-500 mb-6">
            应用遇到了一个意外错误。您可以尝试刷新页面，或点击下方按钮重试。
          </p>

          {/* 开发环境：展示错误摘要 */}
          {process.env.NODE_ENV !== "production" && this.state.error && (
            <details className="mb-6 text-left rounded-lg bg-red-50 p-4 text-xs text-red-700">
              <summary className="cursor-pointer font-medium select-none mb-1">
                错误详情（开发模式）
              </summary>
              <pre className="whitespace-pre-wrap break-all mt-2 opacity-80">
                {this.state.error.message}
                {this.state.errorInfo?.componentStack}
              </pre>
            </details>
          )}

          <div className="flex flex-col gap-3">
            <button
              onClick={this.handleReset}
              className="w-full rounded-lg bg-blue-600 px-4 py-2.5 text-sm font-medium text-white hover:bg-blue-700 transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
            >
              重试
            </button>
            <button
              onClick={() => window.location.reload()}
              className="w-full rounded-lg border border-gray-200 px-4 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2"
            >
              刷新页面
            </button>
          </div>
        </div>
      </div>
    );
  }
}

export default ErrorBoundary;
