
import * as React from 'react';
import {
  AlertTriangle,
  AlertCircle,
  Info,
  X,
  ChevronDown,
  ChevronUp,
  Bug,
  Copy,
} from 'lucide-react';
import { cn } from '@/lib/utils';
import { Button } from './button';
import { Card, CardContent } from './card';
import { Dialog } from './dialog';
import type { ApiError } from '@/schemas/apiSchemas';
import { formatUserFriendlyError } from '@/utils/errorMessages';
import { parseApiError, getErrorCategory } from '@/utils/apiErrorHandler';
import {
  formatIssueReport,
  copyIssueReportToClipboard,
  openGitHubIssue,
} from '@/utils/reportIssue';
import toast from '@/utils/toast';

export interface ErrorDisplayProps {
  error: Error | ApiError | string | { message: string; code?: string | number; status?: number; details?: Record<string, any> };
  onRetry?: () => void | Promise<void>;
  onDismiss?: () => void;
  showDetails?: boolean;
  context?: {
    action?: string;
    resource?: string;
    resourceId?: string;
    [key: string]: any;
  };
  variant?: 'inline' | 'banner' | 'modal' | 'card';
  severity?: 'error' | 'warning' | 'info';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
  dismissible?: boolean;
  title?: string;
  icon?: React.ReactNode;
  actions?: Array<{ label: string; onClick: () => void; variant?: 'default' | 'outline' | 'ghost' }>;
}

function normalizeError(error: any): { message: string; code?: string | number; status?: number; details?: any; stack?: string } {
  if (typeof error === 'string') return { message: error };
  if (error instanceof Error) return { message: error.message, stack: error.stack };
  if (error && typeof error === 'object') {
    try {
      const apiError = parseApiError(error);
      return {
        message: apiError.message || 'An error occurred',
        code: apiError.code,
        status: typeof apiError.code === 'number' ? apiError.code : undefined,
        details: apiError.details,
      };
    } catch {
      return {
        message: (error as any).message || String(error),
        code: (error as any).code,
        status: (error as any).status,
        details: (error as any).details,
      };
    }
  }
  return { message: String(error) };
}

export const ErrorDisplay = React.forwardRef<HTMLDivElement, ErrorDisplayProps>(({
  error, onRetry, onDismiss, showDetails: showDetailsProp, context,
  variant = 'inline', severity = 'error', size = 'md', className, dismissible,
  title: titleProp, icon: iconProp, actions = [], ...props
}, ref) => {
  const [isDetailsExpanded, setIsDetailsExpanded] = React.useState(false);
  const [isRetrying, setIsRetrying] = React.useState(false);
  const [isModalOpen, setIsModalOpen] = React.useState(true);

  const normalizedError = React.useMemo(() => normalizeError(error), [error]);
  const apiError = React.useMemo(() => parseApiError(error), [error]);
  const errorCategory = React.useMemo(() => getErrorCategory(apiError), [apiError]);
  const isServerError = React.useMemo(() => errorCategory === 'server_error' || (normalizedError.status !== undefined && normalizedError.status >= 500), [errorCategory, error]);
  const isDev = import.meta.env.DEV;
  const showDetails = showDetailsProp ?? isDev;

  const userMessage = React.useMemo(() => {
    if (normalizedError.message) {
      try {
        return formatUserFriendlyError(normalizedError as ApiError, context?.resource as any, false);
      } catch {
        return normalizedError.message;
      }
    }
    return 'An unexpected error occurred';
  }, [normalizedError, context]);

  const title = React.useMemo(() => {
    if (titleProp) return titleProp;
    if (context?.action) return `Error ${context.action}`;
    switch (severity) {
      case 'error': return 'Error';
      case 'warning': return 'Warning';
      case 'info': return 'Information';
      default: return 'Error';
    }
  }, [titleProp, context, severity]);

  const iconConfig = React.useMemo(() => {
    if (iconProp) return iconProp;
    const iconSize = size === 'sm' ? 'w-4 h-4' : size === 'lg' ? 'w-6 h-6' : 'w-5 h-5';
    switch (severity) {
      case 'error': return <AlertCircle className={cn(iconSize, 'text-destructive')} />;
      case 'warning': return <AlertTriangle className={cn(iconSize, 'text-warning')} />;
      case 'info': return <Info className={cn(iconSize, 'text-info')} />;
      default: return <AlertCircle className={cn(iconSize, 'text-destructive')} />;
    }
  }, [iconProp, severity, size]);

  const colorStyles = React.useMemo(() => {
    switch (severity) {
      case 'error': return { bg: 'bg-destructive/10', border: 'border-destructive/30', text: 'text-destructive', icon: 'text-destructive' };
      case 'warning': return { bg: 'bg-warning/10', border: 'border-warning/30', text: 'text-warning', icon: 'text-warning' };
      case 'info': return { bg: 'bg-info/10', border: 'border-info/30', text: 'text-info', icon: 'text-info' };
      default: return { bg: 'bg-destructive/10', border: 'border-destructive/30', text: 'text-destructive', icon: 'text-destructive' };
    }
  }, [severity]);

  const sizeStyles = React.useMemo(() => {
    switch (size) {
      case 'sm': return { padding: 'p-4', text: 'text-xs', title: 'text-sm', gap: 'gap-2' };
      case 'lg': return { padding: 'p-6', text: 'text-base', title: 'text-lg', gap: 'gap-4' };
      default: return { padding: 'p-4', text: 'text-sm', title: 'text-base', gap: 'gap-4' };
    }
  }, [size]);

  const handleRetry = React.useCallback(async () => {
    if (!onRetry || isRetrying) return;
    setIsRetrying(true);
    try { await onRetry(); } finally { setIsRetrying(false); }
  }, [onRetry, isRetrying]);

  const handleDismiss = React.useCallback(() => {
    if (onDismiss) onDismiss();
    if (variant === 'modal') setIsModalOpen(false);
  }, [onDismiss, variant]);

  const handleReportIssue = React.useCallback(async () => {
    try {
      const report = formatIssueReport(error, { component: context?.resource, action: context?.action, userId: context?.userId, additionalInfo: context });
      try { openGitHubIssue(report); toast.success('Opening GitHub issue...'); }
      catch { await copyIssueReportToClipboard(report); toast.success('Issue report copied to clipboard'); }
    } catch { toast.error('Failed to generate issue report'); }
  }, [error, context]);

  const handleCopyRequestId = React.useCallback(async () => {
    if (!apiError.request_id) return;
    try { await navigator.clipboard.writeText(apiError.request_id); toast.success('Request ID copied to clipboard'); }
    catch { toast.error('Failed to copy request ID'); }
  }, [apiError.request_id]);

  const isDismissible = dismissible ?? (onDismiss !== undefined || variant === 'modal');

  const renderDetails = () => {
    if (!showDetails || !isDetailsExpanded) return null;
    const details = [];
    if (apiError.request_id) details.push({ label: 'Request ID', value: apiError.request_id });
    if (normalizedError.code) details.push({ label: 'Error Code', value: String(normalizedError.code) });
    if (normalizedError.status) details.push({ label: 'HTTP Status', value: String(normalizedError.status) });
    if (normalizedError.details) details.push({ label: 'Details', value: JSON.stringify(normalizedError.details, null, 2) });
    if (normalizedError.stack) details.push({ label: 'Stack Trace', value: normalizedError.stack });
    if (context) details.push({ label: 'Context', value: JSON.stringify(context, null, 2) });
    if (details.length === 0) return null;

    return (
      <div className="mt-4 pt-4 border-t border-white/10">
        <div className="space-y-2">
          {details.map((detail, idx) => (
            <div key={idx} className="text-xs">
              <span className="font-semibold opacity-70">{detail.label}:</span>
              <pre className="mt-1 p-2 bg-black/20 rounded text-xs overflow-x-auto">
                {typeof detail.value === 'string' ? detail.value : JSON.stringify(detail.value, null, 2)}
              </pre>
            </div>
          ))}
        </div>
      </div>
    );
  };

  const errorContent = (
    <div ref={ref} role="alert" aria-live="polite" className={cn('rounded-lg border flex shadow-card', colorStyles.bg, colorStyles.border, colorStyles.text, sizeStyles.padding, sizeStyles.gap, className)} {...props}>
      <div className="flex-shrink-0 pt-0.5">{iconConfig}</div>
      <div className="flex-1 min-w-0">
        <div className={cn('font-semibold mb-1', sizeStyles.title)}>{title}</div>
        <div className={cn('opacity-90', sizeStyles.text)}>{userMessage}</div>
        {renderDetails()}
        {(onRetry || actions.length > 0 || isServerError || (showDetails && (normalizedError.code || normalizedError.details || normalizedError.stack || context))) && (
          <div className="mt-4 flex flex-wrap gap-2 items-center">
            {onRetry && <Button variant="outline" size={size === 'sm' ? 'sm' : 'default'} onClick={handleRetry} disabled={isRetrying} className="border-current text-current hover:bg-current/10 bg-transparent">{isRetrying ? 'Retrying...' : 'Retry'}</Button>}
            {isServerError && apiError.request_id && (
              <>
                <Button variant="outline" size={size === 'sm' ? 'sm' : 'default'} onClick={handleCopyRequestId} className="border-current text-current hover:bg-current/10 bg-transparent" title="Copy Request ID"><Copy className="w-4 h-4 mr-1.5" />Copy ID</Button>
                <Button variant="outline" size={size === 'sm' ? 'sm' : 'default'} onClick={handleReportIssue} className="border-current text-current hover:bg-current/10 bg-transparent"><Bug className="w-4 h-4 mr-1.5" />Report</Button>
              </>
            )}
            {actions.map((action, idx) => (
              <Button key={idx} variant={action.variant || 'outline'} size={size === 'sm' ? 'sm' : 'default'} onClick={action.onClick} className="border-current text-current hover:bg-current/10 bg-transparent">{action.label}</Button>
            ))}
            {showDetails && (normalizedError.code || normalizedError.details || normalizedError.stack || context) && (
              <Button variant="ghost" size={size === 'sm' ? 'sm' : 'default'} onClick={() => setIsDetailsExpanded(!isDetailsExpanded)} className="text-current hover:bg-current/10 hover:text-current">{isDetailsExpanded ? <><ChevronUp className="w-4 h-4 mr-1" />Hide Details</> : <><ChevronDown className="w-4 h-4 mr-1" />Show Details</>}</Button>
            )}
          </div>
        )}
      </div>
      {isDismissible && (
        <button onClick={handleDismiss} className="opacity-70 hover:opacity-100 transition-opacity flex-shrink-0 self-start" aria-label="Dismiss error">
          <X className={cn(size === 'sm' ? 'w-4 h-4' : 'w-5 h-5')} />
        </button>
      )}
    </div>
  );

  switch (variant) {
    case 'banner': return <div className="w-full">{errorContent}</div>;
    case 'card': return <Card className={cn(colorStyles.border, className, "glass")}><CardContent className={cn(sizeStyles.padding, 'pt-6')}>{errorContent}</CardContent></Card>;
    case 'modal': return (
      <Dialog open={isModalOpen} onClose={handleDismiss} title={title} variant={severity === 'error' ? 'alert' : 'default'} footer={
        <div className="flex gap-2 justify-end">
          {onRetry && <Button variant="outline" onClick={handleRetry} disabled={isRetrying}>{isRetrying ? 'Retrying...' : 'Retry'}</Button>}
          {actions.map((action, idx) => <Button key={idx} variant={action.variant || 'outline'} onClick={action.onClick}>{action.label}</Button>)}
          <Button variant="default" onClick={handleDismiss}>{onRetry ? 'Close' : 'Dismiss'}</Button>
        </div>
      }>
        <div className={cn(colorStyles.text)}>
          <div className={cn('opacity-90 mb-4', sizeStyles.text)}>{userMessage}</div>
          {renderDetails()}
        </div>
      </Dialog>
    );
    case 'inline': default: return errorContent;
  }
});

ErrorDisplay.displayName = 'ErrorDisplay';
