import { Component } from 'react';
import type { ErrorInfo, ReactNode } from 'react';
import { AlertTriangle, ExternalLink, RotateCcw } from 'lucide-react';
import { Button } from '@/components/ui/button';

const REPO_URL = 'https://git.karti.ai/PIG/PIG-Demo';

export interface DemoErrorBoundaryProps {
  children: ReactNode;
  /** Named in the fallback copy, so the visitor knows what broke. */
  demoTitle?: string;
  /** Link to the exact source, if the caller knows it. Falls back to the repo. */
  sourceHref?: string;
  /** Called when the visitor asks to try again; use it to reset shell state. */
  onReset?: () => void;
}

interface DemoErrorBoundaryState {
  error: Error | null;
}

/**
 * One broken demo must never take the site down.
 *
 * This is a class because there is still no hook for `componentDidCatch`; that
 * is the entire reason for the exception to the function-component rule here.
 *
 * The fallback is deliberately calm and specific. A site whose pitch is
 * "here are the receipts" cannot answer a crash with a shrug: it names the
 * demo, links the source, and lets the visitor retry without a full reload.
 */
export class DemoErrorBoundary extends Component<DemoErrorBoundaryProps, DemoErrorBoundaryState> {
  override state: DemoErrorBoundaryState = { error: null };

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

  override componentDidCatch(error: Error, info: ErrorInfo) {
    // No telemetry endpoint on a static site, and none is wanted. The console
    // is the only place a maintainer can see this, so keep the component stack.
    console.error('[pig-demo] a demo surface threw', error, info.componentStack);
  }

  private handleReset = () => {
    this.setState({ error: null });
    this.props.onReset?.();
  };

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

    const { demoTitle, sourceHref } = this.props;
    return (
      <div role="alert" className="card mx-auto my-10 max-w-xl p-6">
        <div className="flex items-center gap-2 text-warning">
          <AlertTriangle className="size-5" aria-hidden="true" />
          <h2 className="text-base font-semibold">
            {demoTitle ? `${demoTitle} failed to render` : 'This demo failed to render'}
          </h2>
        </div>
        <p className="mt-3 text-sm leading-relaxed text-muted">
          Something in this demo threw while drawing. The rest of the site is unaffected — every
          other demo is a separate module. The environment and the recorded runs behind this page
          are in the repository either way, and you can run them yourself.
        </p>
        <p className="mt-3 break-words rounded-lg bg-surface-2 px-3 py-2 font-mono text-xs text-muted">
          {error.message || 'Unknown error'}
        </p>
        <div className="mt-5 flex flex-wrap gap-2">
          <Button size="touch" onClick={this.handleReset}>
            <RotateCcw aria-hidden="true" />
            Try again
          </Button>
          <Button variant="outline" size="touch" asChild>
            <a href={sourceHref ?? REPO_URL} rel="noreferrer">
              Read the source
              <ExternalLink aria-hidden="true" />
            </a>
          </Button>
        </div>
      </div>
    );
  }
}
