/**
 * @fileoverview Tests for the infrastructure command
 */

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { CLIError } from '../utils/index.js';
import { infrastructureCommand } from './infrastructure.js';

// Mock the generators
vi.mock('../utils/deployment/docker-generator.js', () => ({
  DockerGenerator: vi.fn().mockImplementation(() => ({
    generateDockerfile: vi.fn().mockReturnValue('FROM node:18-alpine'),
    generateDockerCompose: vi.fn().mockReturnValue('version: \'3.8\''),
    generateDockerignore: vi.fn().mockReturnValue('node_modules'),
    generateKubernetesManifests: vi.fn().mockReturnValue({
      deployment: 'apiVersion: apps/v1',
      service: 'apiVersion: v1',
      ingress: 'apiVersion: networking.k8s.io/v1',
      configMap: 'apiVersion: v1',
      secret: ''
    })
  }))
}));

vi.mock('../utils/deployment/cicd-generator.js', () => ({
  CICDGenerator: vi.fn().mockImplementation(() => ({
    generatePipeline: vi.fn().mockReturnValue('name: OrdoJS CI/CD Pipeline')
  }))
}));

vi.mock('../utils/deployment/monitoring-generator.js', () => ({
  MonitoringGenerator: vi.fn().mockImplementation(() => ({
    generateMonitoringConfig: vi.fn().mockReturnValue({
      logger: 'import winston',
      metrics: 'import prometheus',
      tracing: 'import opentelemetry',
      healthCheck: 'import healthCheck',
      errorHandler: 'import errorHandler',
      dockerCompose: 'version: \'3.8\'',
      kubernetes: 'apiVersion: v1'
    })
  }))
}));

// Mock file system operations
vi.mock('../utils/fs.js', () => ({
  writeFile: vi.fn().mockResolvedValue(undefined),
  mkdir: vi.fn().mockResolvedValue(undefined)
}));

describe('infrastructureCommand', () => {
  const mockOptions = {
    type: 'all',
    platform: 'github',
    output: '.',
    nodeVersion: '18',
    port: 3000,
    multiStage: true,
    healthCheck: true,
    autoDeploy: false,
    monitoring: true,
    env: {
      NODE_ENV: 'production',
      PORT: '3000'
    }
  };

  beforeEach(() => {
    vi.clearAllMocks();
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  describe('Docker infrastructure generation', () => {
    it('should generate Docker infrastructure when type is docker', async () => {
      const options = { ...mockOptions, type: 'docker' };

      await infrastructureCommand(options);

      const { writeFile } = await import('../utils/fs.js');
      expect(writeFile).toHaveBeenCalledWith('./Dockerfile', 'FROM node:18-alpine');
      expect(writeFile).toHaveBeenCalledWith('./docker-compose.yml', 'version: \'3.8\'');
      expect(writeFile).toHaveBeenCalledWith('./.dockerignore', 'node_modules');
    });

    it('should generate Kubernetes manifests', async () => {
      const options = { ...mockOptions, type: 'docker' };

      await infrastructureCommand(options);

      const { writeFile, mkdir } = await import('../utils/fs.js');
      expect(mkdir).toHaveBeenCalledWith('./k8s', { recursive: true });
      expect(writeFile).toHaveBeenCalledWith('./k8s/deployment.yaml', 'apiVersion: apps/v1');
      expect(writeFile).toHaveBeenCalledWith('./k8s/service.yaml', 'apiVersion: v1');
      expect(writeFile).toHaveBeenCalledWith('./k8s/ingress.yaml', 'apiVersion: networking.k8s.io/v1');
      expect(writeFile).toHaveBeenCalledWith('./k8s/configmap.yaml', 'apiVersion: v1');
    });

    it('should use custom Node.js version', async () => {
      const options = { ...mockOptions, type: 'docker', nodeVersion: '20' };

      await infrastructureCommand(options);

      const { DockerGenerator } = await import('../utils/deployment/docker-generator.js');
      const mockGenerator = DockerGenerator.mock.instances[0];
      expect(mockGenerator.generateDockerfile).toHaveBeenCalledWith(
        expect.any(Object),
        expect.objectContaining({ nodeVersion: '20' })
      );
    });

    it('should use custom port', async () => {
      const options = { ...mockOptions, type: 'docker', port: 8080 };

      await infrastructureCommand(options);

      const { DockerGenerator } = await import('../utils/deployment/docker-generator.js');
      const mockGenerator = DockerGenerator.mock.instances[0];
      expect(mockGenerator.generateDockerfile).toHaveBeenCalledWith(
        expect.any(Object),
        expect.objectContaining({ port: 8080 })
      );
    });
  });

  describe('CI/CD infrastructure generation', () => {
    it('should generate GitHub Actions when platform is github', async () => {
      const options = { ...mockOptions, type: 'cicd', platform: 'github' };

      await infrastructureCommand(options);

      const { writeFile, mkdir } = await import('../utils/fs.js');
      expect(mkdir).toHaveBeenCalledWith('./.github/workflows', { recursive: true });
      expect(writeFile).toHaveBeenCalledWith('./.github/workflows/ci-cd.yml', 'name: OrdoJS CI/CD Pipeline');
    });

    it('should generate GitLab CI when platform is gitlab', async () => {
      const options = { ...mockOptions, type: 'cicd', platform: 'gitlab' };

      await infrastructureCommand(options);

      const { writeFile } = await import('../utils/fs.js');
      expect(writeFile).toHaveBeenCalledWith('./.gitlab-ci.yml', 'name: OrdoJS CI/CD Pipeline');
    });

    it('should generate Jenkins pipeline when platform is jenkins', async () => {
      const options = { ...mockOptions, type: 'cicd', platform: 'jenkins' };

      await infrastructureCommand(options);

      const { writeFile } = await import('../utils/fs.js');
      expect(writeFile).toHaveBeenCalledWith('./Jenkinsfile', 'name: OrdoJS CI/CD Pipeline');
    });

    it('should throw error for unsupported platform', async () => {
      const options = { ...mockOptions, type: 'cicd', platform: 'unsupported' };

      await expect(infrastructureCommand(options)).rejects.toThrow(CLIError);
    });
  });

  describe('Monitoring infrastructure generation', () => {
    it('should generate monitoring infrastructure when monitoring is enabled', async () => {
      const options = { ...mockOptions, type: 'monitoring', monitoring: true };

      await infrastructureCommand(options);

      const { writeFile, mkdir } = await import('../utils/fs.js');
      expect(mkdir).toHaveBeenCalledWith('./monitoring', { recursive: true });
      expect(mkdir).toHaveBeenCalledWith('./src/monitoring', { recursive: true });
      expect(writeFile).toHaveBeenCalledWith('./src/monitoring/logger.js', 'import winston');
      expect(writeFile).toHaveBeenCalledWith('./src/monitoring/metrics.js', 'import prometheus');
      expect(writeFile).toHaveBeenCalledWith('./src/monitoring/tracing.js', 'import opentelemetry');
      expect(writeFile).toHaveBeenCalledWith('./src/monitoring/health-check.js', 'import healthCheck');
      expect(writeFile).toHaveBeenCalledWith('./src/monitoring/error-handler.js', 'import errorHandler');
      expect(writeFile).toHaveBeenCalledWith('./docker-compose.monitoring.yml', 'version: \'3.8\'');
      expect(writeFile).toHaveBeenCalledWith('./k8s/monitoring/monitoring.yml', 'apiVersion: v1');
    });

    it('should not generate monitoring infrastructure when monitoring is disabled', async () => {
      const options = { ...mockOptions, type: 'monitoring', monitoring: false };

      await infrastructureCommand(options);

      const { writeFile } = await import('../utils/fs.js');
      // Should not call writeFile for monitoring files
      expect(writeFile).not.toHaveBeenCalledWith('./src/monitoring/logger.js', expect.any(String));
    });
  });

  describe('Complete infrastructure generation', () => {
    it('should generate all infrastructure components when type is all', async () => {
      const options = { ...mockOptions, type: 'all' };

      await infrastructureCommand(options);

      const { writeFile, mkdir } = await import('../utils/fs.js');

      // Check Docker files
      expect(writeFile).toHaveBeenCalledWith('./Dockerfile', expect.any(String));
      expect(writeFile).toHaveBeenCalledWith('./docker-compose.yml', expect.any(String));
      expect(writeFile).toHaveBeenCalledWith('./.dockerignore', expect.any(String));

      // Check Kubernetes files
      expect(mkdir).toHaveBeenCalledWith('./k8s', { recursive: true });
      expect(writeFile).toHaveBeenCalledWith('./k8s/deployment.yaml', expect.any(String));
      expect(writeFile).toHaveBeenCalledWith('./k8s/service.yaml', expect.any(String));

      // Check CI/CD files
      expect(mkdir).toHaveBeenCalledWith('./.github/workflows', { recursive: true });
      expect(writeFile).toHaveBeenCalledWith('./.github/workflows/ci-cd.yml', expect.any(String));

      // Check monitoring files
      expect(mkdir).toHaveBeenCalledWith('./monitoring', { recursive: true });
      expect(mkdir).toHaveBeenCalledWith('./src/monitoring', { recursive: true });
      expect(writeFile).toHaveBeenCalledWith('./src/monitoring/logger.js', expect.any(String));
      expect(writeFile).toHaveBeenCalledWith('./src/monitoring/metrics.js', expect.any(String));
      expect(writeFile).toHaveBeenCalledWith('./src/monitoring/tracing.js', expect.any(String));
      expect(writeFile).toHaveBeenCalledWith('./src/monitoring/health-check.js', expect.any(String));
      expect(writeFile).toHaveBeenCalledWith('./src/monitoring/error-handler.js', expect.any(String));
      expect(writeFile).toHaveBeenCalledWith('./docker-compose.monitoring.yml', expect.any(String));
      expect(writeFile).toHaveBeenCalledWith('./k8s/monitoring/monitoring.yml', expect.any(String));
    });
  });

  describe('Environment variables', () => {
    it('should pass environment variables to generators', async () => {
      const options = {
        ...mockOptions,
        type: 'docker',
        env: {
          NODE_ENV: 'production',
          API_KEY: 'secret',
          DATABASE_URL: 'postgres://localhost:5432/mydb'
        }
      };

      await infrastructureCommand(options);

      const { DockerGenerator } = await import('../utils/deployment/docker-generator.js');
      const mockGenerator = DockerGenerator.mock.instances[0];
      expect(mockGenerator.generateDockerfile).toHaveBeenCalledWith(
        expect.objectContaining({
          env: {
            NODE_ENV: 'production',
            API_KEY: 'secret',
            DATABASE_URL: 'postgres://localhost:5432/mydb'
          }
        }),
        expect.any(Object)
      );
    });
  });

  describe('Error handling', () => {
    it('should throw CLIError when file writing fails', async () => {
      const { writeFile } = await import('../utils/fs.js');
      vi.mocked(writeFile).mockRejectedValueOnce(new Error('Write failed'));

      await expect(infrastructureCommand(mockOptions)).rejects.toThrow(CLIError);
    });

    it('should throw CLIError when directory creation fails', async () => {
      const { mkdir } = await import('../utils/fs.js');
      vi.mocked(mkdir).mockRejectedValueOnce(new Error('Mkdir failed'));

      await expect(infrastructureCommand(mockOptions)).rejects.toThrow(CLIError);
    });
  });
});
