/**
 * Enhanced Error Handling Generator
 * Generates comprehensive error handling infrastructure
 *
 * Features:
 * - Centralized error handling middleware
 * - Custom error classes
 * - Error logging integration
 * - Error tracking (Sentry integration)
 * - Frontend error boundaries (React)
 * - Error formatting for API responses
 * - Error recovery strategies
 */

import { GeneratorContext, GeneratedFile, GenerationResult } from '../types';
import { ErrorHandling } from '../../types/workflow';
import * as path from 'path';

export class EnhancedErrorHandlingGenerator {
  readonly id = 'enhanced-error-handling';
  readonly name = 'Enhanced Error Handling Generator';
  readonly description = 'Generates comprehensive error handling infrastructure with middleware, tracking, and recovery';

  generate(context: GeneratorContext): GenerationResult {
    const { workflow, outputDirectory } = context;
    const files: GeneratedFile[] = [];

    if (!workflow.error_handling) {
      return { files };
    }

    const errorHandling = workflow.error_handling;

    // Generate error configuration
    files.push(this.generateErrorConfig(errorHandling, outputDirectory));

    // Generate custom error classes
    files.push(this.generateCustomErrors(errorHandling, outputDirectory));

    // Generate error middleware
    if (errorHandling.middleware !== false) {
      files.push(this.generateErrorMiddleware(errorHandling, outputDirectory));
    }

    // Generate error logger
    if (errorHandling.logging) {
      files.push(this.generateErrorLogger(errorHandling, outputDirectory));
    }

    // Generate error tracking integration
    if (errorHandling.tracking) {
      files.push(this.generateErrorTracking(errorHandling, outputDirectory));
    }

    // Generate frontend error boundaries
    if (errorHandling.error_boundaries?.enabled) {
      files.push(this.generateErrorBoundary(errorHandling, outputDirectory));
      files.push(this.generateErrorFallback(errorHandling, outputDirectory));
    }

    // Generate API error formatter
    files.push(this.generateErrorFormatter(errorHandling, outputDirectory));

    // Generate error recovery utilities
    if (errorHandling.recovery_strategies && errorHandling.recovery_strategies.length > 0) {
      files.push(this.generateErrorRecovery(errorHandling, outputDirectory));
    }

    return { files };
  }

  private generateErrorConfig(errorHandling: ErrorHandling, outputDirectory: string): GeneratedFile {
    const content = `/**
 * Error Handling Configuration
 * Generated by Bangstack
 */

export const errorConfig = {
  middleware: ${errorHandling.middleware !== false},
  logging: {
    provider: '${errorHandling.logging?.provider || 'winston'}',
    level: '${errorHandling.logging?.level || 'error'}',
  },
  tracking: {
    provider: '${errorHandling.tracking?.provider || 'sentry'}',
    frontend: ${errorHandling.tracking?.frontend !== false},
    backend: ${errorHandling.tracking?.backend !== false},
  },
  errorBoundaries: {
    enabled: ${errorHandling.error_boundaries?.enabled || false},
    fallbackUi: '${errorHandling.error_boundaries?.fallback_ui || 'default'}',
  },
  apiFormatting: {
    format: '${errorHandling.api_formatting?.format || 'json_api'}',
    includeStack: ${errorHandling.api_formatting?.include_stack !== false && 'process.env.NODE_ENV !== \'production\''},
  },
};

export default errorConfig;
`;

    return {
      path: path.join(outputDirectory, 'api/src/config/error-handling.ts'),
      content,
    };
  }

  private generateCustomErrors(errorHandling: ErrorHandling, outputDirectory: string): GeneratedFile {
    const customErrors = errorHandling.custom_errors || [];

    const defaultErrors = [
      { name: 'ValidationError', status_code: 400, default_message: 'Validation failed' },
      { name: 'AuthenticationError', status_code: 401, default_message: 'Authentication required' },
      { name: 'AuthorizationError', status_code: 403, default_message: 'Access forbidden' },
      { name: 'NotFoundError', status_code: 404, default_message: 'Resource not found' },
      { name: 'ConflictError', status_code: 409, default_message: 'Resource conflict' },
      { name: 'RateLimitError', status_code: 429, default_message: 'Rate limit exceeded' },
      { name: 'InternalServerError', status_code: 500, default_message: 'Internal server error' },
    ];

    const allErrors = [...defaultErrors, ...customErrors];

    const content = `/**
 * Custom Error Classes
 * Generated by Bangstack
 */

/**
 * Base Application Error
 */
export class AppError extends Error {
  public readonly statusCode: number;
  public readonly isOperational: boolean;
  public readonly context?: Record<string, any>;

  constructor(
    message: string,
    statusCode: number = 500,
    isOperational: boolean = true,
    context?: Record<string, any>
  ) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = isOperational;
    this.context = context;

    // Maintains proper stack trace for where our error was thrown
    Error.captureStackTrace(this, this.constructor);

    // Set the prototype explicitly
    Object.setPrototypeOf(this, AppError.prototype);
  }

  toJSON() {
    return {
      name: this.name,
      message: this.message,
      statusCode: this.statusCode,
      context: this.context,
    };
  }
}

${allErrors.map(error => `/**
 * ${error.name}
 * Status Code: ${error.status_code}
 */
export class ${error.name} extends ${(error as any).base_class || 'AppError'} {
  constructor(message: string = '${error.default_message}', context?: Record<string, any>) {
    super(message, ${error.status_code}, true, context);
    this.name = '${error.name}';
    Object.setPrototypeOf(this, ${error.name}.prototype);
  }
}`).join('\n\n')}

/**
 * Type guard to check if error is an AppError
 */
export function isAppError(error: unknown): error is AppError {
  return error instanceof AppError;
}

/**
 * Type guard to check if error is operational
 */
export function isOperationalError(error: unknown): boolean {
  if (isAppError(error)) {
    return error.isOperational;
  }
  return false;
}
`;

    return {
      path: path.join(outputDirectory, 'api/src/errors/custom-errors.ts'),
      content,
    };
  }

  private generateErrorMiddleware(errorHandling: ErrorHandling, outputDirectory: string): GeneratedFile {
    const content = `/**
 * Error Handling Middleware
 * Generated by Bangstack
 */

import { Request, Response, NextFunction } from 'express';
import { AppError, isAppError, isOperationalError } from '../errors/custom-errors';
import { errorLogger } from './error-logger';
import { errorTracker } from './error-tracker';
import { formatError } from './error-formatter';

/**
 * Error handling middleware
 * Must be registered after all routes
 */
export function errorHandler(
  err: Error,
  req: Request,
  res: Response,
  _next: NextFunction
): void {
  // Log the error
  errorLogger.error('Error occurred', {
    error: err,
    path: req.path,
    method: req.method,
    ip: req.ip,
    userId: (req as any).user?.id,
  });

  // Track the error (Sentry, etc.)
  if (!isOperationalError(err)) {
    errorTracker.captureException(err, {
      user: (req as any).user,
      request: {
        method: req.method,
        url: req.url,
        headers: req.headers,
        query: req.query,
        body: req.body,
      },
    });
  }

  // Format and send error response
  const formattedError = formatError(err);
  res.status(formattedError.status).json(formattedError.body);
}

/**
 * Async error wrapper
 * Wraps async route handlers to catch errors
 */
export function asyncHandler(
  fn: (req: Request, res: Response, next: NextFunction) => Promise<any>
) {
  return (req: Request, res: Response, next: NextFunction) => {
    Promise.resolve(fn(req, res, next)).catch(next);
  };
}

/**
 * 404 Not Found handler
 * Should be registered after all routes
 */
export function notFoundHandler(req: Request, _res: Response, next: NextFunction): void {
  const error = new AppError(\`Route not found: \${req.method} \${req.path}\`, 404);
  next(error);
}

/**
 * Unhandled rejection handler
 */
export function handleUnhandledRejection(): void {
  process.on('unhandledRejection', (reason: Error) => {
    errorLogger.error('Unhandled Rejection', { error: reason });
    errorTracker.captureException(reason);
  });
}

/**
 * Uncaught exception handler
 */
export function handleUncaughtException(): void {
  process.on('uncaughtException', (error: Error) => {
    errorLogger.error('Uncaught Exception', { error });
    errorTracker.captureException(error);

    // Give time for logs to be written, then exit
    setTimeout(() => {
      process.exit(1);
    }, 1000);
  });
}
`;

    return {
      path: path.join(outputDirectory, 'api/src/middleware/error-handler.ts'),
      content,
    };
  }

  private generateErrorLogger(errorHandling: ErrorHandling, outputDirectory: string): GeneratedFile {
    const provider = errorHandling.logging?.provider || 'winston';

    let content = '';

    if (provider === 'winston') {
      content = `/**
 * Error Logger
 * Generated by Bangstack
 */

import winston from 'winston';

const logLevel = process.env.LOG_LEVEL || '${errorHandling.logging?.level || 'error'}';

export const errorLogger = winston.createLogger({
  level: logLevel,
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  transports: [
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.colorize(),
        winston.format.simple()
      ),
    }),
    new winston.transports.File({
      filename: 'logs/error.log',
      level: 'error',
    }),
    new winston.transports.File({
      filename: 'logs/combined.log',
    }),
  ],
});

// If we're not in production, log to console with colorization
if (process.env.NODE_ENV !== 'production') {
  errorLogger.add(
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.colorize(),
        winston.format.simple()
      ),
    })
  );
}
`;
    } else if (provider === 'pino') {
      content = `/**
 * Error Logger
 * Generated by Bangstack
 */

import pino from 'pino';

export const errorLogger = pino({
  level: process.env.LOG_LEVEL || '${errorHandling.logging?.level || 'error'}',
  transport: process.env.NODE_ENV !== 'production' ? {
    target: 'pino-pretty',
    options: {
      colorize: true,
      translateTime: 'SYS:standard',
    },
  } : undefined,
});
`;
    }

    return {
      path: path.join(outputDirectory, 'api/src/middleware/error-logger.ts'),
      content,
    };
  }

  private generateErrorTracking(errorHandling: ErrorHandling, outputDirectory: string): GeneratedFile {
    const provider = errorHandling.tracking?.provider || 'sentry';

    let content = '';

    if (provider === 'sentry') {
      content = `/**
 * Error Tracking (Sentry)
 * Generated by Bangstack
 */

import * as Sentry from '@sentry/node';
import { ProfilingIntegration } from '@sentry/profiling-node';

export function initErrorTracking(): void {
  if (!process.env.SENTRY_DSN) {
    console.warn('SENTRY_DSN not configured, error tracking disabled');
    return;
  }

  Sentry.init({
    dsn: process.env.SENTRY_DSN,
    environment: process.env.NODE_ENV || 'development',
    integrations: [
      new ProfilingIntegration(),
    ],
    tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
    profilesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
  });
}

export const errorTracker = {
  captureException(error: Error, context?: Record<string, any>): void {
    if (context) {
      Sentry.setContext('custom', context);
    }
    Sentry.captureException(error);
  },

  captureMessage(message: string, level: Sentry.SeverityLevel = 'info'): void {
    Sentry.captureMessage(message, level);
  },

  setUser(user: { id: string; email?: string; username?: string }): void {
    Sentry.setUser(user);
  },

  addBreadcrumb(breadcrumb: Sentry.Breadcrumb): void {
    Sentry.addBreadcrumb(breadcrumb);
  },
};
`;
    }

    return {
      path: path.join(outputDirectory, 'api/src/middleware/error-tracker.ts'),
      content,
    };
  }

  private generateErrorBoundary(errorHandling: ErrorHandling, outputDirectory: string): GeneratedFile {
    const content = `/**
 * Error Boundary Component
 * Generated by Bangstack
 */

import React, { Component, ErrorInfo, ReactNode } from 'react';
import { ErrorFallback } from './ErrorFallback';

interface Props {
  children: ReactNode;
  fallback?: ReactNode;
  onError?: (error: Error, errorInfo: ErrorInfo) => void;
}

interface State {
  hasError: boolean;
  error?: Error;
}

export class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false };
  }

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

  componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
    // Log error to error tracking service
    console.error('Error caught by boundary:', error, errorInfo);

    // Report to Sentry or other tracking service
    if (window.Sentry) {
      window.Sentry.captureException(error, {
        contexts: {
          react: {
            componentStack: errorInfo.componentStack,
          },
        },
      });
    }

    // Call custom error handler if provided
    this.props.onError?.(error, errorInfo);
  }

  render(): ReactNode {
    if (this.state.hasError) {
      // Custom fallback UI
      if (this.props.fallback) {
        return this.props.fallback;
      }

      // Default fallback UI
      return <ErrorFallback error={this.state.error} />;
    }

    return this.props.children;
  }
}
`;

    return {
      path: path.join(outputDirectory, 'web/src/components/ErrorBoundary.tsx'),
      content,
    };
  }

  private generateErrorFallback(errorHandling: ErrorHandling, outputDirectory: string): GeneratedFile {
    const _fallbackType = errorHandling.error_boundaries?.fallback_ui || 'default';

    const content = `/**
 * Error Fallback UI
 * Generated by Bangstack
 */

import React from 'react';

interface Props {
  error?: Error;
  resetError?: () => void;
}

export function ErrorFallback({ error, resetError }: Props): JSX.Element {
  return (
    <div style={{ padding: '20px', textAlign: 'center' }}>
      <h1>Something went wrong</h1>
      <p>We apologize for the inconvenience. Please try again later.</p>

      {process.env.NODE_ENV !== 'production' && error && (
        <details style={{ marginTop: '20px', textAlign: 'left' }}>
          <summary>Error Details (Development Only)</summary>
          <pre style={{
            backgroundColor: '#f5f5f5',
            padding: '10px',
            borderRadius: '4px',
            overflow: 'auto',
          }}>
            {error.message}
            {error.stack}
          </pre>
        </details>
      )}

      {resetError && (
        <button
          onClick={resetError}
          style={{
            marginTop: '20px',
            padding: '10px 20px',
            backgroundColor: '#007bff',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer',
          }}
        >
          Try Again
        </button>
      )}
    </div>
  );
}
`;

    return {
      path: path.join(outputDirectory, 'web/src/components/ErrorFallback.tsx'),
      content,
    };
  }

  private generateErrorFormatter(errorHandling: ErrorHandling, outputDirectory: string): GeneratedFile {
    const format = errorHandling.api_formatting?.format || 'json_api';

    const content = `/**
 * Error Formatter
 * Generated by Bangstack
 */

import { AppError, isAppError } from '../errors/custom-errors';

export interface FormattedError {
  status: number;
  body: any;
}

/**
 * Format error for API response
 */
export function formatError(err: Error): FormattedError {
  const statusCode = isAppError(err) ? err.statusCode : 500;
  const message = err.message || 'Internal server error';
  const includeStack = ${errorHandling.api_formatting?.include_stack !== false} && process.env.NODE_ENV !== 'production';

  ${format === 'json_api' ? `
  // JSON:API format
  return {
    status: statusCode,
    body: {
      errors: [{
        status: String(statusCode),
        title: err.name,
        detail: message,
        ...(isAppError(err) && err.context ? { meta: err.context } : {}),
        ...(includeStack ? { stack: err.stack } : {}),
      }],
    },
  };
  ` : format === 'problem_json' ? `
  // Problem JSON (RFC 7807) format
  return {
    status: statusCode,
    body: {
      type: 'about:blank',
      title: err.name,
      status: statusCode,
      detail: message,
      ...(isAppError(err) && err.context ? err.context : {}),
      ...(includeStack ? { stack: err.stack } : {}),
    },
  };
  ` : `
  // Custom format
  return {
    status: statusCode,
    body: {
      error: {
        name: err.name,
        message: message,
        statusCode: statusCode,
        ...(isAppError(err) && err.context ? { context: err.context } : {}),
        ...(includeStack ? { stack: err.stack } : {}),
      },
    },
  };
  `}
}
`;

    return {
      path: path.join(outputDirectory, 'api/src/middleware/error-formatter.ts'),
      content,
    };
  }

  private generateErrorRecovery(errorHandling: ErrorHandling, outputDirectory: string): GeneratedFile {
    const content = `/**
 * Error Recovery Strategies
 * Generated by Bangstack
 */

export interface RetryOptions {
  maxAttempts: number;
  delay: number;
  backoff?: 'linear' | 'exponential';
}

/**
 * Retry a function with exponential backoff
 */
export async function retry<T>(
  fn: () => Promise<T>,
  options: RetryOptions = { maxAttempts: 3, delay: 1000 }
): Promise<T> {
  let lastError: Error | undefined;

  for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error as Error;

      if (attempt < options.maxAttempts) {
        const delay = options.backoff === 'exponential'
          ? options.delay * Math.pow(2, attempt - 1)
          : options.delay * attempt;

        await new Promise(resolve => setTimeout(resolve, delay));
      }
    }
  }

  throw lastError;
}

/**
 * Circuit breaker state
 */
class CircuitBreaker {
  private failures = 0;
  private lastFailureTime: number | null = null;
  private state: 'closed' | 'open' | 'half-open' = 'closed';

  constructor(
    private threshold: number = 5,
    private timeout: number = 60000, // 1 minute
    private resetTimeout: number = 30000 // 30 seconds
  ) {}

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.state === 'open') {
      if (Date.now() - this.lastFailureTime! > this.resetTimeout) {
        this.state = 'half-open';
      } else {
        throw new Error('Circuit breaker is open');
      }
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  private onSuccess(): void {
    this.failures = 0;
    this.state = 'closed';
  }

  private onFailure(): void {
    this.failures++;
    this.lastFailureTime = Date.now();

    if (this.failures >= this.threshold) {
      this.state = 'open';
    }
  }

  getState(): string {
    return this.state;
  }
}

/**
 * Create a circuit breaker
 */
export function createCircuitBreaker(
  threshold?: number,
  timeout?: number,
  resetTimeout?: number
): CircuitBreaker {
  return new CircuitBreaker(threshold, timeout, resetTimeout);
}

/**
 * Fallback wrapper
 */
export async function withFallback<T>(
  primary: () => Promise<T>,
  fallback: () => Promise<T>
): Promise<T> {
  try {
    return await primary();
  } catch (error) {
    console.warn('Primary function failed, using fallback', error);
    return await fallback();
  }
}
`;

    return {
      path: path.join(outputDirectory, 'api/src/utils/error-recovery.ts'),
      content,
    };
  }
}

export const enhancedErrorHandlingGenerator = new EnhancedErrorHandlingGenerator();
