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

/**
 * ErrorBoundary - React 渲染异常兜底（fallback UI）
 *
 * 用途：捕获子组件树渲染异常，展示降级 UI，避免整页白屏。
 *
 * @example
 * <ErrorBoundary fallback={<ErrorFallback />}>
 *   <Dashboard />
 * </ErrorBoundary>
 */

export interface ErrorBoundaryProps {
  children: ReactNode;
  /** 自定义降级 UI；未提供时使用默认 fallback */
  fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);
  /** 错误回调（上报 / 日志） */
  onError?: (error: Error, info: ErrorInfo) => void;
  /** 重置回调（用于外部触发重试） */
  onReset?: () => void;
}

export interface ErrorBoundaryState {
  error: Error | null;
}

export class ErrorBoundary extends Component<
  ErrorBoundaryProps,
  ErrorBoundaryState
> {
  state: ErrorBoundaryState = { error: null };

  static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    return { error };
  }

  componentDidCatch(error: Error, info: ErrorInfo): void {
    this.props.onError?.(error, info);
  }

  reset = (): void => {
    this.props.onReset?.();
    this.setState({ error: null });
  };

  render(): ReactNode {
    const { error } = this.state;
    const { children, fallback } = this.props;

    if (error) {
      if (typeof fallback === "function") {
        return fallback(error, this.reset);
      }
      if (fallback !== undefined) {
        return fallback;
      }
      return <DefaultErrorFallback error={error} onReset={this.reset} />;
    }

    return children;
  }
}

function DefaultErrorFallback({
  error,
  onReset,
}: {
  error: Error;
  onReset: () => void;
}): ReactNode {
  return (
    <div
      role="alert"
      className="flex min-h-[200px] flex-col items-center justify-center gap-4 p-8"
    >
      <h2 className="text-lg font-semibold">出错了</h2>
      <p className="text-sm text-muted-foreground">{error.message}</p>
      <button
        type="button"
        onClick={onReset}
        className="rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground hover:bg-primary/90"
      >
        重试
      </button>
    </div>
  );
}
