import React from 'react';

// ─────────────────────────────────────────────────────────────────────────────
// ErrorBoundary — reusable class-based error boundary
//
// React error boundaries MUST be class components. This is the single base
// implementation used throughout the app via three pre-configured wrappers:
//
//   <AppErrorBoundary>    — Root level: catches provider/context crashes
//   <PageErrorBoundary>   — Route level: catches page-level render errors
//   <SectionErrorBoundary>— Feature level: catches isolated section errors
//
// Usage:
//   import { PageErrorBoundary } from '@/components/shared/error-boundary';
//   <PageErrorBoundary><MyPage /></PageErrorBoundary>
// ─────────────────────────────────────────────────────────────────────────────

// ── Types ─────────────────────────────────────────────────────────────────────

export interface FallbackProps {
  error: Error;
  reset: () => void;
}

interface ErrorBoundaryProps {
  children: React.ReactNode;
  /** Custom fallback UI. Receives the error and a reset callback. */
  fallback: React.ComponentType<FallbackProps>;
  /**
   * Optional callback fired when an error is caught.
   * Use to log to Sentry, Datadog, etc.
   */
  onError?: (error: Error, info: React.ErrorInfo) => void;
  /**
   * List of values that, when changed, automatically reset the boundary.
   * Useful for resetting on route changes (pass `location.pathname`).
   */
  resetKeys?: unknown[];
}

interface ErrorBoundaryState {
  hasError: boolean;
  error: Error | null;
}

// ── Core class ────────────────────────────────────────────────────────────────

export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
  constructor(props: ErrorBoundaryProps) {
    super(props);
    this.state = { hasError: false, error: null };
    this.reset = this.reset.bind(this);
  }

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

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

    if (import.meta.env.DEV) {
      console.group('[ErrorBoundary] Uncaught error');
      console.error(error);
      console.error('Component stack:', info.componentStack);
      console.groupEnd();
    }
  }

  componentDidUpdate(prevProps: ErrorBoundaryProps) {
    if (!this.state.hasError) return;

    const { resetKeys } = this.props;
    if (!resetKeys?.length) return;

    const prevKeys = prevProps.resetKeys ?? [];
    const changed = resetKeys.some((key, i) => key !== prevKeys[i]);
    if (changed) this.reset();
  }

  reset() {
    this.setState({ hasError: false, error: null });
  }

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

    if (hasError && error) {
      return <Fallback error={error} reset={this.reset} />;
    }

    return children;
  }
}

// ── Pre-configured variants ───────────────────────────────────────────────────
// Import the fallbacks from error-fallbacks.tsx to avoid circular deps.
// These are thin wrappers so callers never need to wire up the fallback prop.

import { AppErrorFallback, PageErrorFallback, SectionErrorFallback } from './error-fallbacks';

interface BoundaryProps {
  children: React.ReactNode;
  onError?: (error: Error, info: React.ErrorInfo) => void;
  resetKeys?: unknown[];
}

/**
 * `AppErrorBoundary` — root level.
 *
 * Place at the very top of the tree, outside all providers. If something inside
 * the provider stack crashes during setup, this boundary prevents a blank screen.
 */
export function AppErrorBoundary({ children, onError, resetKeys }: BoundaryProps) {
  return (
    <ErrorBoundary fallback={AppErrorFallback} onError={onError} resetKeys={resetKeys}>
      {children}
    </ErrorBoundary>
  );
}

/**
 * `PageErrorBoundary` — route level.
 *
 * Wrap `<AppRoutes>` / `<AuthGuard>` inside the router. When a lazy page chunk
 * fails to load, or a page component throws, the rest of the app (providers,
 * nav chrome) stays alive and only the page area shows the error UI.
 */
export function PageErrorBoundary({ children, onError, resetKeys }: BoundaryProps) {
  return (
    <ErrorBoundary fallback={PageErrorFallback} onError={onError} resetKeys={resetKeys}>
      {children}
    </ErrorBoundary>
  );
}

/**
 * `SectionErrorBoundary` — feature/section level.
 *
 * Use around individual sections within a page (e.g., a data table, a chart,
 * the assistant panel). An error in one section does not crash the entire page.
 */
export function SectionErrorBoundary({ children, onError, resetKeys }: BoundaryProps) {
  return (
    <ErrorBoundary fallback={SectionErrorFallback} onError={onError} resetKeys={resetKeys}>
      {children}
    </ErrorBoundary>
  );
}
