"use strict";
/**
 * Docker Compose Generator
 * Generates Docker Compose configuration and Dockerfiles for the entire stack
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.DockerComposeGenerator = void 0;
const path = __importStar(require("path"));
class DockerComposeGenerator {
    id = 'docker-compose';
    name = 'Docker Compose Generator';
    description = 'Generates Docker Compose and Dockerfiles for stack orchestration';
    generate(context) {
        const { workflow, outputDirectory } = context;
        const files = [];
        // Generate Dockerfiles for each stack component
        for (const [componentName, component] of Object.entries(workflow.stack)) {
            const comp = component;
            if (comp.type === 'frontend' && comp.technology === 'react') {
                files.push(this.generateFrontendDockerfile(componentName, comp, outputDirectory));
                files.push(this.generateNginxConfig(componentName, outputDirectory));
            }
            else if (comp.type === 'backend' && comp.technology?.startsWith('typescript')) {
                files.push(this.generateBackendDockerfile(componentName, comp, outputDirectory));
            }
        }
        // Generate docker-compose.yml (includes infrastructure services from stages)
        files.push(this.generateDockerCompose(workflow, outputDirectory));
        // Generate .dockerignore files
        for (const [componentName, component] of Object.entries(workflow.stack)) {
            const comp = component;
            if (comp.type === 'frontend' || comp.type === 'backend') {
                files.push(this.generateDockerIgnore(componentName, outputDirectory));
            }
        }
        // Generate docker startup script
        files.push(this.generateStartupScript(outputDirectory));
        return { files };
    }
    generateFrontendDockerfile(componentName, component, outputDir) {
        const dockerfile = `# Frontend Dockerfile - ${componentName}
FROM node:20-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci

# Copy source code
COPY . .

# Build the application
RUN npm run build

# Production stage
FROM nginx:alpine

# Copy built assets from builder
COPY --from=builder /app/dist /usr/share/nginx/html

# Copy nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
`;
        return {
            path: path.join(outputDir, componentName, 'Dockerfile'),
            content: dockerfile,
            overwrite: false,
        };
    }
    generateBackendDockerfile(componentName, component, outputDir) {
        const dockerfile = `# Backend Dockerfile - ${componentName}
FROM node:20-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci

# Copy source code
COPY . .

# Build the application
RUN npm run build

# Production stage
FROM node:20-alpine

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install production dependencies only
RUN npm ci --only=production

# Copy built application
COPY --from=builder /app/dist ./dist

# Expose port
EXPOSE 3000

CMD ["node", "dist/index.js"]
`;
        return {
            path: path.join(outputDir, componentName, 'Dockerfile'),
            content: dockerfile,
            overwrite: false,
        };
    }
    generateNginxConfig(componentName, outputDir) {
        const config = `server {
    listen 80;
    server_name localhost;
    root /usr/share/nginx/html;
    index index.html;

    # Enable gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;

    # SPA routing - redirect all requests to index.html
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Cache static assets
    location ~* \\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
}
`;
        return {
            path: path.join(outputDir, componentName, 'nginx.conf'),
            content: config,
            overwrite: false,
        };
    }
    generateDockerCompose(workflow, outputDir) {
        const services = {};
        const volumes = [];
        // Analyze stack components
        const components = Object.entries(workflow.stack);
        // Generate services for each component
        for (const [componentName, component] of components) {
            const comp = component;
            if (comp.type === 'frontend') {
                services[componentName] = this.generateFrontendService(componentName, comp, workflow);
            }
            else if (comp.type === 'backend') {
                services[componentName] = this.generateBackendService(componentName, comp, workflow);
            }
            else if (comp.type === 'database') {
                const dbService = this.generateDatabaseService(componentName, comp, workflow);
                services[dbService.name] = dbService.config;
                if (dbService.volume) {
                    volumes.push(dbService.volume);
                }
            }
        }
        // Add infrastructure services from stages (local/development)
        const infrastructureServices = this.generateInfrastructureServices(workflow, volumes);
        Object.assign(services, infrastructureServices);
        // Build docker-compose structure
        const compose = {
            version: '3.8',
            services,
        };
        if (volumes.length > 0) {
            compose.volumes = {};
            for (const volume of volumes) {
                compose.volumes[volume] = null;
            }
        }
        // Convert to YAML format
        const yaml = this.toYAML(compose);
        return {
            path: path.join(outputDir, 'docker-compose.yml'),
            content: yaml,
            overwrite: false,
        };
    }
    generateFrontendService(componentName, component, workflow) {
        const service = {
            build: `./${componentName}`,
            ports: ['5173:80'],
            environment: [],
            depends_on: [],
        };
        // Add environment variables
        if (workflow.environment?.development?.[componentName]) {
            for (const [key, value] of Object.entries(workflow.environment.development[componentName])) {
                service.environment.push(`${key}=${value}`);
            }
        }
        // Add dependencies
        if (component.dependsOn) {
            service.depends_on = component.dependsOn;
        }
        return service;
    }
    generateBackendService(componentName, component, workflow) {
        const service = {
            build: `./${componentName}`,
            ports: ['3000:3000'],
            environment: [],
            depends_on: [],
        };
        // Add environment variables
        if (workflow.environment?.development?.[componentName]) {
            // Find database service name from dependencies
            const dbServiceName = component.dependsOn?.find((dep) => {
                const depComponent = workflow.stack[dep];
                return depComponent && depComponent.type === 'database';
            });
            for (const [key, value] of Object.entries(workflow.environment.development[componentName])) {
                let envValue = value;
                // Replace localhost with database service name in DATABASE_URL for Docker networking
                if (key === 'DATABASE_URL' && typeof envValue === 'string' && dbServiceName) {
                    envValue = envValue.replace(/localhost:(\d+)/, `${dbServiceName}:$1`);
                }
                service.environment.push(`${key}=${envValue}`);
            }
        }
        // Add dependencies
        if (component.dependsOn) {
            service.depends_on = component.dependsOn;
        }
        return service;
    }
    generateDatabaseService(componentName, component, workflow) {
        const technology = component.technology || 'postgresql';
        // Extract base database name (e.g., 'postgresql:15-alpine' -> 'postgresql')
        const baseDb = technology.split(':')[0].split('/')[0];
        let config = {};
        let volumeName;
        // Get environment variables from workflow
        const _envVars = workflow.environment?.development?.[componentName] || {};
        switch (baseDb) {
            case 'postgresql':
            case 'postgres': {
                volumeName = `${componentName}_data`;
                // Use the full technology string if it looks like a full image name, otherwise use default
                // Normalize 'postgresql' to 'postgres' for the image name
                const postgresImage = technology.includes(':')
                    ? technology.replace(/^postgresql:/, 'postgres:')
                    : 'postgres:15-alpine';
                config = {
                    image: postgresImage,
                    expose: [5432],
                    environment: [],
                    volumes: [`${volumeName}:/var/lib/postgresql/data`],
                    healthcheck: {
                        test: ['CMD-SHELL', 'pg_isready -U postgres'],
                        interval: '10s',
                        timeout: '5s',
                        retries: 5,
                    },
                };
                // Add environment variables from workflow
                if (workflow.environment?.development?.[componentName]) {
                    for (const [key, value] of Object.entries(workflow.environment.development[componentName])) {
                        config.environment.push(`${key}=${value}`);
                    }
                }
                else {
                    // Fallback defaults
                    config.environment.push('POSTGRES_DB=${POSTGRES_DB:-myapp}');
                    config.environment.push('POSTGRES_USER=${POSTGRES_USER:-postgres}');
                    config.environment.push('POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres}');
                }
                break;
            }
            case 'mysql':
                volumeName = `${componentName}_data`;
                config = {
                    image: 'mysql:8-oracle',
                    expose: [3306],
                    environment: [
                        'MYSQL_DATABASE=${MYSQL_DATABASE:-myapp}',
                        'MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}',
                    ],
                    volumes: [`${volumeName}:/var/lib/mysql`],
                    healthcheck: {
                        test: ['CMD', 'mysqladmin', 'ping', '-h', 'localhost'],
                        interval: '10s',
                        timeout: '5s',
                        retries: 5,
                    },
                };
                break;
            case 'mongodb':
                volumeName = `${componentName}_data`;
                config = {
                    image: 'mongo:7',
                    expose: [27017],
                    environment: [
                        'MONGO_INITDB_DATABASE=${MONGO_INITDB_DATABASE:-myapp}',
                        'MONGO_INITDB_ROOT_USERNAME=${MONGO_INITDB_ROOT_USERNAME:-root}',
                        'MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD:-root}',
                    ],
                    volumes: [`${volumeName}:/data/db`],
                };
                break;
            default:
                // Default to PostgreSQL
                volumeName = `${componentName}_data`;
                config = {
                    image: 'postgres:15-alpine',
                    expose: [5432],
                    environment: [
                        'POSTGRES_DB=${POSTGRES_DB:-myapp}',
                        'POSTGRES_USER=${POSTGRES_USER:-postgres}',
                        'POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres}',
                    ],
                    volumes: [`${volumeName}:/var/lib/postgresql/data`],
                };
        }
        return {
            name: componentName,
            config,
            volume: volumeName,
        };
    }
    generateDockerIgnore(componentName, outputDir) {
        const content = `node_modules
npm-debug.log
.git
.gitignore
.env
.env.local
.DS_Store
dist
coverage
.vscode
.idea
*.log
`;
        return {
            path: path.join(outputDir, componentName, '.dockerignore'),
            content,
            overwrite: false,
        };
    }
    generateInfrastructureServices(workflow, volumes) {
        const services = {};
        // Get infrastructure services from local or development stage
        const stages = workflow.stages;
        if (!stages) {
            return services;
        }
        const stage = stages.local || stages.development;
        if (!stage?.infrastructure?.services) {
            return services;
        }
        const infraServices = stage.infrastructure.services;
        // Redis
        if (infraServices.redis) {
            const volumeName = 'redis_data';
            volumes.push(volumeName);
            services.redis = {
                image: 'redis:7-alpine',
                ports: ['6379:6379'],
                volumes: [`${volumeName}:/data`],
                healthcheck: {
                    test: ['CMD', 'redis-cli', 'ping'],
                    interval: '10s',
                    timeout: '5s',
                    retries: 5,
                },
            };
        }
        // Queue (BullMQ with Redis)
        if (infraServices.queue) {
            services.queue = {
                image: 'redis:7-alpine',
                ports: ['6380:6379'],
                command: ['redis-server', '--port', '6379'],
                healthcheck: {
                    test: ['CMD', 'redis-cli', 'ping'],
                    interval: '10s',
                    timeout: '5s',
                    retries: 5,
                },
            };
        }
        // Storage (MinIO - S3-compatible)
        if (infraServices.storage) {
            const volumeName = 'storage_data';
            volumes.push(volumeName);
            services.storage = {
                image: 'minio/minio:latest',
                ports: ['9000:9000', '9001:9001'],
                environment: [
                    'MINIO_ROOT_USER=${MINIO_ROOT_USER:-minioadmin}',
                    'MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-minioadmin}',
                ],
                command: ['server', '/data', '--console-address', ':9001'],
                volumes: [`${volumeName}:/data`],
                healthcheck: {
                    test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live'],
                    interval: '30s',
                    timeout: '20s',
                    retries: 3,
                },
            };
        }
        // Search (Elasticsearch)
        if (infraServices.search) {
            const volumeName = 'search_data';
            volumes.push(volumeName);
            services.search = {
                image: 'elasticsearch:8.11.0',
                ports: ['9200:9200'],
                environment: [
                    'discovery.type=single-node',
                    'xpack.security.enabled=false',
                    'ES_JAVA_OPTS=-Xms512m -Xmx512m',
                ],
                volumes: [`${volumeName}:/usr/share/elasticsearch/data`],
                healthcheck: {
                    test: ['CMD-SHELL', 'curl -f http://localhost:9200/_cluster/health || exit 1'],
                    interval: '30s',
                    timeout: '10s',
                    retries: 5,
                },
            };
        }
        // Email (Mailhog for development)
        if (infraServices.email) {
            services.email = {
                image: 'mailhog/mailhog:latest',
                ports: ['1025:1025', '8025:8025'],
                environment: [],
            };
        }
        return services;
    }
    generateStartupScript(outputDir) {
        const script = `#!/bin/bash

# Bangstack Docker Startup Script
echo "🚀 Starting Bangstack application..."

# Check if Docker is running
if ! docker info > /dev/null 2>&1; then
    echo "❌ Error: Docker is not running. Please start Docker and try again."
    exit 1
fi

# Check if docker-compose exists
if ! command -v docker-compose &> /dev/null; then
    echo "⚠️  docker-compose not found. Using docker compose instead..."
    DOCKER_COMPOSE_CMD="docker compose"
else
    DOCKER_COMPOSE_CMD="docker-compose"
fi

# Build and start services
echo "📦 Building Docker images..."
$DOCKER_COMPOSE_CMD build

echo "🎬 Starting services..."
$DOCKER_COMPOSE_CMD up -d

echo ""
echo "✅ Application started successfully!"
echo ""
echo "📊 Service status:"
$DOCKER_COMPOSE_CMD ps

echo ""
echo "🌐 Access your application:"
echo "   Frontend: http://localhost:5173"
echo "   Backend:  http://localhost:3000"
echo ""
echo "📝 Useful commands:"
echo "   View logs:     $DOCKER_COMPOSE_CMD logs -f"
echo "   Stop services: $DOCKER_COMPOSE_CMD down"
echo "   Restart:       $DOCKER_COMPOSE_CMD restart"
echo ""
`;
        return {
            path: path.join(outputDir, 'start.sh'),
            content: script,
            overwrite: false,
        };
    }
    toYAML(obj, indent = 0) {
        let yaml = '';
        const spaces = '  '.repeat(indent);
        for (const [key, value] of Object.entries(obj)) {
            if (value === null || value === undefined) {
                yaml += `${spaces}${key}:\n`;
            }
            else if (Array.isArray(value)) {
                if (value.length === 0) {
                    yaml += `${spaces}${key}: []\n`;
                }
                else {
                    yaml += `${spaces}${key}:\n`;
                    for (const item of value) {
                        if (typeof item === 'object') {
                            yaml += `${spaces}  -\n`;
                            yaml += this.toYAML(item, indent + 2).split('\n').map(line => line ? `  ${line}` : '').join('\n') + '\n';
                        }
                        else {
                            yaml += `${spaces}  - ${item}\n`;
                        }
                    }
                }
            }
            else if (typeof value === 'object') {
                yaml += `${spaces}${key}:\n`;
                yaml += this.toYAML(value, indent + 1);
            }
            else {
                // Quote version numbers to avoid YAML parsing them as floats
                let formattedValue = value;
                if (key === 'version' || (typeof value === 'string' && /^\d+(\.\d+)*$/.test(value))) {
                    formattedValue = `'${value}'`;
                }
                yaml += `${spaces}${key}: ${formattedValue}\n`;
            }
        }
        return yaml;
    }
}
exports.DockerComposeGenerator = DockerComposeGenerator;
//# sourceMappingURL=docker-compose-generator.js.map