/**
 * @fileoverview Tests for the Docker generator
 */

import { beforeEach, describe, expect, it } from 'vitest';
import type { DeploymentConfig } from './adapter-interface.js';
import { DockerGenerator } from './docker-generator.js';

describe('DockerGenerator', () => {
  let dockerGenerator: DockerGenerator;
  let mockDeploymentConfig: DeploymentConfig;

  beforeEach(() => {
    dockerGenerator = new DockerGenerator();
    mockDeploymentConfig = {
      outputDir: 'dist',
      isStatic: false,
      includeServerFunctions: true,
      env: {
        NODE_ENV: 'production',
        PORT: '3000'
      }
    };
  });

  describe('generateDockerfile', () => {
    it('should generate multi-stage Dockerfile by default', () => {
      const dockerfile = dockerGenerator.generateDockerfile(mockDeploymentConfig);

      expect(dockerfile).toContain('FROM node:18-alpine AS base');
      expect(dockerfile).toContain('FROM base AS builder');
      expect(dockerfile).toContain('FROM node:18-alpine AS production');
      expect(dockerfile).toContain('RUN pnpm install --frozen-lockfile');
      expect(dockerfile).toContain('RUN pnpm run build');
      expect(dockerfile).toContain('EXPOSE 3000');
      expect(dockerfile).toContain('CMD ["node", "dist/server.js"]');
    });

    it('should generate simple Dockerfile when multiStage is false', () => {
      const dockerfile = dockerGenerator.generateDockerfile(mockDeploymentConfig, {
        multiStage: false
      });

      expect(dockerfile).toContain('FROM node:18-alpine');
      expect(dockerfile).not.toContain('AS base');
      expect(dockerfile).not.toContain('AS builder');
      expect(dockerfile).not.toContain('AS production');
      expect(dockerfile).toContain('RUN pnpm install --frozen-lockfile');
      expect(dockerfile).toContain('RUN pnpm run build');
    });

    it('should include environment variables', () => {
      const dockerfile = dockerGenerator.generateDockerfile(mockDeploymentConfig, {
        environment: {
          NODE_ENV: 'production',
          API_KEY: 'secret'
        }
      });

      expect(dockerfile).toContain('ENV NODE_ENV=production');
      expect(dockerfile).toContain('ENV API_KEY=secret');
    });

    it('should include health check when enabled', () => {
      const dockerfile = dockerGenerator.generateDockerfile(mockDeploymentConfig, {
        healthCheck: true,
        port: 3000
      });

      expect(dockerfile).toContain('HEALTHCHECK');
      expect(dockerfile).toContain('curl -f http://localhost:3000/health');
    });

    it('should not include health check when disabled', () => {
      const dockerfile = dockerGenerator.generateDockerfile(mockDeploymentConfig, {
        healthCheck: false
      });

      expect(dockerfile).not.toContain('HEALTHCHECK');
    });

    it('should use custom Node.js version', () => {
      const dockerfile = dockerGenerator.generateDockerfile(mockDeploymentConfig, {
        nodeVersion: '20'
      });

      expect(dockerfile).toContain('FROM node:20-alpine');
    });

    it('should use custom port', () => {
      const dockerfile = dockerGenerator.generateDockerfile(mockDeploymentConfig, {
        port: 8080
      });

      expect(dockerfile).toContain('EXPOSE 8080');
    });
  });

  describe('generateDockerCompose', () => {
    it('should generate docker-compose.yml with basic configuration', () => {
      const dockerCompose = dockerGenerator.generateDockerCompose(mockDeploymentConfig);

      expect(dockerCompose).toContain('version: \'3.8\'');
      expect(dockerCompose).toContain('ordojs-app:');
      expect(dockerCompose).toContain('build:');
      expect(dockerCompose).toContain('context: .');
      expect(dockerCompose).toContain('dockerfile: Dockerfile');
      expect(dockerCompose).toContain('ports:');
      expect(dockerCompose).toContain('- "3000:3000"');
      expect(dockerCompose).toContain('restart: unless-stopped');
    });

    it('should include environment variables', () => {
      const dockerCompose = dockerGenerator.generateDockerCompose(mockDeploymentConfig, {
        environment: {
          NODE_ENV: 'production',
          DATABASE_URL: 'postgres://localhost:5432/mydb'
        }
      });

      expect(dockerCompose).toContain('environment:');
      expect(dockerCompose).toContain('- NODE_ENV=production');
      expect(dockerCompose).toContain('- DATABASE_URL=postgres://localhost:5432/mydb');
    });

    it('should include volumes when specified', () => {
      const dockerCompose = dockerGenerator.generateDockerCompose(mockDeploymentConfig, {
        volumes: ['./logs:/app/logs', './data:/app/data']
      });

      expect(dockerCompose).toContain('volumes:');
      expect(dockerCompose).toContain('- ./logs:/app/logs');
      expect(dockerCompose).toContain('- ./data:/app/data');
    });

    it('should include health check configuration', () => {
      const dockerCompose = dockerGenerator.generateDockerCompose(mockDeploymentConfig, {
        port: 3000
      });

      expect(dockerCompose).toContain('healthcheck:');
      expect(dockerCompose).toContain('test: ["CMD", "curl", "-f", "http://localhost:3000/health"]');
      expect(dockerCompose).toContain('interval: 30s');
      expect(dockerCompose).toContain('timeout: 10s');
      expect(dockerCompose).toContain('retries: 3');
    });
  });

  describe('generateDockerignore', () => {
    it('should generate comprehensive .dockerignore file', () => {
      const dockerignore = dockerGenerator.generateDockerignore();

      expect(dockerignore).toContain('node_modules');
      expect(dockerignore).toContain('.git/');
      expect(dockerignore).toContain('.env');
      expect(dockerignore).toContain('dist/');
      expect(dockerignore).toContain('*.log');
      expect(dockerignore).toContain('coverage');
      expect(dockerignore).toContain('.vscode/');
      expect(dockerignore).toContain('*.test.js');
      expect(dockerignore).toContain('*.test.ts');
    });
  });

  describe('generateKubernetesManifests', () => {
    it('should generate Kubernetes deployment manifest', () => {
      const manifests = dockerGenerator.generateKubernetesManifests(mockDeploymentConfig);

      expect(manifests.deployment).toContain('apiVersion: apps/v1');
      expect(manifests.deployment).toContain('kind: Deployment');
      expect(manifests.deployment).toContain('name: ordojs-app');
      expect(manifests.deployment).toContain('replicas: 3');
      expect(manifests.deployment).toContain('containerPort: 3000');
      expect(manifests.deployment).toContain('livenessProbe:');
      expect(manifests.deployment).toContain('readinessProbe:');
    });

    it('should generate Kubernetes service manifest', () => {
      const manifests = dockerGenerator.generateKubernetesManifests(mockDeploymentConfig);

      expect(manifests.service).toContain('apiVersion: v1');
      expect(manifests.service).toContain('kind: Service');
      expect(manifests.service).toContain('name: ordojs-app-service');
      expect(manifests.service).toContain('type: ClusterIP');
      expect(manifests.service).toContain('port: 80');
      expect(manifests.service).toContain('targetPort: 3000');
    });

    it('should generate Kubernetes ingress manifest', () => {
      const manifests = dockerGenerator.generateKubernetesManifests(mockDeploymentConfig);

      expect(manifests.ingress).toContain('apiVersion: networking.k8s.io/v1');
      expect(manifests.ingress).toContain('kind: Ingress');
      expect(manifests.ingress).toContain('name: ordojs-app-ingress');
      expect(manifests.ingress).toContain('host: ordojs-app.local');
    });

    it('should generate ConfigMap for non-secret environment variables', () => {
      const manifests = dockerGenerator.generateKubernetesManifests(mockDeploymentConfig, {
        environment: {
          NODE_ENV: 'production',
          API_URL: 'https://api.example.com',
          DB_PASSWORD: 'secret'
        }
      });

      expect(manifests.configMap).toContain('apiVersion: v1');
      expect(manifests.configMap).toContain('kind: ConfigMap');
      expect(manifests.configMap).toContain('NODE_ENV: "production"');
      expect(manifests.configMap).toContain('API_URL: "https://api.example.com"');
      expect(manifests.configMap).not.toContain('DB_PASSWORD');
    });

    it('should generate Secret for sensitive environment variables', () => {
      const manifests = dockerGenerator.generateKubernetesManifests(mockDeploymentConfig, {
        environment: {
          NODE_ENV: 'production',
          DB_PASSWORD: 'secret',
          API_SECRET: 'very-secret'
        }
      });

      expect(manifests.secret).toContain('apiVersion: v1');
      expect(manifests.secret).toContain('kind: Secret');
      expect(manifests.secret).toContain('type: Opaque');
      expect(manifests.secret).toContain('DB_PASSWORD:');
      expect(manifests.secret).toContain('API_SECRET:');
      expect(manifests.secret).not.toContain('NODE_ENV');
    });

    it('should not generate Secret when no sensitive variables', () => {
      const manifests = dockerGenerator.generateKubernetesManifests(mockDeploymentConfig, {
        environment: {
          NODE_ENV: 'production',
          API_URL: 'https://api.example.com'
        }
      });

      expect(manifests.secret).toBe('');
    });
  });

  describe('error handling', () => {
    it('should throw CLIError when Dockerfile generation fails', () => {
      // Mock a scenario that would cause generation to fail
      const invalidConfig = {
        ...mockDeploymentConfig,
        outputDir: null as any
      };

      expect(() => {
        dockerGenerator.generateDockerfile(invalidConfig);
      }).toThrow();
    });
  });
});
