/**
 * @fileoverview OrdoJS CLI - Infrastructure command
 */

import { Command } from 'commander';
import { CICDGenerator } from '../utils/deployment/cicd-generator.js';
import { DockerGenerator } from '../utils/deployment/docker-generator.js';
import { MonitoringGenerator } from '../utils/deployment/monitoring-generator.js';
import { mkdir, writeFile } from '../utils/fs.js';
import { CLIError, ErrorType, logger } from '../utils/index.js';

/**
 * Register the infrastructure command
 */
export function registerInfrastructureCommand(program: Command): void {
  program
    .command('infrastructure')
    .description('Generate deployment infrastructure for OrdoJS applications')
    .option('-t, --type <type>', 'Infrastructure type (docker, cicd, monitoring, all)', 'all')
    .option('-p, --platform <platform>', 'CI/CD platform (github, gitlab, jenkins)', 'github')
    .option('-o, --output <dir>', 'Output directory for generated files', '.')
    .option('--node-version <version>', 'Node.js version', '18')
    .option('--port <port>', 'Application port', '3000')
    .option('--multi-stage', 'Use multi-stage Dockerfile', true)
    .option('--health-check', 'Enable health checks', true)
    .option('--auto-deploy', 'Enable automatic deployment', false)
    .option('--monitoring', 'Enable monitoring and observability', true)
    .option('--env <variables>', 'Environment variables in KEY=VALUE format', collectEnvVars, {})
    .action(async (options) => {
      try {
        await infrastructureCommand(options);
      } catch (error) {
        if (error instanceof CLIError) {
          logger.error(`${error.type.toUpperCase()} ERROR: ${error.message}`);
          if (error.code) {
            logger.error(`Error code: ${error.code}`);
          }
          if (error.suggestions && error.suggestions.length > 0) {
            logger.info('Suggestions:');
            error.suggestions.forEach(suggestion => {
              logger.info(`  - ${suggestion}`);
            });
          }
        } else {
          logger.error(`Infrastructure generation failed: ${error instanceof Error ? error.message : String(error)}`);
        }
        process.exit(1);
      }
    });
}

/**
 * Collect environment variables from command line
 */
function collectEnvVars(value: string, previous: Record<string, string>): Record<string, string> {
  const [key, val] = value.split('=');
  if (!key || !val) {
    throw new CLIError('Environment variables must be in KEY=VALUE format', ErrorType.VALIDATION, 'CLI-201', [
      'Example: --env NODE_ENV=production'
    ]);
  }
  return { ...previous, [key]: val };
}

/**
 * Infrastructure command implementation
 */
export async function infrastructureCommand(options: {
  type: string;
  platform: string;
  output: string;
  nodeVersion: string;
  port: number;
  multiStage: boolean;
  healthCheck: boolean;
  autoDeploy: boolean;
  monitoring: boolean;
  env: Record<string, string>;
}): Promise<void> {
  logger.info(`Generating ${options.type} infrastructure...`);

  try {
    // Create output directory if it doesn't exist
    await mkdir(options.output, { recursive: true });

    // Initialize generators
    const dockerGenerator = new DockerGenerator();
    const cicdGenerator = new CICDGenerator();
    const monitoringGenerator = new MonitoringGenerator();

    // Mock deployment config for generation
    const deploymentConfig = {
      outputDir: 'dist',
      isStatic: false,
      includeServerFunctions: true,
      env: options.env
    };

    const generatedFiles: string[] = [];

    // Generate Docker infrastructure
    if (options.type === 'docker' || options.type === 'all') {
      logger.info('Generating Docker infrastructure...');

      const dockerfile = dockerGenerator.generateDockerfile(deploymentConfig, {
        nodeVersion: options.nodeVersion,
        port: options.port,
        multiStage: options.multiStage,
        healthCheck: options.healthCheck,
        environment: options.env
      });

      const dockerCompose = dockerGenerator.generateDockerCompose(deploymentConfig, {
        nodeVersion: options.nodeVersion,
        port: options.port,
        environment: options.env
      });

      const dockerignore = dockerGenerator.generateDockerignore();

      const k8sManifests = dockerGenerator.generateKubernetesManifests(deploymentConfig, {
        nodeVersion: options.nodeVersion,
        port: options.port,
        environment: options.env
      });

      // Write Docker files
      await writeFile(`${options.output}/Dockerfile`, dockerfile);
      await writeFile(`${options.output}/docker-compose.yml`, dockerCompose);
      await writeFile(`${options.output}/.dockerignore`, dockerignore);

      // Write Kubernetes manifests
      await mkdir(`${options.output}/k8s`, { recursive: true });
      await writeFile(`${options.output}/k8s/deployment.yaml`, k8sManifests.deployment);
      await writeFile(`${options.output}/k8s/service.yaml`, k8sManifests.service);
      if (k8sManifests.ingress) {
        await writeFile(`${options.output}/k8s/ingress.yaml`, k8sManifests.ingress);
      }
      if (k8sManifests.configMap) {
        await writeFile(`${options.output}/k8s/configmap.yaml`, k8sManifests.configMap);
      }
      if (k8sManifests.secret) {
        await writeFile(`${options.output}/k8s/secret.yaml`, k8sManifests.secret);
      }

      generatedFiles.push(
        'Dockerfile',
        'docker-compose.yml',
        '.dockerignore',
        'k8s/deployment.yaml',
        'k8s/service.yaml'
      );

      if (k8sManifests.ingress) generatedFiles.push('k8s/ingress.yaml');
      if (k8sManifests.configMap) generatedFiles.push('k8s/configmap.yaml');
      if (k8sManifests.secret) generatedFiles.push('k8s/secret.yaml');
    }

    // Generate CI/CD infrastructure
    if (options.type === 'cicd' || options.type === 'all') {
      logger.info(`Generating ${options.platform} CI/CD pipeline...`);

      const pipeline = cicdGenerator.generatePipeline(deploymentConfig, {
        platform: options.platform as 'github' | 'gitlab' | 'jenkins',
        nodeVersion: options.nodeVersion,
        autoDeploy: options.autoDeploy,
        environments: ['production', 'staging']
      });

      // Write CI/CD files
      switch (options.platform) {
        case 'github':
          await mkdir(`${options.output}/.github/workflows`, { recursive: true });
          await writeFile(`${options.output}/.github/workflows/ci-cd.yml`, pipeline);
          generatedFiles.push('.github/workflows/ci-cd.yml');
          break;
        case 'gitlab':
          await writeFile(`${options.output}/.gitlab-ci.yml`, pipeline);
          generatedFiles.push('.gitlab-ci.yml');
          break;
        case 'jenkins':
          await writeFile(`${options.output}/Jenkinsfile`, pipeline);
          generatedFiles.push('Jenkinsfile');
          break;
        default:
          throw new CLIError(`Unsupported CI/CD platform: ${options.platform}`, ErrorType.VALIDATION, 'CLI-202', [
            'Supported platforms: github, gitlab, jenkins'
          ]);
      }
    }

    // Generate monitoring infrastructure
    if (options.type === 'monitoring' || options.type === 'all') {
      if (options.monitoring) {
        logger.info('Generating monitoring infrastructure...');

        const monitoringConfig = monitoringGenerator.generateMonitoringConfig(deploymentConfig, {
          logLevel: 'info',
          structuredLogging: true,
          metricsCollection: true,
          distributedTracing: true,
          errorReporting: true,
          healthChecks: true,
          performanceMonitoring: true,
          alerting: true
        });

        // Create monitoring directory
        await mkdir(`${options.output}/monitoring`, { recursive: true });
        await mkdir(`${options.output}/src/monitoring`, { recursive: true });

        // Write monitoring configuration files
        await writeFile(`${options.output}/src/monitoring/logger.js`, monitoringConfig.logger);
        await writeFile(`${options.output}/src/monitoring/metrics.js`, monitoringConfig.metrics);
        await writeFile(`${options.output}/src/monitoring/tracing.js`, monitoringConfig.tracing);
        await writeFile(`${options.output}/src/monitoring/health-check.js`, monitoringConfig.healthCheck);
        await writeFile(`${options.output}/src/monitoring/error-handler.js`, monitoringConfig.errorHandler);

        // Write Docker Compose monitoring services
        await writeFile(`${options.output}/docker-compose.monitoring.yml`, monitoringConfig.dockerCompose);

        // Write Kubernetes monitoring manifests
        await mkdir(`${options.output}/k8s/monitoring`, { recursive: true });
        await writeFile(`${options.output}/k8s/monitoring/monitoring.yml`, monitoringConfig.kubernetes);

        // Create Prometheus configuration
        const prometheusConfig = `global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'ordojs-app'
    static_configs:
      - targets: ['ordojs-app:3000']
    metrics_path: '/metrics'
    scrape_interval: 5s

  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']
`;

        await writeFile(`${options.output}/monitoring/prometheus.yml`, prometheusConfig);

        generatedFiles.push(
          'src/monitoring/logger.js',
          'src/monitoring/metrics.js',
          'src/monitoring/tracing.js',
          'src/monitoring/health-check.js',
          'src/monitoring/error-handler.js',
          'docker-compose.monitoring.yml',
          'k8s/monitoring/monitoring.yml',
          'monitoring/prometheus.yml'
        );
      }
    }

    // Generate package.json scripts
    if (options.type === 'all') {
      logger.info('Generating package.json scripts...');

      const packageJsonScripts = {
        "docker:build": "docker build -t ordojs-app .",
        "docker:run": "docker run -p 3000:3000 ordojs-app",
        "docker:compose": "docker-compose up -d",
        "docker:compose:down": "docker-compose down",
        "k8s:apply": "kubectl apply -f k8s/",
        "k8s:delete": "kubectl delete -f k8s/",
        "monitoring:start": "docker-compose -f docker-compose.monitoring.yml up -d",
        "monitoring:stop": "docker-compose -f docker-compose.monitoring.yml down"
      };

      const packageJsonPath = `${options.output}/package.json`;
      try {
        const existingPackageJson = await import('fs').then(fs => fs.readFileSync(packageJsonPath, 'utf8'));
        const packageJson = JSON.parse(existingPackageJson);
        packageJson.scripts = { ...packageJson.scripts, ...packageJsonScripts };
        await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
      } catch (error) {
        logger.warn('Could not update package.json scripts - file may not exist');
      }
    }

    // Display generated files
    logger.success('\nInfrastructure generation completed successfully!');
    logger.info('\nGenerated files:');
    generatedFiles.forEach(file => {
      logger.info(`  - ${file}`);
    });

    // Display next steps
    logger.info('\nNext steps:');
    if (options.type === 'docker' || options.type === 'all') {
      logger.info('  Docker:');
      logger.info('    - docker build -t ordojs-app .');
      logger.info('    - docker run -p 3000:3000 ordojs-app');
      logger.info('    - docker-compose up -d');
    }

    if (options.type === 'cicd' || options.type === 'all') {
      logger.info(`  CI/CD (${options.platform}):`);
      logger.info('    - Commit and push your changes');
      logger.info('    - Check the CI/CD pipeline in your platform');
    }

    if (options.type === 'monitoring' || options.type === 'all') {
      logger.info('  Monitoring:');
      logger.info('    - docker-compose -f docker-compose.monitoring.yml up -d');
      logger.info('    - Access Grafana at http://localhost:3001 (admin/admin)');
      logger.info('    - Access Jaeger at http://localhost:16686');
      logger.info('    - Access Prometheus at http://localhost:9090');
    }

  } catch (error) {
    if (error instanceof CLIError) {
      throw error;
    } else {
      throw new CLIError(
        `Infrastructure generation failed: ${error instanceof Error ? error.message : String(error)}`,
        ErrorType.DEPLOYMENT,
        'CLI-203'
      );
    }
  }
}
