'use client';

import React from 'react';
import { ErrorHandler, BaseError } from '../../../core/errors';

interface ErrorBoundaryState {
  hasError: boolean;
  error?: Error;
  errorInfo?: React.ErrorInfo;
}

interface ErrorBoundaryProps {
  children: React.ReactNode;
  fallback?: React.ComponentType<{ error: Error; retry: () => void }>;
}

export class PayrollErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
  private errorHandler: ErrorHandler;

  constructor(props: ErrorBoundaryProps) {
    super(props);
    this.state = { hasError: false };
    this.errorHandler = ErrorHandler.getInstance();
  }

  static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    this.errorHandler.handleError(error, {
      metadata: {
        type: 'react_error_boundary',
        componentStack: errorInfo.componentStack,
        errorBoundary: 'PayrollErrorBoundary'
      }
    });
    
    this.setState({ error, errorInfo });
  }

  private handleRetry = () => {
    this.setState({ hasError: false, error: undefined, errorInfo: undefined });
  };

  render() {
    if (this.state.hasError) {
      const FallbackComponent = this.props.fallback || DefaultErrorFallback;
      return <FallbackComponent error={this.state.error!} retry={this.handleRetry} />;
    }

    return this.props.children;
  }
}

interface ErrorFallbackProps {
  error: Error;
  retry: () => void;
}

const DefaultErrorFallback: React.FC<ErrorFallbackProps> = ({ error, retry }) => {
  const isBaseError = error instanceof BaseError;
  
  return (
    <div className="min-h-[400px] flex items-center justify-center p-6">
      <div className="max-w-md w-full bg-white rounded-lg shadow-lg border border-red-200">
        <div className="p-6">
          <div className="flex items-center mb-4">
            <div className="flex-shrink-0">
              <svg className="h-8 w-8 text-red-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 19.5c-.77.833.192 2.5 1.732 2.5z" />
              </svg>
            </div>
            <div className="ml-3">
              <h3 className="text-lg font-medium text-gray-900">
                Something went wrong
              </h3>
            </div>
          </div>
          
          <div className="mb-4">
            <p className="text-sm text-gray-600 mb-2">
              {isBaseError ? error.message : 'An unexpected error occurred while loading this component.'}
            </p>
            
            {isBaseError && (
              <div className="bg-gray-50 rounded-md p-3">
                <div className="text-xs text-gray-500 space-y-1">
                  <div><strong>Error Code:</strong> {error.code}</div>
                  {error.context?.timestamp && (
                    <div><strong>Time:</strong> {new Date(error.context.timestamp).toLocaleString()}</div>
                  )}
                </div>
              </div>
            )}
          </div>
          
          <div className="flex space-x-3">
            <button
              onClick={retry}
              className="flex-1 bg-blue-600 text-white px-4 py-2 rounded-md text-sm font-medium hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
            >
              Try Again
            </button>
            <button
              onClick={() => window.location.reload()}
              className="flex-1 bg-gray-300 text-gray-700 px-4 py-2 rounded-md text-sm font-medium hover:bg-gray-400 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2"
            >
              Reload Page
            </button>
          </div>
        </div>
      </div>
    </div>
  );
};

// Hook for handling async errors in functional components
export function useErrorHandler() {
  const errorHandler = React.useMemo(() => ErrorHandler.getInstance(), []);
  
  const handleError = React.useCallback((error: Error, context?: Record<string, any>) => {
    errorHandler.handleError(error, context);
  }, [errorHandler]);

  const wrapAsync = React.useCallback(<T extends (...args: any[]) => Promise<any>>(
    fn: T,
    context?: Record<string, any>
  ): T => {
    return (async (...args: any[]) => {
      try {
        return await fn(...args);
      } catch (error) {
        handleError(error as Error, context);
        throw error;
      }
    }) as T;
  }, [handleError]);

  return { handleError, wrapAsync };
}

// Loading state component with error handling
interface AsyncComponentProps<T> {
  asyncFn: () => Promise<T>;
  children: (data: T) => React.ReactNode;
  fallback?: React.ReactNode;
  errorFallback?: React.ComponentType<ErrorFallbackProps>;
}

export function AsyncComponent<T>({ 
  asyncFn, 
  children, 
  fallback = <div>Loading...</div>,
  errorFallback: ErrorFallback = DefaultErrorFallback
}: AsyncComponentProps<T>) {
  const [state, setState] = React.useState<{
    data?: T;
    loading: boolean;
    error?: Error;
  }>({ loading: true });

  const { handleError } = useErrorHandler();

  const loadData = React.useCallback(async () => {
    try {
      setState({ loading: true });
      const data = await asyncFn();
      setState({ data, loading: false });
    } catch (error) {
      const err = error as Error;
      handleError(err, { component: 'AsyncComponent' });
      setState({ error: err, loading: false });
    }
  }, [asyncFn, handleError]);

  React.useEffect(() => {
    loadData();
  }, [loadData]);

  if (state.loading) {
    return <>{fallback}</>;
  }

  if (state.error) {
    return <ErrorFallback error={state.error} retry={loadData} />;
  }

  if (state.data) {
    return <>{children(state.data)}</>;
  }

  return null;
}