import * as React from "react";
import { ErrorState } from "@/components/ui/error-state";

interface ErrorBoundaryState {
  error: Error | null;
}

interface ErrorBoundaryProps {
  children: React.ReactNode;
  /** Render prop for full custom fallback */
  fallback?: (error: Error, reset: () => void) => React.ReactNode;
  /** Reset boundary when any value in this array changes (like `useEffect` deps) */
  resetKeys?: unknown[];
  /** Called once when a render error is caught */
  onError?: (error: Error, info: React.ErrorInfo) => void;
}

/**
 * Catches render-time errors anywhere below it and shows a fallback UI.
 * Wrap the whole app at the root (catches everything) and optionally
 * wrap individual routes/sections for finer-grained recovery.
 *
 * Note: does NOT catch errors inside event handlers, async code, or
 * server-side rendering. For those, surface errors via React Query
 * `onError` or `toast.error()`.
 */
export class ErrorBoundary extends React.Component<
  ErrorBoundaryProps,
  ErrorBoundaryState
> {
  state: ErrorBoundaryState = { error: null };

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

  componentDidCatch(error: Error, info: React.ErrorInfo) {
    this.props.onError?.(error, info);
    // eslint-disable-next-line no-console
    console.error("[ErrorBoundary]", error, info);
  }

  componentDidUpdate(prevProps: ErrorBoundaryProps) {
    if (!this.state.error) return;
    const prev = prevProps.resetKeys ?? [];
    const next = this.props.resetKeys ?? [];
    if (
      prev.length !== next.length ||
      prev.some((v, i) => !Object.is(v, next[i]))
    ) {
      this.reset();
    }
  }

  reset = () => this.setState({ error: null });

  render() {
    const { error } = this.state;
    if (!error) return this.props.children;
    if (this.props.fallback) return this.props.fallback(error, this.reset);

    return (
      <div className="mx-auto max-w-3xl px-6 py-16">
        <ErrorState
          severity="error"
          title="Something went wrong"
          description="An unexpected error broke this view. You can try reloading just this section, or refresh the page."
          detail={error.stack ?? error.message}
          onRetry={this.reset}
        />
      </div>
    );
  }
}
