/**
 * Enhanced Error Reporter with user notifications and environment-specific handling
 */

import { ErrorReporter } from '../interfaces/ErrorReporter';
import { Logger } from '../interfaces/Logger';
import { ApiError } from '../api/base/ApiError';
import { connectionMonitor } from '../api/base/ApiConnectionMonitor';

// Import notification system (will be used if available)
try {
  // Dynamically import to avoid circular dependencies
  import('@/components/notifications/NotificationProvider').then(_module => {
    // Notification system available
  }).catch(() => {
    // Notification system not available yet
  });
} catch {
  // Silent fail - notification system may not be initialized
}

export interface EnhancedErrorReporterOptions {
  /**
   * Whether to show user-facing notifications
   */
  showUserNotifications?: boolean;
  
  /**
   * Whether to log to console (always true in dev)
   */
  logToConsole?: boolean;
  
  /**
   * Whether to report to external service (e.g., Sentry)
   */
  reportToExternal?: boolean;
  
  /**
   * Custom error handler for specific error types
   */
  customHandlers?: Record<string, (error: ApiError) => void>;
}

export class EnhancedErrorReporter implements ErrorReporter {
  private options: EnhancedErrorReporterOptions;
  private logger: Logger;
  private errorBuffer: Array<{ error: ApiError; context: unknown }> = [];
  private readonly MAX_BUFFER_SIZE = 50;

  constructor(logger: Logger, options: EnhancedErrorReporterOptions = {}) {
    this.logger = logger;
    this.options = {
      showUserNotifications: options.showUserNotifications ?? true,
      logToConsole: options.logToConsole ?? true,
      reportToExternal: options.reportToExternal ?? false,
      customHandlers: options.customHandlers || {},
    };
  }

  /**
   * Main error reporting method
   */
  report(error: Error, context?: unknown): void {
    // Only handle ApiError instances for enhanced reporting
    if (!(error instanceof ApiError)) {
      // For non-API errors, use basic logging
      if (this.options.logToConsole) {
        console.error('Non-API Error:', error, context);
      }
      return;
    }

    // Add to buffer for potential batch reporting
    this.addToBuffer(error, context);

    // Log based on environment and severity
    this.logError(error, context);

    // Handle custom error types
    this.handleCustomHandlers(error);

    // Show user notifications if enabled
    if (this.options.showUserNotifications) {
      this.showUserNotification(error);
    }

    // Report to external services if configured
    if (this.options.reportToExternal) {
      this.reportToExternal(error, context);
    }

    // Update connection monitor
    if (error.isConnectivityIssue()) {
      connectionMonitor.recordFailure(error);
    }
  }

  /**
   * Log error with appropriate severity
   */
  private logError(error: ApiError, context: unknown): void {
    if (!this.options.logToConsole) return;

    const isDev = process.env.NODE_ENV === 'development';
    const severity = error.getSeverity();

    const message = isDev ? error.getDeveloperMessage() : error.getUserMessage();
    const contextObj = typeof context === 'object' && context !== null ? context : {};
    const errorContextObj = typeof error.context === 'object' && error.context !== null ? error.context : {};
    
    const logContext = {
      ...errorContextObj,
      ...contextObj,
      type: error.type,
      isRetryable: error.isRetryable(),
      isConnectivity: error.isConnectivityIssue(),
    };

    if (severity === 'error') {
      this.logger.error(message, error, logContext);
      if (isDev) {
        console.error(`[API-ERROR] ${message}`, { error, context: logContext });
      }
    } else if (severity === 'warn') {
      this.logger.warn(message, logContext);
      if (isDev) {
        console.warn(`[API-WARN] ${message}`, { context: logContext });
      }
    } else {
      this.logger.info(message, logContext);
      if (isDev) {
        console.log(`[API-INFO] ${message}`, { context: logContext });
      }
    }
  }

  /**
   * Show user-facing notification
   */
  private showUserNotification(error: ApiError): void {
    const isDev = process.env.NODE_ENV === 'development';

    // In development, we might want to show more details
    if (isDev) {
      // Use console notification in dev
      console.log(`%c[USER-NOTIFICATION] ${error.getUserMessage()}`, 
        'background: #222; color: #bada55; padding: 4px 8px; border-radius: 4px;'
      );
      return;
    }

    // In production, use the notification system if available
    // This is a deferred import to avoid circular dependencies
    if (typeof window !== 'undefined') {
      setTimeout(() => {
        try {
          // Try to access notification context if available
          const notificationEvent = new CustomEvent('gridpilot-notification', {
            detail: {
              type: 'error',
              title: this.getNotificationTitle(error),
              message: error.getUserMessage(),
              variant: error.isConnectivityIssue() ? 'modal' : 'toast',
              autoDismiss: !error.isConnectivityIssue(),
            }
          });
          window.dispatchEvent(notificationEvent);
        } catch (e) {
          // Fallback to browser alert if notification system unavailable
          if (error.isConnectivityIssue()) {
            console.warn('API Error:', error.getUserMessage());
          }
        }
      }, 100);
    }
  }

  /**
   * Get appropriate notification title
   */
  private getNotificationTitle(error: ApiError): string {
    switch (error.type) {
      case 'NETWORK_ERROR':
        return 'Connection Lost';
      case 'TIMEOUT_ERROR':
        return 'Request Timed Out';
      case 'AUTH_ERROR':
        return 'Authentication Required';
      case 'SERVER_ERROR':
        return 'Server Error';
      case 'RATE_LIMIT_ERROR':
        return 'Rate Limit Reached';
      default:
        return 'Something Went Wrong';
    }
  }

  /**
   * Handle custom error type handlers
   */
  private handleCustomHandlers(error: ApiError): void {
    if (this.options.customHandlers && this.options.customHandlers[error.type]) {
      try {
        this.options.customHandlers[error.type]!(error);
      } catch (handlerError) {
        console.error('Custom error handler failed:', handlerError);
      }
    }
  }

  /**
   * Report to external services (placeholder for future integration)
   */
  private reportToExternal(error: ApiError, context: unknown): void {
    // Placeholder for external error reporting (e.g., Sentry, LogRocket)
    // In a real implementation, this would send to your error tracking service
    
    if (process.env.NODE_ENV === 'development') {
      const contextObj = typeof context === 'object' && context !== null ? context : {};
      console.log('[EXTERNAL-REPORT] Would report:', {
        type: error.type,
        message: error.message,
        context: { ...error.context, ...contextObj },
        timestamp: new Date().toISOString(),
      });
    }
  }

  /**
   * Add error to buffer for potential batch reporting
   */
  private addToBuffer(error: ApiError, context: unknown): void {
    this.errorBuffer.push({ error, context });
    
    // Keep buffer size in check
    if (this.errorBuffer.length > this.MAX_BUFFER_SIZE) {
      this.errorBuffer.shift();
    }
  }

  /**
   * Get buffered errors
   */
  getBufferedErrors(): Array<{ error: ApiError; context: unknown }> {
    return [...this.errorBuffer];
  }

  /**
   * Clear error buffer
   */
  clearBuffer(): void {
    this.errorBuffer = [];
  }

  /**
   * Batch report buffered errors
   */
  flush(): void {
    if (this.errorBuffer.length === 0) return;

    if (this.options.logToConsole) {
      console.groupCollapsed(`[API-REPORT] Flushing ${this.errorBuffer.length} buffered errors`);
      this.errorBuffer.forEach(({ error, context }) => {
        console.log(`${error.type}: ${error.message}`, { error, context });
      });
      console.groupEnd();
    }

    // In production, this would batch send to external service
    if (this.options.reportToExternal) {
      const batch = this.errorBuffer.map(({ error, context }) => {
        const contextObj = typeof context === 'object' && context !== null ? context : {};
        return {
          type: error.type,
          message: error.message,
          context: { ...error.context, ...contextObj },
          timestamp: new Date().toISOString(),
        };
      });
      
      console.log('[EXTERNAL-REPORT] Batch:', batch);
    }

    this.clearBuffer();
  }

  /**
   * Update options dynamically
   */
  updateOptions(newOptions: Partial<EnhancedErrorReporterOptions>): void {
    this.options = { ...this.options, ...newOptions };
  }
}

/**
 * Global error reporter instance
 */
let globalReporter: EnhancedErrorReporter | null = null;

export function getGlobalErrorReporter(): EnhancedErrorReporter {
  if (!globalReporter) {
    // Import the console logger
    const { ConsoleLogger } = require('./logging/ConsoleLogger');
    globalReporter = new EnhancedErrorReporter(new ConsoleLogger(), {
      showUserNotifications: true,
      logToConsole: true,
      reportToExternal: process.env.NODE_ENV === 'production',
    });
  }
  return globalReporter;
}

/**
 * Helper function to report API errors easily
 */
export function reportApiError(
  error: ApiError,
  context?: unknown,
  reporter?: EnhancedErrorReporter
): void {
  const rep = reporter || getGlobalErrorReporter();
  rep.report(error, context);
}

/**
 * React hook for error reporting
 */
export function useErrorReporter() {
  const reporter = getGlobalErrorReporter();
  
  return {
    report: (error: Error, context?: unknown) => reporter.report(error, context),
    flush: () => reporter.flush(),
    getBuffered: () => reporter.getBufferedErrors(),
    updateOptions: (opts: Partial<EnhancedErrorReporterOptions>) => reporter.updateOptions(opts),
  };
}