import React from 'react';
import { LogUIError } from '../../wailsjs/go/main/App';

// A render error used to empty the window and leave nothing behind: no line in
// opslog.log, no dialog, no way for the operator to say more than "white screen".
// Two separate reports — one after logging a 10 GHz QSO, one on starting a DVK
// auto-call — stayed undiagnosed for weeks for exactly that reason.
//
// So this does two things, in order of importance:
//
//  1. Writes the error and its stack to the app log through LogUIError, so the
//     next report arrives WITH the cause instead of a description of a blank
//     screen.
//  2. Shows it, with the text selectable and a button to copy it, because the
//     person who can act on it is the one looking at the screen.
//
// It deliberately does NOT try to recover the tree. React cannot guarantee the
// state is sane after a render throw, and a half-working logger is worse than
// one that says plainly what happened and offers a reload.

type Props = { children: React.ReactNode };
type State = { error: Error | null; info: string };

export class ErrorBoundary extends React.Component<Props, State> {
  state: State = { error: null, info: '' };

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

  componentDidCatch(error: Error, info: React.ErrorInfo) {
    const stack = (info?.componentStack || error.stack || '').trim();
    this.setState({ info: stack });
    // Never let the reporting path throw: it runs while the app is already broken.
    try {
      LogUIError('render crash', `${error.name}: ${error.message}`, stack);
    } catch {
      /* the backend may be gone too — the on-screen copy still works */
    }
  }

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

    const report = `${error.name}: ${error.message}\n\n${info}`;
    return (
      <div style={{
        position: 'fixed', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center',
        padding: 24, background: '#0b1220', color: '#e5e7eb', fontFamily: 'ui-sans-serif, system-ui, sans-serif',
        overflow: 'auto', zIndex: 9999,
      }}>
        <div style={{ maxWidth: 820, width: '100%' }}>
          <h1 style={{ fontSize: 18, fontWeight: 700, margin: '0 0 6px' }}>
            OpsLog hit an error and stopped drawing
          </h1>
          <p style={{ fontSize: 13, opacity: 0.8, margin: '0 0 14px' }}>
            Your log is safe — this is the window, not the database. The details below
            are already in the app log; send them with your report.
          </p>
          <pre style={{
            whiteSpace: 'pre-wrap', wordBreak: 'break-word', userSelect: 'text',
            fontSize: 11.5, lineHeight: 1.5, background: '#111827', border: '1px solid #1f2937',
            borderRadius: 8, padding: 12, maxHeight: '48vh', overflow: 'auto', margin: 0,
          }}>{report}</pre>
          <div style={{ display: 'flex', gap: 8, marginTop: 14 }}>
            <button
              onClick={() => { void navigator.clipboard?.writeText(report); }}
              style={{
                padding: '7px 14px', borderRadius: 6, border: '1px solid #374151',
                background: '#1f2937', color: '#e5e7eb', fontSize: 13, cursor: 'pointer',
              }}>
              Copy the details
            </button>
            <button
              onClick={() => window.location.reload()}
              style={{
                padding: '7px 14px', borderRadius: 6, border: '1px solid #1d4ed8',
                background: '#2563eb', color: '#fff', fontSize: 13, fontWeight: 600, cursor: 'pointer',
              }}>
              Reload the window
            </button>
          </div>
        </div>
      </div>
    );
  }
}

// installGlobalErrorLogging catches what a boundary cannot: an error thrown from
// a timer, an event handler or a rejected promise. Those do not unmount the tree,
// so they leave no trace at all — and an auto-call loop lives entirely in
// callbacks, which is precisely where a white screen was reported.
export function installGlobalErrorLogging() {
  const report = (kind: string, message: string, stack?: string) => {
    try {
      LogUIError(kind, message, stack || '');
    } catch {
      /* nothing more we can do from here */
    }
  };

  window.addEventListener('error', (e: ErrorEvent) => {
    const src = e.filename ? ` (${e.filename}:${e.lineno}:${e.colno})` : '';
    report('uncaught error', (e.message || 'unknown error') + src, e.error?.stack);
  });

  window.addEventListener('unhandledrejection', (e: PromiseRejectionEvent) => {
    const r: any = e.reason;
    report('unhandled rejection', String(r?.message ?? r ?? 'unknown'), r?.stack);
  });
}
