import { createContext, useContext, useState, useCallback, useEffect, type ReactNode, Component, type ErrorInfo } from 'react';

export type ToastLevel = 'info' | 'success' | 'error';

interface Toast {
  id: string;
  message: string;
  level: ToastLevel;
}

interface ToastContextType {
  toasts: Toast[];
  notify: (message: string, level?: ToastLevel, duration?: number) => void;
  notifyError: (message: string) => void;
  notifySuccess: (message: string) => void;
  dismiss: (id: string) => void;
}

const ToastContext = createContext<ToastContextType | undefined>(undefined);

export function ToastProvider({ children }: { children: ReactNode }) {
  const [toasts, setToasts] = useState<Toast[]>([]);

  const dismiss = useCallback((id: string) => {
    setToasts(prev => prev.filter(t => t.id !== id));
  }, []);

  const notify = useCallback((message: string, level: ToastLevel = 'info', duration = 4000) => {
    const id = crypto.randomUUID();
    setToasts(prev => [...prev.slice(-4), { id, message, level }]);
    setTimeout(() => dismiss(id), duration);
  }, [dismiss]);

  const notifyError = useCallback((message: string) => notify(message, 'error', 6000), [notify]);
  const notifySuccess = useCallback((message: string) => notify(message, 'success', 3000), [notify]);

  // Listen for global error events from store/other modules that can't access context directly
  useEffect(() => {
    const handler = (e: Event) => {
      const msg = (e as CustomEvent).detail;
      if (msg) notifyError(String(msg));
    };
    window.addEventListener('muse:error', handler);
    window.addEventListener('muse:success', (e: Event) => notifySuccess(String((e as CustomEvent).detail)));
    window.addEventListener('muse:info', (e: Event) => notify(String((e as CustomEvent).detail)));
    return () => {
      window.removeEventListener('muse:error', handler);
    };
  }, [notifyError, notifySuccess, notify]);

  return (
    <ToastContext.Provider value={{ toasts, notify, notifyError, notifySuccess, dismiss }}>
      {children}
      {toasts.length > 0 && (
        <div className="fixed bottom-4 left-1/2 -translate-x-1/2 z-[9999] flex flex-col gap-2 items-center pointer-events-none">
          {toasts.map(toast => (
            <div
              key={toast.id}
              className={`pointer-events-auto px-4 py-2.5 rounded-xl shadow-2xl backdrop-blur-md border text-[12px] font-medium flex items-center gap-2 max-w-[420px] cursor-pointer transition-all ${
                toast.level === 'error' ? 'bg-[#FF453A]/15 border-[#FF453A]/30 text-[#FF6961]' :
                toast.level === 'success' ? 'bg-[#30D158]/15 border-[#30D158]/30 text-[#30D158]' :
                'bg-[#1C1C1E]/90 border-white/10 text-white/90'
              }`}
              onClick={() => dismiss(toast.id)}
            >
              {toast.level === 'error' && <span className="shrink-0 text-[14px]">⚠</span>}
              {toast.level === 'success' && <span className="shrink-0 text-[14px]">✓</span>}
              <span className="break-words">{toast.message}</span>
            </div>
          ))}
        </div>
      )}
    </ToastContext.Provider>
  );
}

export function useToast() {
  const ctx = useContext(ToastContext);
  if (!ctx) throw new Error('useToast must be used within ToastProvider');
  return ctx;
}

/**
 * Safe invoke wrapper — surfaces Rust command errors as toast notifications.
 * Use instead of raw invoke().catch(() => {}).
 */
export async function safeInvoke<T>(
  command: string,
  args: Record<string, unknown> = {},
  options?: { silent?: boolean; label?: string }
): Promise<T | undefined> {
  const { invoke } = await import('@tauri-apps/api/core');
  try {
    return await invoke<T>(command, args);
  } catch (err) {
    const message = typeof err === 'string' ? err : (err as Error)?.message || 'Unknown error';
    if (!options?.silent) {
      const label = options?.label || command.replace(/_/g, ' ');
      window.dispatchEvent(new CustomEvent('muse:error', { detail: `${label}: ${message}` }));
    }
    console.error(`[invoke:${command}]`, err);
    return undefined;
  }
}

/**
 * React Error Boundary — catches render crashes and shows recovery UI.
 */
interface ErrorBoundaryProps { children: ReactNode; fallback?: ReactNode; }
interface ErrorBoundaryState { hasError: boolean; error: Error | null; }

export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
  constructor(props: ErrorBoundaryProps) { super(props); this.state = { hasError: false, error: null }; }
  static getDerivedStateFromError(error: Error) { return { hasError: true, error }; }
  componentDidCatch(error: Error, info: ErrorInfo) { console.error('[ErrorBoundary]', error, info.componentStack); }
  render() {
    if (this.state.hasError) {
      return this.props.fallback || (
        <div className="w-screen h-screen bg-[#0A0A0B] flex items-center justify-center">
          <div className="text-center max-w-md">
            <div className="text-[#FF453A] text-4xl mb-4">⚠</div>
            <h1 className="text-white text-xl font-semibold mb-2">Something went wrong</h1>
            <p className="text-white/50 text-sm mb-4">{this.state.error?.message || 'An unexpected error occurred'}</p>
            <button onClick={() => { this.setState({ hasError: false, error: null }); }} className="px-4 py-2 bg-white/10 hover:bg-white/15 border border-white/10 rounded-lg text-white text-sm font-medium transition-colors">Try Again</button>
            <button onClick={() => window.location.reload()} className="ml-3 px-4 py-2 bg-[#0A84FF]/20 hover:bg-[#0A84FF]/30 border border-[#0A84FF]/30 rounded-lg text-[#0A84FF] text-sm font-medium transition-colors">Reload App</button>
          </div>
        </div>
      );
    }
    return this.props.children;
  }
}
