/**
 * React error boundary component.
 *
 * Catches render-time exceptions in child components and shows a fallback UI.
 * This is the base component; use PageErrorBoundary or SectionErrorBoundary
 * for page and section-level boundaries.
 *
 * All errors are logged using the telemetry service with structured context
 * for distributed tracing and debugging.
 */
import React from "react";
import { Button, makeStyles, Text, tokens } from "@fluentui/react-components";
import { recordCritical } from "../utils/telemetry";

interface ErrorBoundaryState {
  hasError: boolean;
  errorMessage: string | null;
}

interface ErrorBoundaryProps {
  children: React.ReactNode;
  title?: string;
  message?: string;
  showReloadButton?: boolean;
  isFullPage?: boolean;
  onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
}

interface ErrorBoundaryFallbackProps {
  title: string;
  message: string;
  showReloadButton: boolean;
  isFullPage: boolean;
  onReload: () => void;
}

const useFullPageStyles = makeStyles({
  root: {
    display: "flex",
    flexDirection: "column",
    alignItems: "center",
    justifyContent: "center",
    minHeight: "100vh",
    padding: tokens.spacingVerticalL,
    textAlign: "center",
    gap: tokens.spacingVerticalM,
  },
  message: {
    maxWidth: "40rem",
  },
});

const useSectionStyles = makeStyles({
  root: {
    display: "flex",
    flexDirection: "column",
    alignItems: "flex-start",
    padding: tokens.spacingVerticalM,
    backgroundColor: tokens.colorNeutralBackground3,
    borderRadius: tokens.borderRadiusMedium,
    border: `1px solid ${tokens.colorStatusWarningForeground1}`,
    gap: tokens.spacingVerticalM,
  },
  message: {
    color: tokens.colorNeutralForeground1,
  },
});

function ErrorBoundaryFallback({
  title,
  message,
  showReloadButton,
  isFullPage,
  onReload,
}: ErrorBoundaryFallbackProps): React.ReactElement {
  const fullPageStyles = useFullPageStyles();
  const sectionStyles = useSectionStyles();
  const styles = isFullPage ? fullPageStyles : sectionStyles;

  return (
    <div className={styles.root} role="alert" data-testid="page-error-boundary">
      <Text as={isFullPage ? "h1" : "h2"} size={isFullPage ? 700 : 500} weight="semibold">
        {title}
      </Text>
      <Text size={isFullPage ? 300 : 200} className={isFullPage ? fullPageStyles.message : sectionStyles.message}>
        {message}
      </Text>
      {showReloadButton && (
        <Button appearance="primary" onClick={onReload}>
          {isFullPage ? "Reload Page" : "Retry"}
        </Button>
      )}
    </div>
  );
}

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

  static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    return { hasError: true, errorMessage: error.message || "Unknown error" };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
    const { onError } = this.props;

    // Extract componentStack from errorInfo
    // Note: componentStack is not part of the public React.ErrorInfo type definition,
    // but is available at runtime via React DevTools. We cast to any to access it.
    // This captures the React component hierarchy where the error occurred, which is
    // valuable for debugging but may change in future React versions.
    // TODO: Consider removing when React types include componentStack in ErrorInfo
    // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
    const componentStack = (errorInfo as any).componentStack as string | undefined;

    // Log the error using telemetry for distributed tracing
    recordCritical("component_render_error", error, {
      component_stack: componentStack,
      error_message: error.message,
    });

    if (onError) {
      onError(error, errorInfo);
    } else {
      console.error("ErrorBoundary caught an error", { error, errorInfo });
    }
  }

  handleReload = (): void => {
    if (this.props.isFullPage) {
      window.location.reload();
    } else {
      this.setState({ hasError: false, errorMessage: null });
    }
  };

  render(): React.ReactNode {
    if (this.state.hasError) {
      const {
        title = "Something went wrong",
        message = "Please try again or contact support if the problem persists.",
        showReloadButton = true,
        isFullPage = true,
      } = this.props;

      return (
        <ErrorBoundaryFallback
          title={title}
          message={message}
          showReloadButton={showReloadButton}
          isFullPage={isFullPage}
          onReload={this.handleReload}
        />
      );
    }

    return this.props.children;
  }
}
