import { createContext, useContext, useEffect, useRef, ReactNode } from 'react';

/**
 * Captured console error with metadata
 */
export interface ConsoleError {
  message: string;
  source?: string;
  lineno?: number;
  colno?: number;
  timestamp: number;
}

/**
 * Context value for bug report error capture
 */
export interface BugReportContextValue {
  /** Get all captured console errors */
  getConsoleErrors: () => ConsoleError[];
  /** Clear all captured console errors */
  clearConsoleErrors: () => void;
}

const BugReportContext = createContext<BugReportContextValue | null>(null);

/** Maximum number of errors to retain */
const MAX_ERRORS = 20;

export interface BugReportProviderProps {
  children: ReactNode;
  /** Maximum number of errors to capture (default: 20) */
  maxErrors?: number;
}

/**
 * Provider component that captures console errors for bug reports.
 * Wrap your app with this provider to enable error capture.
 *
 * @example
 * ```tsx
 * import { BugReportProvider } from '@convex-dev/feedback/react';
 *
 * function App() {
 *   return (
 *     <BugReportProvider>
 *       <YourApp />
 *     </BugReportProvider>
 *   );
 * }
 * ```
 */
export function BugReportProvider({ children, maxErrors = MAX_ERRORS }: BugReportProviderProps) {
  const errorsRef = useRef<ConsoleError[]>([]);

  useEffect(() => {
    // Capture window.onerror
    const originalOnError = window.onerror;
    window.onerror = (message, source, lineno, colno, error) => {
      errorsRef.current.push({
        message: String(message),
        source,
        lineno: lineno ?? undefined,
        colno: colno ?? undefined,
        timestamp: Date.now(),
      });
      // Keep only last N errors
      if (errorsRef.current.length > maxErrors) {
        errorsRef.current = errorsRef.current.slice(-maxErrors);
      }
      // Call original handler if exists
      if (originalOnError) {
        return originalOnError(message, source, lineno, colno, error);
      }
      return false;
    };

    // Capture unhandled promise rejections
    const handleUnhandledRejection = (event: PromiseRejectionEvent) => {
      errorsRef.current.push({
        message: `Unhandled Promise Rejection: ${String(event.reason)}`,
        timestamp: Date.now(),
      });
      if (errorsRef.current.length > maxErrors) {
        errorsRef.current = errorsRef.current.slice(-maxErrors);
      }
    };
    window.addEventListener('unhandledrejection', handleUnhandledRejection);

    // Override console.error
    const originalConsoleError = console.error;
    console.error = (...args) => {
      errorsRef.current.push({
        message: args.map((arg) => {
          if (arg instanceof Error) {
            return `${arg.name}: ${arg.message}`;
          }
          return String(arg);
        }).join(' '),
        timestamp: Date.now(),
      });
      if (errorsRef.current.length > maxErrors) {
        errorsRef.current = errorsRef.current.slice(-maxErrors);
      }
      originalConsoleError.apply(console, args);
    };

    return () => {
      window.onerror = originalOnError;
      window.removeEventListener('unhandledrejection', handleUnhandledRejection);
      console.error = originalConsoleError;
    };
  }, [maxErrors]);

  const getConsoleErrors = () => [...errorsRef.current];
  const clearConsoleErrors = () => {
    errorsRef.current = [];
  };

  return (
    <BugReportContext.Provider value={{ getConsoleErrors, clearConsoleErrors }}>
      {children}
    </BugReportContext.Provider>
  );
}

/**
 * Hook to access bug report context for error capture.
 * Must be used within a BugReportProvider.
 *
 * @example
 * ```tsx
 * import { useBugReportContext } from '@convex-dev/feedback/react';
 *
 * function MyComponent() {
 *   const { getConsoleErrors, clearConsoleErrors } = useBugReportContext();
 *   // ...
 * }
 * ```
 */
export function useBugReportContext(): BugReportContextValue {
  const context = useContext(BugReportContext);
  if (!context) {
    throw new Error('useBugReportContext must be used within a BugReportProvider');
  }
  return context;
}
