import React, { Component, ErrorInfo, ReactNode } from 'react';
import { useLocation } from 'react-router-dom';

type BoundaryProps = {
  children: ReactNode;
};

type BoundaryState = {
  error: Error | null;
  componentStack: string | null;
};

/**
 * Without a boundary any render/effect throw unmounts the whole tree, which reads
 * as a blank white page on mobile with no way to see the cause on the device.
 */
class ErrorBoundaryView extends Component<BoundaryProps, BoundaryState> {
  constructor(props: BoundaryProps) {
    super(props);
    this.state = { error: null, componentStack: null };
  }

  static getDerivedStateFromError(error: Error): Partial<BoundaryState> {
    return { error };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
    console.error('App crashed', error, errorInfo.componentStack);
    this.setState({ componentStack: errorInfo.componentStack ?? null });
  }

  private details(): string {
    const { error, componentStack } = this.state;
    return [
      `Page: ${window.location.href}`,
      `Agent: ${navigator.userAgent}`,
      `Error: ${error?.name ?? 'Error'}: ${error?.message ?? 'unknown'}`,
      error?.stack ? `Stack:\n${error.stack}` : '',
      componentStack ? `Components:${componentStack}` : '',
    ]
      .filter(Boolean)
      .join('\n\n');
  }

  render(): ReactNode {
    const { error } = this.state;
    if (!error) {
      return this.props.children;
    }

    return (
      <div
        role="alert"
        style={{
          minHeight: '100vh',
          boxSizing: 'border-box',
          padding: '1.5rem 1rem',
          background: '#0f1220',
          color: '#f5f6fa',
          fontFamily: "'Inter', system-ui, sans-serif",
          overflowWrap: 'anywhere',
        }}
      >
        <div style={{ maxWidth: 640, margin: '0 auto' }}>
          <h1 style={{ fontSize: '1.35rem', margin: '0 0 0.75rem' }}>Something broke on this screen</h1>
          <p style={{ margin: '0 0 1rem', lineHeight: 1.5, color: 'rgba(245,246,250,0.75)' }}>
            The rest of the app still works. Reload to try again, or copy the details below so the failure can be fixed.
          </p>

          <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem', marginBottom: '1.25rem' }}>
            <button
              type="button"
              onClick={() => window.location.reload()}
              style={{
                padding: '0.7rem 1.1rem',
                borderRadius: '2rem',
                border: 'none',
                background: '#4c8dff',
                color: '#fff',
                fontWeight: 600,
                cursor: 'pointer',
              }}
            >
              Reload
            </button>
            <button
              type="button"
              onClick={() => {
                void navigator.clipboard?.writeText(this.details());
              }}
              style={{
                padding: '0.7rem 1.1rem',
                borderRadius: '2rem',
                border: '1px solid rgba(245,246,250,0.3)',
                background: 'transparent',
                color: '#f5f6fa',
                fontWeight: 600,
                cursor: 'pointer',
              }}
            >
              Copy details
            </button>
          </div>

          <pre
            style={{
              whiteSpace: 'pre-wrap',
              fontSize: '0.8rem',
              lineHeight: 1.5,
              background: 'rgba(255,255,255,0.06)',
              border: '1px solid rgba(255,255,255,0.12)',
              borderRadius: '0.75rem',
              padding: '1rem',
              margin: 0,
              maxHeight: '50vh',
              overflow: 'auto',
            }}
          >
            {this.details()}
          </pre>
        </div>
      </div>
    );
  }
}

/** Route change clears a previous crash so navigation is never permanently stuck. */
const AppErrorBoundary = ({ children }: BoundaryProps): JSX.Element => {
  const location = useLocation();
  return <ErrorBoundaryView key={location.pathname}>{children}</ErrorBoundaryView>;
};

export default AppErrorBoundary;
