/**
 * @fileoverview OrdoJS CLI - Monitoring and observability generator
 */

import { CLIError, ErrorType } from '../index.js';
import type { DeploymentConfig } from './adapter-interface.js';

export interface MonitoringConfig {
  structuredLogging: boolean;
  logLevel: 'debug' | 'info' | 'warn' | 'error';
  metricsCollection: boolean;
  distributedTracing: boolean;
  errorReporting: boolean;
  healthChecks: boolean;
  performanceMonitoring: boolean;
  alerting: boolean;
  integrations: {
    datadog?: boolean;
    newrelic?: boolean;
    sentry?: boolean;
    prometheus?: boolean;
    grafana?: boolean;
  };
}

export interface MonitoringOptions {
  structuredLogging?: boolean;
  logLevel?: 'debug' | 'info' | 'warn' | 'error';
  metricsCollection?: boolean;
  distributedTracing?: boolean;
  errorReporting?: boolean;
  healthChecks?: boolean;
  performanceMonitoring?: boolean;
  alerting?: boolean;
  integrations?: {
    datadog?: boolean;
    newrelic?: boolean;
    sentry?: boolean;
    prometheus?: boolean;
    grafana?: boolean;
  };
}

/**
 * Monitoring and observability generator for OrdoJS applications
 */
export class MonitoringGenerator {
  private defaultConfig: MonitoringConfig = {
    structuredLogging: true,
    logLevel: 'info',
    metricsCollection: true,
    distributedTracing: true,
    errorReporting: true,
    healthChecks: true,
    performanceMonitoring: true,
    alerting: true,
    integrations: {
      datadog: false,
      newrelic: false,
      sentry: false,
      prometheus: true,
      grafana: true
    }
  };

  /**
   * Generate monitoring configuration
   */
  generateMonitoringConfig(config: DeploymentConfig, options: MonitoringOptions = {}): {
    logger: string;
    metrics: string;
    tracing: string;
    healthCheck: string;
    errorHandler: string;
    dockerCompose: string;
    kubernetes: string;
  } {
    const monitoringConfig = this.mergeConfig(options);

    try {
      return {
        logger: this.generateLoggerConfig(monitoringConfig),
        metrics: this.generateMetricsConfig(monitoringConfig),
        tracing: this.generateTracingConfig(monitoringConfig),
        healthCheck: this.generateHealthCheckConfig(monitoringConfig),
        errorHandler: this.generateErrorHandlerConfig(monitoringConfig),
        dockerCompose: this.generateDockerComposeMonitoring(monitoringConfig),
        kubernetes: this.generateKubernetesMonitoring(monitoringConfig)
      };
    } catch (error) {
      throw new CLIError(
        `Failed to generate monitoring configuration: ${error instanceof Error ? error.message : String(error)}`,
        ErrorType.DEPLOYMENT,
        'MONITORING-001'
      );
    }
  }

  /**
   * Generate structured logging configuration
   */
  generateLoggerConfig(config: MonitoringConfig): string {
    return `import winston from 'winston';
import { format } from 'winston';

// Configure structured logging
const logger = winston.createLogger({
  level: '${config.logLevel}',
  format: format.combine(
    format.timestamp(),
    format.errors({ stack: true }),
    format.json()
  ),
  defaultMeta: { service: 'ordojs-app' },
  transports: [
    new winston.transports.Console({
      format: format.combine(
        format.colorize(),
        format.simple()
      )
    }),
    new winston.transports.File({
      filename: 'logs/error.log',
      level: 'error'
    }),
    new winston.transports.File({
      filename: 'logs/combined.log'
    })
  ]
});

// Add request logging middleware
export const requestLogger = (req: any, res: any, next: any) => {
  const start = Date.now();

  res.on('finish', () => {
    const duration = Date.now() - start;
    logger.info('HTTP Request', {
      method: req.method,
      url: req.url,
      statusCode: res.statusCode,
      duration,
      userAgent: req.get('User-Agent'),
      ip: req.ip
    });
  });

  next();
};

export default logger;
`;
  }

  /**
   * Generate metrics collection configuration
   */
  generateMetricsConfig(config: MonitoringConfig): string {
    return `import prometheus from 'prom-client';
import { register, collectDefaultMetrics } from 'prom-client';

// Initialize Prometheus metrics
collectDefaultMetrics({ register });

// Custom metrics
export const httpRequestDuration = new prometheus.Histogram({
  name: 'http_request_duration_seconds',
  help: 'Duration of HTTP requests in seconds',
  labelNames: ['method', 'route', 'status_code'],
  buckets: [0.1, 0.5, 1, 2, 5]
});

export const httpRequestTotal = new prometheus.Counter({
  name: 'http_requests_total',
  help: 'Total number of HTTP requests',
  labelNames: ['method', 'route', 'status_code']
});

export const activeConnections = new prometheus.Gauge({
  name: 'active_connections',
  help: 'Number of active connections'
});

export const memoryUsage = new prometheus.Gauge({
  name: 'memory_usage_bytes',
  help: 'Memory usage in bytes',
  labelNames: ['type']
});

// Metrics middleware
export const metricsMiddleware = (req: any, res: any, next: any) => {
  const start = Date.now();

  res.on('finish', () => {
    const duration = (Date.now() - start) / 1000;
    const labels = {
      method: req.method,
      route: req.route?.path || req.path,
      status_code: res.statusCode
    };

    httpRequestDuration.observe(labels, duration);
    httpRequestTotal.inc(labels);
  });

  next();
};

// Metrics endpoint
export const metricsEndpoint = async (req: any, res: any) => {
  try {
    res.set('Content-Type', register.contentType);
    res.end(await register.metrics());
  } catch (error) {
    res.status(500).end(error);
  }
};

// Memory usage monitoring
setInterval(() => {
  const memUsage = process.memoryUsage();
  memoryUsage.set({ type: 'rss' }, memUsage.rss);
  memoryUsage.set({ type: 'heapTotal' }, memUsage.heapTotal);
  memoryUsage.set({ type: 'heapUsed' }, memUsage.heapUsed);
  memoryUsage.set({ type: 'external' }, memUsage.external);
}, 30000);
`;
  }

  /**
   * Generate distributed tracing configuration
   */
  generateTracingConfig(config: MonitoringConfig): string {
    return `import { trace, context, SpanStatusCode } from '@opentelemetry/api';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { JaegerExporter } from '@opentelemetry/exporter-jaeger';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';

// Initialize tracing
const provider = new NodeTracerProvider();
const exporter = new JaegerExporter({
  endpoint: process.env.JAEGER_ENDPOINT || 'http://localhost:14268/api/traces'
});

provider.addSpanProcessor(new BatchSpanProcessor(exporter));
provider.register();

registerInstrumentations({
  instrumentations: [getNodeAutoInstrumentations()]
});

const tracer = trace.getTracer('ordojs-app');

// Tracing middleware
export const tracingMiddleware = (req: any, res: any, next: any) => {
  const span = tracer.startSpan('HTTP Request', {
    attributes: {
      'http.method': req.method,
      'http.url': req.url,
      'http.user_agent': req.get('User-Agent'),
      'http.client_ip': req.ip
    }
  });

  const ctx = trace.setSpan(context.active(), span);
  context.with(ctx, () => {
    res.on('finish', () => {
      span.setAttributes({
        'http.status_code': res.statusCode,
        'http.status_text': res.statusMessage
      });

      if (res.statusCode >= 400) {
        span.setStatus({ code: SpanStatusCode.ERROR });
      } else {
        span.setStatus({ code: SpanStatusCode.OK });
      }

      span.end();
    });

    next();
  });
};

// Custom span creation
export const createSpan = (name: string, attributes?: Record<string, any>) => {
  return tracer.startSpan(name, { attributes });
};

export { tracer };
`;
  }

  /**
   * Generate health check configuration
   */
  generateHealthCheckConfig(config: MonitoringConfig): string {
    return `import { createHealthCheck } from './health-check.js';

// Health check configuration
const healthChecks = {
  database: async () => {
    try {
      // Add your database health check logic here
      return { status: 'healthy', details: 'Database connection OK' };
    } catch (error) {
      return { status: 'unhealthy', details: error.message };
    }
  },

  redis: async () => {
    try {
      // Add your Redis health check logic here
      return { status: 'healthy', details: 'Redis connection OK' };
    } catch (error) {
      return { status: 'unhealthy', details: error.message };
    }
  },

  external: async () => {
    try {
      // Add external service health check logic here
      return { status: 'healthy', details: 'External services OK' };
    } catch (error) {
      return { status: 'unhealthy', details: error.message };
    }
  }
};

export const healthCheckMiddleware = createHealthCheck(healthChecks);

// Health check endpoint
export const healthCheckEndpoint = async (req: any, res: any) => {
  const results = await healthCheckMiddleware(req, res);

  const isHealthy = Object.values(results).every(
    (result: any) => result.status === 'healthy'
  );

  res.status(isHealthy ? 200 : 503).json({
    status: isHealthy ? 'healthy' : 'unhealthy',
    timestamp: new Date().toISOString(),
    checks: results
  });
};
`;
  }

  /**
   * Generate error handler configuration
   */
  generateErrorHandlerConfig(config: MonitoringConfig): string {
    return `import logger from './logger.js';
import { tracer } from './tracing.js';

// Global error handler
export const errorHandler = (error: any, req: any, res: any, next: any) => {
  const span = tracer.startSpan('Error Handler');

  // Log error with structured logging
  logger.error('Application Error', {
    error: {
      message: error.message,
      stack: error.stack,
      name: error.name
    },
    request: {
      method: req.method,
      url: req.url,
      headers: req.headers,
      body: req.body,
      userAgent: req.get('User-Agent'),
      ip: req.ip
    },
    user: req.user?.id,
    timestamp: new Date().toISOString()
  });

  // Set span attributes
  span.setAttributes({
    'error.message': error.message,
    'error.type': error.name,
    'error.stack': error.stack
  });

  span.setStatus({ code: 2 }); // ERROR
  span.end();

  // Send error response
  const statusCode = error.statusCode || 500;
  const message = statusCode === 500 ? 'Internal Server Error' : error.message;

  res.status(statusCode).json({
    error: {
      message,
      statusCode,
      timestamp: new Date().toISOString()
    }
  });
};

// Async error wrapper
export const asyncHandler = (fn: Function) => {
  return (req: any, res: any, next: any) => {
    Promise.resolve(fn(req, res, next)).catch(next);
  };
};

// Unhandled rejection handler
process.on('unhandledRejection', (reason, promise) => {
  logger.error('Unhandled Rejection', {
    reason: reason instanceof Error ? reason.message : reason,
    stack: reason instanceof Error ? reason.stack : undefined,
    promise: promise.toString()
  });
});

// Uncaught exception handler
process.on('uncaughtException', (error) => {
  logger.error('Uncaught Exception', {
    error: {
      message: error.message,
      stack: error.stack,
      name: error.name
    }
  });

  // Graceful shutdown
  process.exit(1);
});
`;
  }

  /**
   * Generate Docker Compose monitoring services
   */
  generateDockerComposeMonitoring(config: MonitoringConfig): string {
    return `# Monitoring services for Docker Compose
version: '3.8'

services:
  # Prometheus for metrics collection
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
      - '--storage.tsdb.retention.time=200h'
      - '--web.enable-lifecycle'
    restart: unless-stopped

  # Grafana for visualization
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3001:3000"
    volumes:
      - grafana_data:/var/lib/grafana
      - ./monitoring/grafana/provisioning:/etc/grafana/provisioning
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_USERS_ALLOW_SIGN_UP=false
    restart: unless-stopped

  # Jaeger for distributed tracing
  jaeger:
    image: jaegertracing/all-in-one:latest
    container_name: jaeger
    ports:
      - "16686:16686"
      - "14268:14268"
    environment:
      - COLLECTOR_OTLP_ENABLED=true
    restart: unless-stopped

  # Redis for caching and session storage
  redis:
    image: redis:alpine
    container_name: redis
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    restart: unless-stopped

volumes:
  prometheus_data:
  grafana_data:
  redis_data:
`;
  }

  /**
   * Generate Kubernetes monitoring manifests
   */
  generateKubernetesMonitoring(config: MonitoringConfig): string {
    return `# Prometheus ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
data:
  prometheus.yml: |
    global:
      scrape_interval: 15s
    scrape_configs:
      - job_name: 'ordojs-app'
        static_configs:
          - targets: ['ordojs-app-service:3000']
        metrics_path: '/metrics'
        scrape_interval: 5s

---
# Prometheus Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus
spec:
  replicas: 1
  selector:
    matchLabels:
      app: prometheus
  template:
    metadata:
      labels:
        app: prometheus
    spec:
      containers:
      - name: prometheus
        image: prom/prometheus:latest
        ports:
        - containerPort: 9090
        volumeMounts:
        - name: prometheus-config
          mountPath: /etc/prometheus
        - name: prometheus-storage
          mountPath: /prometheus
        command:
        - /bin/prometheus
        - --config.file=/etc/prometheus/prometheus.yml
        - --storage.tsdb.path=/prometheus
        - --web.console.libraries=/etc/prometheus/console_libraries
        - --web.console.templates=/etc/prometheus/consoles
        - --storage.tsdb.retention.time=200h
        - --web.enable-lifecycle
      volumes:
      - name: prometheus-config
        configMap:
          name: prometheus-config
      - name: prometheus-storage
        persistentVolumeClaim:
          claimName: prometheus-pvc

---
# Prometheus Service
apiVersion: v1
kind: Service
metadata:
  name: prometheus-service
spec:
  selector:
    app: prometheus
  ports:
  - port: 9090
    targetPort: 9090
  type: ClusterIP

---
# Grafana Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: grafana
spec:
  replicas: 1
  selector:
    matchLabels:
      app: grafana
  template:
    metadata:
      labels:
        app: grafana
    spec:
      containers:
      - name: grafana
        image: grafana/grafana:latest
        ports:
        - containerPort: 3000
        env:
        - name: GF_SECURITY_ADMIN_PASSWORD
          value: "admin"
        - name: GF_USERS_ALLOW_SIGN_UP
          value: "false"
        volumeMounts:
        - name: grafana-storage
          mountPath: /var/lib/grafana
      volumes:
      - name: grafana-storage
        persistentVolumeClaim:
          claimName: grafana-pvc

---
# Grafana Service
apiVersion: v1
kind: Service
metadata:
  name: grafana-service
spec:
  selector:
    app: grafana
  ports:
  - port: 3000
    targetPort: 3000
  type: ClusterIP

---
# Jaeger Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jaeger
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jaeger
  template:
    metadata:
      labels:
        app: jaeger
    spec:
      containers:
      - name: jaeger
        image: jaegertracing/all-in-one:latest
        ports:
        - containerPort: 16686
        - containerPort: 14268
        env:
        - name: COLLECTOR_OTLP_ENABLED
          value: "true"

---
# Jaeger Service
apiVersion: v1
kind: Service
metadata:
  name: jaeger-service
spec:
  selector:
    app: jaeger
  ports:
  - name: ui
    port: 16686
    targetPort: 16686
  - name: collector
    port: 14268
    targetPort: 14268
  type: ClusterIP

---
# Persistent Volume Claims
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: prometheus-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: grafana-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
`;
  }

  private mergeConfig(options: MonitoringOptions): MonitoringConfig {
    return {
      ...this.defaultConfig,
      ...options,
      integrations: {
        ...this.defaultConfig.integrations,
        ...options.integrations
      }
    };
  }
}
