// --- File: client/src/components/ErrorBoundary.tsx ---
// --- Tech Used ---
// - React: For building the user interface. Uses both Class Components (for ErrorBoundary itself) and Functional Components (for hooks/HOCs).
// - TypeScript: For static typing, improving code quality and maintainability.
// - Shadcn/ui (Likely): Based on "@/components/ui/..." imports. This library provides accessible and customizable UI components, often built on Tailwind CSS and Radix UI.
// - Lucide Icons (`lucide-react`): For clean and consistent SVG icons.
// - Custom Error Handling (`@/lib/errorHandler`): A centralized module for logging/processing errors.

// --- Imports ---
import React, { Component, ReactNode, ErrorInfo } from 'react'; // React.ErrorInfo provides types for componentDidCatch
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; // Added CardFooter
import { AlertTriangle, RefreshCw, Home, Copy } from "lucide-react"; // Added Copy icon
import { errorHandler } from "@/lib/errorHandler"; // Your custom error logging utility

// --- Component Props Interface ---
// Defines the expected properties for the ErrorBoundary component.
interface Props {
  children: ReactNode; // The React elements that this boundary will wrap.
  fallback?: ReactNode; // Optional custom UI to render when an error is caught.
  moduleName?: string; // A more descriptive name for the module/component being wrapped.
  onRetry?: () => void; // Optional callback for when the user clicks "Try Again".
}

// --- Component State Interface ---
// Defines the structure of the ErrorBoundary's internal state.
interface State {
  hasError: boolean; // True if an error has been caught.
  error?: Error; // The caught JavaScript error object.
  errorInfo?: ErrorInfo; // Additional info from React about the component stack.
  errorId?: string; // An ID for the logged error, useful for tracking.
}

// --- ErrorBoundary Class Component ---
// Function: Catches JavaScript errors in its child component tree, logs them, and displays a fallback UI.
export class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    // Initialize state: no error by default.
    this.state = { hasError: false };
  }

  // Lifecycle method: Called during the "render" phase if an error is thrown by a descendant.
  // Updates the state to trigger a re-render with the fallback UI.
  static getDerivedStateFromError(error: Error): Partial<State> {
    return { hasError: true, error };
  }

  // Lifecycle method: Called during the "commit" phase after an error is thrown and getDerivedStateFromError has been called.
  // Suitable for side effects like logging.
  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    const { moduleName } = this.props;
    const errorContext = moduleName || 'UnknownComponent';

    // Log the error using the custom error handler.
    const errorId = errorHandler.logError(
      error,
      errorContext,
      'react_component_crash', // A category for this type of error
      { componentStack: errorInfo.componentStack } // Include component stack
    );

    // Update state with the error information and ID.
    this.setState({ errorInfo, errorId });

    // Log to console for development purposes.
    console.error(`ErrorBoundary (${errorContext}): Uncaught error:`, error, errorInfo);
  }

  // Resets the error state, allowing children to attempt re-rendering.
  handleRetry = () => {
    if (this.props.onRetry) {
      this.props.onRetry(); // Call custom retry logic if provided
    }
    this.setState({ hasError: false, error: undefined, errorInfo: undefined, errorId: undefined });
  };

  // Navigates to the home page.
  // For SPAs, using a router's navigation method (e.g., useNavigate from react-router-dom) is better than a hard reload.
  handleGoHome = () => {
    // TODO: Replace with router navigation if using react-router or similar.
    // Example with react-router: navigate('/');
    window.location.href = '/';
  };

  // Copies the error ID to the clipboard.
  handleCopyErrorId = () => {
    if (this.state.errorId) {
      navigator.clipboard.writeText(this.state.errorId)
        .then(() => {
          // Optional: Show a temporary "Copied!" message
          console.info("Error ID copied to clipboard:", this.state.errorId);
        })
        .catch(err => {
          console.error("Failed to copy error ID:", err);
        });
    }
  };

  render() {
    if (this.state.hasError) {
      // Render custom fallback if provided
      if (this.props.fallback) {
        return this.props.fallback;
      }

      // Default fallback UI
      const { error, errorId, errorInfo } = this.state;
      const { moduleName } = this.props;
      const componentName = moduleName || 'the application';

      return (
        <div role="alert" className="min-h-screen bg-background text-foreground flex items-center justify-center p-4">
          <Card className="max-w-lg w-full shadow-xl border-destructive">
            <CardHeader className="text-center bg-destructive/10">
              <div className="mx-auto w-16 h-16 bg-destructive/20 rounded-full flex items-center justify-center mb-4 ring-4 ring-destructive/30">
                <AlertTriangle className="w-8 h-8 text-destructive" />
              </div>
              <CardTitle className="text-2xl font-bold text-destructive">An Error Occurred</CardTitle>
              <CardDescription className="text-muted-foreground">
                Sorry, something went wrong in {componentName}.
              </CardDescription>
            </CardHeader>
            <CardContent className="space-y-4 pt-6">
              <div className="bg-muted p-4 rounded-md border text-sm">
                <strong className="text-card-foreground">Message:</strong>
                <p className="font-mono text-destructive break-words whitespace-pre-wrap">
                  {error?.message || 'No specific error message available.'}
                </p>
                {errorId && (
                  <div className="mt-3 text-xs text-muted-foreground flex items-center justify-between">
                    <span>Error ID: {errorId}</span>
                    <Button variant="ghost" size="sm" onClick={this.handleCopyErrorId} aria-label="Copy Error ID">
                      <Copy className="w-3 h-3 mr-1" /> Copy ID
                    </Button>
                  </div>
                )}
              </div>

              {errorInfo?.componentStack && process.env.NODE_ENV === 'development' && (
                <details className="text-xs bg-muted p-2 rounded">
                  <summary className="cursor-pointer text-muted-foreground">Component Stack (Dev Only)</summary>
                  <pre className="mt-1 whitespace-pre-wrap break-all text-card-foreground/80">
                    {errorInfo.componentStack}
                  </pre>
                </details>
              )}
            </CardContent>
            <CardFooter className="flex flex-col sm:flex-row sm:justify-end gap-3 pt-6 border-t">
              <Button onClick={this.handleGoHome} variant="outline" className="w-full sm:w-auto">
                <Home className="w-4 h-4 mr-2" /> Go to Dashboard
              </Button>
              <Button onClick={this.handleRetry} variant="default" className="w-full sm:w-auto">
                <RefreshCw className="w-4 h-4 mr-2" /> Try Again
              </Button>
            </CardFooter>
          </Card>
        </div>
      );
    }
    return this.props.children; // Render children if no error
  }
}

// --- Custom Hook: useErrorHandler ---
// Function: Provides convenient error handling functions for use in Functional Components.
export function useErrorHandler(moduleName: string) { // moduleName: Context for the error (e.g., component name).

  // Function: Manually logs an error using the centralized errorHandler.
  const handleError = (error: Error, userActionContext?: string): string | undefined => {
    console.error(`Error handled in module '${moduleName}' (action: ${userActionContext || 'N/A'}):`, error);
    return errorHandler.logError(error, moduleName, userActionContext);
  };

  // Function: Wraps an asynchronous operation (Promise) to automatically catch and log errors.
  // T: Generic type for the expected data from the successful operation.
  // SIMPLIFIED VERSION FOR TESTING
  const handleAsyncOperation = async ( // Generic <T> removed for simplicity during testing
    operation: any, // Using 'any' for the operation itself
    userActionContext?: string | undefined // Adding userActionContext back as an optional string
  ): Promise<any> => { // Using 'any' for the Promise return type
    try {
      const data = await operation();
      return { success: true, data };
    } catch (err) {
      // Ensure the caught item is an Error instance before logging.
      const error = err instanceof Error ? err : new Error(String(err));
      const errorId = handleError(error, userActionContext); 
      return { success: false, errorId };
    }
  };

  return { handleError, handleAsyncOperation };
}

// --- Higher-Order Component (HOC): withErrorBoundary ---
// Function: A utility to easily wrap existing components with the ErrorBoundary.
// P: Generic type for the props of the component being wrapped.
export function withErrorBoundary<P extends React.JSX.IntrinsicAttributes>( // Use a more appropriate constraint for P
  WrappedComponent: React.ComponentType<P>,
  options: { moduleName: string; fallback?: ReactNode; onRetry?: () => void } // Options object
) {
  const { moduleName, fallback, onRetry } = options;
  // Returns a new component that renders the WrappedComponent inside an ErrorBoundary.
  const ComponentWithBoundary = (props: P) => ( // Give component a display name
    <ErrorBoundary moduleName={moduleName} fallback={fallback} onRetry={onRetry}>
      <WrappedComponent {...props} />
    </ErrorBoundary>
  );
  // Set a display name for easier debugging in React DevTools
  const wrappedComponentName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
  ComponentWithBoundary.displayName = `WithErrorBoundary(${wrappedComponentName})`;
  return ComponentWithBoundary;
}