"use strict";
/**
 * Code Generator
 * Orchestrates code generation from workflows
 * Updated for flexible stack system
 */
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.CodeGenerator = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const workflow_engine_1 = require("../engine/workflow-engine");
const generator_registry_1 = require("./generator-registry");
const file_writer_1 = require("./file-writer");
const parallel_executor_1 = require("./parallel-executor");
class CodeGenerator {
    registry;
    engine;
    fileWriter;
    parallelExecutor;
    metrics;
    constructor(registry) {
        this.registry = registry || generator_registry_1.globalRegistry;
        this.engine = new workflow_engine_1.WorkflowEngine();
        this.fileWriter = new file_writer_1.FileWriter();
        this.parallelExecutor = new parallel_executor_1.ParallelExecutor();
        this.metrics = new parallel_executor_1.ExecutionMetrics();
    }
    /**
     * Generate code from a workflow
     */
    async generate(workflow, options) {
        const result = {
            files: [],
            written: [],
            skipped: [],
            errors: [],
            warnings: [],
        };
        // Validate workflow first
        const validation = this.engine.validate(workflow);
        if (!validation.valid) {
            result.errors.push(...validation.errors);
            return result;
        }
        if (validation.warnings.length > 0) {
            result.warnings.push(...validation.warnings);
        }
        // Analyze workflow to create execution plan
        const executionPlan = this.engine.analyze(workflow);
        // Create generator context
        const context = {
            workflow,
            executionPlan,
            outputDirectory: options.outputDirectory,
            options: options.generatorOptions,
            extendedWorkflow: options.extendedWorkflow,
        };
        // Generate code for each stack component
        for (const [componentName, component] of Object.entries(workflow.stack)) {
            const componentResult = await this.generateComponent(componentName, component, context);
            result.files.push(...componentResult.files);
            if (componentResult.errors) {
                result.errors.push(...componentResult.errors);
            }
            if (componentResult.warnings) {
                result.warnings.push(...componentResult.warnings);
            }
        }
        // Generate from extended workflow technology if available
        let authDependencies;
        if (options.extendedWorkflow) {
            const extendedResult = await this.generateFromExtendedWorkflow(options.extendedWorkflow, context);
            result.files.push(...extendedResult.files);
            if (extendedResult.errors) {
                result.errors.push(...extendedResult.errors);
            }
            if (extendedResult.warnings) {
                result.warnings.push(...extendedResult.warnings);
            }
            // Store auth dependencies to inject later
            if (extendedResult.dependencies) {
                authDependencies = extendedResult.dependencies;
            }
        }
        // Generate documentation
        if (workflow.documentation) {
            const docsResult = await this.generateDocumentation(context);
            result.files.push(...docsResult.files);
            if (docsResult.errors) {
                result.errors.push(...docsResult.errors);
            }
            if (docsResult.warnings) {
                result.warnings.push(...docsResult.warnings);
            }
        }
        // Generate tests
        if (workflow.testing) {
            const testResult = await this.generateTests(context);
            result.files.push(...testResult.files);
            if (testResult.errors) {
                result.errors.push(...testResult.errors);
            }
            if (testResult.warnings) {
                result.warnings.push(...testResult.warnings);
            }
        }
        // Generate Docker orchestration
        const dockerResult = await this.generateDockerOrchestration(context);
        result.files.push(...dockerResult.files);
        if (dockerResult.errors) {
            result.errors.push(...dockerResult.errors);
        }
        if (dockerResult.warnings) {
            result.warnings.push(...dockerResult.warnings);
        }
        // Generate with custom generators (CI/CD, etc.)
        const customResult = await this.generateCustom(context);
        result.files.push(...customResult.files);
        if (customResult.errors) {
            result.errors.push(...customResult.errors);
        }
        if (customResult.warnings) {
            result.warnings.push(...customResult.warnings);
        }
        // Copy workflow file to output directory and generate config files
        if (options.workflowFilePath) {
            const workflowFile = this.generateWorkflowFile(workflow, options.workflowFilePath, options.outputDirectory);
            if (workflowFile) {
                result.files.push(workflowFile);
            }
            // Generate TypeScript config files for backend and frontend
            const configFiles = this.generateWorkflowConfigFiles(workflow, options.extendedWorkflow, options.outputDirectory);
            result.files.push(...configFiles);
        }
        // Inject auth dependencies into package.json files (must happen before manifest generation)
        if (authDependencies) {
            this.injectDependencies(result.files, authDependencies);
        }
        // Generate version manifest (must be last to track all generated files)
        const manifestResult = await this.generateManifest(context);
        result.files.push(...manifestResult.files);
        if (manifestResult.warnings) {
            result.warnings.push(...manifestResult.warnings);
        }
        // Write files to disk
        if (result.files.length > 0) {
            const writeResult = await this.fileWriter.write(result.files, {
                dryRun: options.dryRun,
                verbose: options.verbose,
                onProgress: options.onProgress,
            });
            result.written = writeResult.written;
            result.skipped = writeResult.skipped;
            result.errors.push(...writeResult.errors.map(e => `${e.path}: ${e.error}`));
        }
        return result;
    }
    /**
     * Generate code for a single stack component
     */
    async generateComponent(componentName, component, context) {
        // Look up generator by component type and technology
        const generator = this.registry.getComponentGenerator(component.type, component.technology);
        if (!generator) {
            return {
                files: [],
                warnings: [
                    `No generator found for component "${componentName}" (type: ${component.type}, technology: ${component.technology})`,
                ],
            };
        }
        try {
            return await Promise.resolve(generator.generate(context));
        }
        catch (error) {
            return {
                files: [],
                errors: [
                    `Generation failed for component "${componentName}": ${error instanceof Error ? error.message : String(error)}`,
                ],
            };
        }
    }
    /**
     * Generate documentation
     */
    async generateDocumentation(context) {
        // Default to markdown
        const format = 'markdown';
        const generator = this.registry.getDocumentationGenerator(format);
        if (!generator) {
            return {
                files: [],
                warnings: [`No generator found for documentation format: ${format}`],
            };
        }
        try {
            return await Promise.resolve(generator.generate(context));
        }
        catch (error) {
            return {
                files: [],
                errors: [
                    `Documentation generation failed: ${error instanceof Error ? error.message : String(error)}`,
                ],
            };
        }
    }
    /**
     * Generate tests
     */
    async generateTests(context) {
        // Collect all test generators
        const generators = [];
        const frontendTestGen = this.registry.getTestGenerator('vitest');
        if (frontendTestGen) {
            generators.push({ name: 'Frontend Tests (Vitest)', generator: frontendTestGen });
        }
        const backendTestGen = this.registry.getTestGenerator('jest');
        if (backendTestGen) {
            generators.push({ name: 'Backend Tests (Jest)', generator: backendTestGen });
        }
        const e2eTestGen = this.registry.getTestGenerator('playwright');
        if (e2eTestGen) {
            generators.push({ name: 'E2E Tests (Playwright)', generator: e2eTestGen });
        }
        const cliTestGen = this.registry.getTestGenerator('cli-jest');
        if (cliTestGen) {
            generators.push({ name: 'CLI Tests (Jest)', generator: cliTestGen });
        }
        if (generators.length === 0) {
            return { files: [] };
        }
        // Execute test generators in parallel
        const parallelResult = await this.parallelExecutor.execute(generators, context, {
            maxConcurrency: 4, // Test generators are I/O heavy, can run more in parallel
            failFast: false,
        });
        return {
            files: parallelResult.allFiles,
            errors: parallelResult.allErrors.length > 0 ? parallelResult.allErrors : undefined,
            warnings: parallelResult.allWarnings.length > 0 ? parallelResult.allWarnings : undefined,
        };
    }
    /**
     * Generate Docker orchestration
     */
    async generateDockerOrchestration(context) {
        // Import Docker generator dynamically
        try {
            const { DockerComposeGenerator } = await Promise.resolve().then(() => __importStar(require('./docker/docker-compose-generator')));
            const dockerGen = new DockerComposeGenerator();
            return await Promise.resolve(dockerGen.generate(context));
        }
        catch (_error) {
            return {
                files: [],
                warnings: ['Docker orchestration generation skipped'],
            };
        }
    }
    /**
     * Generate version manifest
     */
    async generateManifest(context) {
        try {
            const { ManifestGenerator } = await Promise.resolve().then(() => __importStar(require('./manifest-generator')));
            const manifestGen = new ManifestGenerator();
            return await Promise.resolve(manifestGen.generate(context));
        }
        catch (_error) {
            return {
                files: [],
                warnings: ['Manifest generation skipped'],
            };
        }
    }
    /**
     * Generate with custom generators (CI/CD, etc.)
     */
    async generateCustom(context) {
        // Get all custom generators
        const customGenerators = this.registry.getCustomGenerators();
        if (customGenerators.length === 0) {
            return { files: [] };
        }
        // Prepare generators for parallel execution
        const generators = customGenerators.map(gen => ({
            name: gen.name,
            generator: gen,
        }));
        // Execute custom generators in parallel
        const parallelResult = await this.parallelExecutor.execute(generators, context, {
            maxConcurrency: 3, // CI/CD, env, etc. can run in parallel
            failFast: false,
        });
        return {
            files: parallelResult.allFiles,
            errors: parallelResult.allErrors.length > 0 ? parallelResult.allErrors : undefined,
            warnings: parallelResult.allWarnings.length > 0 ? parallelResult.allWarnings : undefined,
        };
    }
    /**
     * Copy workflow file to output directory
     */
    generateWorkflowFile(workflow, workflowFilePath, outputDirectory) {
        try {
            // Read the original workflow file
            const workflowContent = fs.readFileSync(workflowFilePath, 'utf8');
            // Use the workflow name as filename
            const workflowName = workflow.workflow.id || 'workflow';
            const filename = `${workflowName}.workflow.yaml`;
            return {
                path: path.join(outputDirectory, filename),
                content: workflowContent,
                overwrite: false,
            };
        }
        catch (_error) {
            // If we can't read the file, just skip it silently
            return null;
        }
    }
    /**
     * Generate TypeScript workflow config files for backend and frontend
     * Makes workflow configuration available at runtime
     */
    generateWorkflowConfigFiles(workflow, extendedWorkflow, outputDirectory) {
        const files = [];
        // Build the runtime config object
        const config = {
            id: workflow.workflow.id,
            version: workflow.workflow.version,
            name: workflow.workflow.name,
            description: workflow.workflow.description,
            technology: extendedWorkflow?.technology || {},
            models: extendedWorkflow?.models || {},
            // Only include what's needed at runtime
        };
        // Generate TypeScript config content
        const tsContent = `/**
 * Workflow Configuration
 *
 * Auto-generated from ${workflow.workflow.id}.workflow.yaml
 * DO NOT EDIT MANUALLY - Regenerate from workflow file
 *
 * This file provides runtime access to the workflow configuration
 * that was used to generate this application.
 */

export const workflowConfig = ${JSON.stringify(config, null, 2)} as const;

export type WorkflowConfig = typeof workflowConfig;

/**
 * Helper to check if a technology is configured
 */
export function hasTechnology(category: keyof typeof workflowConfig.technology): boolean {
  return !!workflowConfig.technology[category];
}

/**
 * Helper to get technology provider
 */
export function getTechnologyProvider(
  category: keyof typeof workflowConfig.technology,
  field: string = 'provider'
): string | undefined {
  const tech = workflowConfig.technology[category] as any;
  return tech?.[field];
}

/**
 * Check if a specific feature is enabled
 */
export function hasFeature(
  category: keyof typeof workflowConfig.technology,
  feature: string
): boolean {
  const tech = workflowConfig.technology[category] as any;
  const features = tech?.features || [];
  return features.includes(feature);
}
`;
        // Check if backend directory will be generated
        const hasBackend = extendedWorkflow?.technology?.backend ||
            Object.values(workflow.stack).some((c) => c.type === 'backend');
        if (hasBackend) {
            files.push({
                path: path.join(outputDirectory, 'backend', 'src', 'config', 'workflow.ts'),
                content: tsContent,
                overwrite: true,
            });
        }
        // Check if frontend directory will be generated
        const hasFrontend = extendedWorkflow?.technology?.frontend ||
            Object.values(workflow.stack).some((c) => c.type === 'frontend');
        if (hasFrontend) {
            files.push({
                path: path.join(outputDirectory, 'frontend', 'src', 'config', 'workflow.ts'),
                content: tsContent,
                overwrite: true,
            });
        }
        return files;
    }
    /**
     * Generate from extended workflow technology configuration
     */
    async generateFromExtendedWorkflow(extendedWorkflow, context) {
        const allFiles = [];
        const allErrors = [];
        const allWarnings = [];
        const technology = extendedWorkflow.technology;
        // Generate backend if configured
        if (technology?.backend?.language && technology.backend.framework) {
            const backendGen = this.registry.getBackendGenerator(technology.backend.language, technology.backend.framework);
            if (backendGen) {
                try {
                    const result = await Promise.resolve(backendGen.generate(context));
                    allFiles.push(...result.files);
                    if (result.errors)
                        allErrors.push(...result.errors);
                    if (result.warnings)
                        allWarnings.push(...result.warnings);
                }
                catch (error) {
                    allErrors.push(`Backend generation failed: ${error instanceof Error ? error.message : String(error)}`);
                }
            }
            else {
                allWarnings.push(`No backend generator found for ${technology.backend.language}/${technology.backend.framework}`);
            }
        }
        // Generate frontend if configured
        if (technology?.frontend?.framework) {
            const frontendGen = this.registry.getFrontendGenerator(technology.frontend.framework);
            if (frontendGen) {
                try {
                    const result = await Promise.resolve(frontendGen.generate(context));
                    allFiles.push(...result.files);
                    if (result.errors)
                        allErrors.push(...result.errors);
                    if (result.warnings)
                        allWarnings.push(...result.warnings);
                }
                catch (error) {
                    allErrors.push(`Frontend generation failed: ${error instanceof Error ? error.message : String(error)}`);
                }
            }
            else {
                allWarnings.push(`No frontend generator found for ${technology.frontend.framework}`);
            }
        }
        // Generate authentication if configured (auth is on extendedWorkflow, not technology)
        let authDeps;
        if (extendedWorkflow.auth && extendedWorkflow.auth.enabled !== false) {
            const authResult = await this.generateAuthentication(extendedWorkflow.auth, context);
            allFiles.push(...authResult.files);
            if (authResult.errors)
                allErrors.push(...authResult.errors);
            if (authResult.warnings)
                allWarnings.push(...authResult.warnings);
            // Store auth dependencies to be returned
            if (authResult.dependencies) {
                authDeps = authResult.dependencies;
            }
        }
        // Generate event-driven architecture if events are defined
        if (extendedWorkflow.events && Object.keys(extendedWorkflow.events).length > 0) {
            const eventResult = await this.generateEventInfrastructure(extendedWorkflow, context);
            allFiles.push(...eventResult.files);
            if (eventResult.errors)
                allErrors.push(...eventResult.errors);
            if (eventResult.warnings)
                allWarnings.push(...eventResult.warnings);
        }
        return {
            files: allFiles,
            errors: allErrors.length > 0 ? allErrors : undefined,
            warnings: allWarnings.length > 0 ? allWarnings : undefined,
            dependencies: authDeps,
        };
    }
    /**
     * Inject dependencies into package.json files
     */
    injectDependencies(files, dependencies) {
        // Find backend package.json
        if (dependencies.backend) {
            const backendPkgFile = files.find(f => f.path.includes('backend') && f.path.endsWith('package.json'));
            if (backendPkgFile) {
                try {
                    const pkg = JSON.parse(backendPkgFile.content);
                    pkg.dependencies = { ...pkg.dependencies, ...dependencies.backend };
                    backendPkgFile.content = JSON.stringify(pkg, null, 2) + '\n';
                }
                catch (_error) {
                    // If parsing fails, skip injection
                }
            }
        }
        // Find frontend package.json
        if (dependencies.frontend) {
            const frontendPkgFile = files.find(f => f.path.includes('frontend') && f.path.endsWith('package.json'));
            if (frontendPkgFile) {
                try {
                    const pkg = JSON.parse(frontendPkgFile.content);
                    pkg.dependencies = { ...pkg.dependencies, ...dependencies.frontend };
                    frontendPkgFile.content = JSON.stringify(pkg, null, 2) + '\n';
                }
                catch (_error) {
                    // If parsing fails, skip injection
                }
            }
        }
    }
    /**
     * Generate event-driven architecture infrastructure
     */
    async generateEventInfrastructure(extendedWorkflow, context) {
        const allFiles = [];
        const allErrors = [];
        const allWarnings = [];
        try {
            // 1. Generate event type definitions
            const eventTypesGen = this.registry.getCustomGenerator('event-types');
            if (eventTypesGen) {
                const result = await Promise.resolve(eventTypesGen.generate(context));
                allFiles.push(...result.files);
                if (result.errors)
                    allErrors.push(...result.errors);
                if (result.warnings)
                    allWarnings.push(...result.warnings);
            }
            // 2. Generate event bus infrastructure (Redis pub/sub)
            const eventBusGen = this.registry.getCustomGenerator('event-bus');
            if (eventBusGen) {
                const result = await Promise.resolve(eventBusGen.generate(context));
                allFiles.push(...result.files);
                if (result.errors)
                    allErrors.push(...result.errors);
                if (result.warnings)
                    allWarnings.push(...result.warnings);
            }
            // 3. Generate event handlers
            const eventHandlersGen = this.registry.getCustomGenerator('event-handlers');
            if (eventHandlersGen) {
                const result = await Promise.resolve(eventHandlersGen.generate(context));
                allFiles.push(...result.files);
                if (result.errors)
                    allErrors.push(...result.errors);
                if (result.warnings)
                    allWarnings.push(...result.warnings);
            }
            // 4. Generate WebSocket server for real-time updates
            const wsServerGen = this.registry.getCustomGenerator('websocket-server');
            if (wsServerGen) {
                const result = await Promise.resolve(wsServerGen.generate(context));
                allFiles.push(...result.files);
                if (result.errors)
                    allErrors.push(...result.errors);
                if (result.warnings)
                    allWarnings.push(...result.warnings);
            }
            // 5. Generate WebSocket client for frontend (if React frontend exists)
            if (extendedWorkflow.technology?.frontend?.framework === 'react') {
                const wsClientGen = this.registry.getCustomGenerator('websocket-client');
                if (wsClientGen) {
                    const result = await Promise.resolve(wsClientGen.generate(context));
                    allFiles.push(...result.files);
                    if (result.errors)
                        allErrors.push(...result.errors);
                    if (result.warnings)
                        allWarnings.push(...result.warnings);
                }
            }
        }
        catch (error) {
            allErrors.push(`Event infrastructure generation failed: ${error instanceof Error ? error.message : String(error)}`);
        }
        return {
            files: allFiles,
            errors: allErrors.length > 0 ? allErrors : undefined,
            warnings: allWarnings.length > 0 ? allWarnings : undefined,
        };
    }
    /**
     * Generate authentication integration based on configured provider
     */
    async generateAuthentication(authConfig, context) {
        const provider = authConfig.provider;
        if (!provider) {
            return { files: [] };
        }
        try {
            switch (provider) {
                case 'clerk': {
                    const { ClerkAuthGenerator } = await Promise.resolve().then(() => __importStar(require('./auth/clerk-generator')));
                    const clerkGen = new ClerkAuthGenerator();
                    return clerkGen.generate(context, {
                        features: authConfig.strategies,
                        afterSignInUrl: authConfig.after_sign_in_url,
                        afterSignUpUrl: authConfig.after_sign_up_url,
                    });
                }
                case 'auth0': {
                    // Future: Auth0 generator
                    return {
                        files: [],
                        warnings: ['Auth0 generator not yet implemented'],
                    };
                }
                case 'local':
                    // Local auth is handled by the backend generator
                    return { files: [] };
                default:
                    return {
                        files: [],
                        warnings: [`Unknown auth provider: ${provider}`],
                    };
            }
        }
        catch (error) {
            return {
                files: [],
                errors: [
                    `Authentication generation failed: ${error instanceof Error ? error.message : String(error)}`,
                ],
            };
        }
    }
    /**
     * Get the generator registry
     */
    getRegistry() {
        return this.registry;
    }
}
exports.CodeGenerator = CodeGenerator;
//# sourceMappingURL=code-generator.js.map