"use strict";
/**
 * Manifest Generator
 * Tracks workflow versions, component dependencies, and file checksums
 * Enables version rollback detection and mismatch warnings
 */
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.ManifestGenerator = void 0;
const dependency_analyzer_1 = require("./dependency-analyzer");
const path = __importStar(require("path"));
const crypto = __importStar(require("crypto"));
const fs = __importStar(require("fs"));
class ManifestGenerator {
    id = 'version-manifest';
    name = 'Version Manifest Generator';
    description = 'Generates version tracking manifest for rollback detection';
    analyzer;
    constructor() {
        this.analyzer = new dependency_analyzer_1.DependencyAnalyzer();
    }
    generate(context) {
        const { workflow, outputDirectory } = context;
        const files = [];
        // Generate manifest (includes dependency analysis warnings)
        const manifest = this.createManifest(workflow, outputDirectory);
        // Check for existing manifest and compare (version changes, service changes, etc.)
        const manifestPath = path.join(outputDirectory, '.bangstack', 'manifest.json');
        const comparisonWarnings = this.checkForMismatches(manifestPath, manifest);
        // Merge warnings from dependency analysis and version comparison
        const allWarnings = [
            ...(manifest.warnings || []),
            ...comparisonWarnings,
        ];
        if (allWarnings.length > 0) {
            manifest.warnings = allWarnings;
        }
        // Write manifest
        files.push({
            path: manifestPath,
            content: JSON.stringify(manifest, null, 2),
            overwrite: true, // Always overwrite manifest
        });
        return { files };
    }
    createManifest(workflow, outputDir) {
        const manifest = {
            workflow_version: workflow.workflow.version,
            generated_at: new Date().toISOString(),
            components: {},
            infrastructure_services: {},
        };
        const warnings = [];
        // Collect component information
        for (const [componentName, component] of Object.entries(workflow.stack || {})) {
            const comp = component;
            // Analyze component code for actual dependencies
            const analysisResult = this.analyzer.analyzeComponent(outputDir, componentName, comp.type, comp.technology);
            manifest.components[componentName] = {
                type: comp.type,
                technology: comp.technology,
                expected_dependencies: comp.expected_dependencies || [],
                detected_dependencies: analysisResult.detectedDependencies,
                implementation_files: this.scanImplementationFiles(outputDir, componentName),
            };
            // Add analysis errors/warnings if any (e.g., unsupported language)
            if (analysisResult.errors && analysisResult.errors.length > 0) {
                for (const error of analysisResult.errors) {
                    // Check if this is an unsupported language message
                    const isUnsupportedLanguage = error.includes('not supported');
                    if (isUnsupportedLanguage) {
                        // Don't treat as error, just informational warning
                        warnings.push(`Component '${componentName}': ${error}`);
                    }
                    else {
                        // Real error during analysis
                        warnings.push(`Component '${componentName}': ${error}`);
                    }
                }
            }
            // Compare expected vs detected dependencies (only if analysis was successful)
            const analysisSuccessful = !analysisResult.errors ||
                !analysisResult.errors.some(e => e.includes('not supported'));
            if (analysisSuccessful &&
                comp.expected_dependencies &&
                comp.expected_dependencies.length > 0) {
                const comparisonWarnings = this.analyzer.compareDependencies(comp.expected_dependencies, analysisResult.detectedDependencies);
                for (const warning of comparisonWarnings) {
                    warnings.push(`Component '${componentName}': ${warning}`);
                }
            }
        }
        // Collect infrastructure services across all stages
        if (workflow.stages) {
            const allServices = this.collectInfrastructureServices(workflow);
            for (const [serviceName, serviceData] of Object.entries(allServices)) {
                const data = serviceData;
                manifest.infrastructure_services[serviceName] = {
                    provider: data.providers.join(', '),
                    stages: data.stages,
                };
            }
        }
        // Store warnings from dependency analysis
        if (warnings.length > 0) {
            manifest.warnings = warnings;
        }
        return manifest;
    }
    collectInfrastructureServices(workflow) {
        const services = {};
        for (const [stageName, stage] of Object.entries(workflow.stages || {})) {
            const stageConfig = stage;
            const infraServices = stageConfig.infrastructure?.services || {};
            for (const [serviceName, serviceConfig] of Object.entries(infraServices)) {
                const config = serviceConfig;
                if (!services[serviceName]) {
                    services[serviceName] = {
                        providers: [],
                        stages: [],
                    };
                }
                if (config.provider && !services[serviceName].providers.includes(config.provider)) {
                    services[serviceName].providers.push(config.provider);
                }
                services[serviceName].stages.push(stageName);
            }
        }
        return services;
    }
    scanImplementationFiles(outputDir, _componentName) {
        const files = {};
        // Look for client files in backend/src/services/
        const servicesDir = path.join(outputDir, 'backend', 'src', 'services');
        if (!fs.existsSync(servicesDir)) {
            return files;
        }
        const clientFiles = fs.readdirSync(servicesDir).filter(f => f.endsWith('-client.ts'));
        for (const file of clientFiles) {
            const filePath = path.join(servicesDir, file);
            const stats = fs.statSync(filePath);
            const content = fs.readFileSync(filePath, 'utf-8');
            const checksum = crypto.createHash('sha256').update(content).digest('hex');
            files[file] = {
                checksum,
                overwrite: false, // Client files are never overwritten
                last_generated: stats.mtime.toISOString(),
            };
        }
        return files;
    }
    checkForMismatches(manifestPath, newManifest) {
        const warnings = [];
        // Check if previous manifest exists
        if (!fs.existsSync(manifestPath)) {
            return warnings; // First generation, no warnings
        }
        try {
            const oldContent = fs.readFileSync(manifestPath, 'utf-8');
            const oldManifest = JSON.parse(oldContent);
            // Check for version changes
            if (oldManifest.workflow_version !== newManifest.workflow_version) {
                warnings.push(`Workflow version changed: ${oldManifest.workflow_version} → ${newManifest.workflow_version}`);
            }
            // Check for removed services
            const oldServices = Object.keys(oldManifest.infrastructure_services);
            const newServices = Object.keys(newManifest.infrastructure_services);
            const removedServices = oldServices.filter(s => !newServices.includes(s));
            if (removedServices.length > 0) {
                warnings.push(`Removed services: ${removedServices.join(', ')} - Client files may still exist`);
            }
            // Check for added services
            const addedServices = newServices.filter(s => !oldServices.includes(s));
            if (addedServices.length > 0) {
                warnings.push(`Added services: ${addedServices.join(', ')}`);
            }
            // Check for component dependency changes
            for (const [componentName, component] of Object.entries(newManifest.components)) {
                const oldComponent = oldManifest.components[componentName];
                if (oldComponent && component.expected_dependencies) {
                    const oldDeps = oldComponent.expected_dependencies?.map(d => d.service) || [];
                    const newDeps = component.expected_dependencies.map(d => d.service);
                    const removedDeps = oldDeps.filter(d => !newDeps.includes(d));
                    const addedDeps = newDeps.filter(d => !oldDeps.includes(d));
                    if (removedDeps.length > 0 || addedDeps.length > 0) {
                        warnings.push(`Component '${componentName}' dependencies changed: ` +
                            `removed [${removedDeps.join(', ')}], added [${addedDeps.join(', ')}]`);
                    }
                }
            }
            // Check for file modifications
            for (const [componentName, component] of Object.entries(newManifest.components)) {
                const oldComponent = oldManifest.components[componentName];
                if (oldComponent) {
                    for (const [fileName, fileInfo] of Object.entries(component.implementation_files)) {
                        const oldFileInfo = oldComponent.implementation_files[fileName];
                        if (oldFileInfo && oldFileInfo.checksum !== fileInfo.checksum) {
                            warnings.push(`File '${fileName}' was manually modified (checksum changed) - verify compatibility on rollback`);
                        }
                    }
                }
            }
        }
        catch (error) {
            warnings.push(`Warning: Could not read previous manifest: ${error}`);
        }
        return warnings;
    }
}
exports.ManifestGenerator = ManifestGenerator;
//# sourceMappingURL=manifest-generator.js.map