/**
 * Phase 5 Build — Stage 4: Integration Injection
 *
 * Copies selected integration module code from the repo cache into the
 * generated project directory. Generates barrel exports and a manifest
 * describing which integrations were injected and why.
 *
 * Reads the integration inference result from the previous stage output.
 * Resolves each module's path in ~/.agentics/repo-cache/integrations/.
 *
 * For TypeScript projects: copies `src/` directory from each module.
 * For Rust projects: copies rust variant if available, falls back to TS.
 *
 * Error codes:
 *   ECLI-P5B-040  Integration injection failed
 *   ECLI-P5B-041  Integration module not found in repo cache
 */
import * as fs from 'node:fs';
import * as path from 'node:path';
import { createSpan, endSpan, emitSpan } from '../../phase2/telemetry.js';
// ============================================================================
// Constants
// ============================================================================
const DIR_MODE = 0o700;
const FILE_MODE = 0o600;
const GENERATED_HEADER = '// Generated by Phase 5 Build — Integration Injection\n';
// ============================================================================
// Helpers
// ============================================================================
/**
 * Recursively copy a directory tree.
 */
function copyDirRecursive(src, dest) {
    let count = 0;
    fs.mkdirSync(dest, { recursive: true, mode: DIR_MODE });
    for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
        const srcPath = path.join(src, entry.name);
        const destPath = path.join(dest, entry.name);
        if (entry.isDirectory()) {
            // Skip test directories, __mocks__, examples, node_modules
            if (['__tests__', '__mocks__', '__fixtures__', 'examples', 'node_modules', '.git'].includes(entry.name))
                continue;
            count += copyDirRecursive(srcPath, destPath);
        }
        else {
            // Skip test files and markdown
            if (entry.name.endsWith('.test.ts') || entry.name.endsWith('.spec.ts'))
                continue;
            if (entry.name.endsWith('.md') || entry.name === 'vitest.config.ts' || entry.name === 'tsup.config.ts')
                continue;
            fs.copyFileSync(srcPath, destPath);
            count++;
        }
    }
    return count;
}
/**
 * Sanitize a module ID into a valid directory name.
 * e.g., 'gcp/bigquery' → 'gcp-bigquery'
 */
function sanitizeModuleId(id) {
    return id.replace(/\//g, '-');
}
/**
 * Extract dependencies from a package.json file.
 */
function extractDependencies(pkgJsonPath) {
    try {
        if (!fs.existsSync(pkgJsonPath))
            return {};
        const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
        return (pkg['dependencies'] ?? {});
    }
    catch {
        return {};
    }
}
// ============================================================================
// Barrel Export Generator
// ============================================================================
function generateTsBarrel(modules) {
    const lines = [
        GENERATED_HEADER,
        '/**',
        ' * Auto-generated integration barrel.',
        ' * Lists all inferred integrations for this prototype.',
        ' */',
        '',
    ];
    for (const mod of modules) {
        const dirName = sanitizeModuleId(mod.id);
        const alias = dirName.replace(/-/g, '_');
        lines.push(`// ${mod.category}: ${mod.id} (score: ${mod.score}, ${mod.rationale})`);
        lines.push(`export * as ${alias} from './${dirName}/index.js';`);
        lines.push('');
    }
    return lines.join('\n');
}
function generateRustBarrel(modules) {
    const lines = [
        GENERATED_HEADER.replace('//', '//'),
        '//! Auto-generated integration barrel.',
        '//! Lists all inferred integrations for this prototype.',
        '',
    ];
    for (const mod of modules) {
        const dirName = sanitizeModuleId(mod.id);
        const modName = dirName.replace(/-/g, '_');
        lines.push(`// ${mod.category}: ${mod.id} (score: ${mod.score})`);
        lines.push(`pub mod ${modName};`);
        lines.push('');
    }
    return lines.join('\n');
}
// ============================================================================
// Public: injectIntegrations
// ============================================================================
/**
 * Stage 4: Copy selected integration modules into the generated project.
 *
 * @param context - The Phase 5 Build pipeline context
 * @param inferenceResult - The result from the integration inference stage
 * @param language - The resolved implementation language ('typescript' | 'rust')
 * @param codegenDir - The codegen project directory (phase5/codegen/)
 * @returns Injection result
 */
export function injectIntegrations(context, inferenceResult, language, codegenDir) {
    const startTime = Date.now();
    const span = createSpan('integration-injection', 'inject-integrations', {
        traceId: context.traceId,
        moduleCount: inferenceResult.selectedModules.length,
    });
    try {
        const integrationsRepoPath = context.platformRepoPaths['integrations'];
        if (!integrationsRepoPath || !fs.existsSync(integrationsRepoPath)) {
            // No integrations repo available — return a completed-with-skip result
            // instead of throwing (this is expected when running outside the full platform)
            const skipSpan = endSpan(span, 'ok', { skipped: true, reason: 'no-integrations-repo' });
            emitSpan(skipSpan);
            context.telemetrySpans.push(skipSpan);
            return {
                stageOutput: {
                    stage: 'integration-injection',
                    status: 'completed',
                    data: { copiedModules: [], barrelExportPath: '', manifestPath: '', mergedDependencies: 0 },
                    artifacts: [],
                    timing: Date.now() - startTime,
                    summary: '     Integration injection skipped (no integrations repo available)',
                },
            };
        }
        const isRust = language === 'rust';
        const projectDir = path.join(codegenDir, 'project');
        const integrationsDir = isRust
            ? path.join(projectDir, 'src', 'integrations')
            : path.join(projectDir, 'src', 'integrations');
        fs.mkdirSync(integrationsDir, { recursive: true, mode: DIR_MODE });
        const copiedModules = [];
        const allArtifacts = [];
        const mergedDeps = {};
        let totalFiles = 0;
        for (const mod of inferenceResult.selectedModules) {
            const moduleId = mod.id;
            const dirName = sanitizeModuleId(moduleId);
            // Resolve source path: prefer language variant, fall back to typescript
            let sourcePath;
            if (isRust && mod.hasRustVariant) {
                sourcePath = path.join(integrationsRepoPath, mod.repoPath, 'rust', 'src');
                if (!fs.existsSync(sourcePath)) {
                    sourcePath = path.join(integrationsRepoPath, mod.repoPath, 'typescript', 'src');
                }
            }
            else {
                sourcePath = path.join(integrationsRepoPath, mod.repoPath, 'typescript', 'src');
            }
            // Some modules might have a different structure (e.g., aws/bedrock)
            if (!fs.existsSync(sourcePath)) {
                // Try direct path (for modules like gcp/bigquery → gcp/bigquery/typescript/src)
                const altPath = path.join(integrationsRepoPath, mod.repoPath, 'src');
                if (fs.existsSync(altPath)) {
                    sourcePath = altPath;
                }
                else {
                    // Skip missing modules — log but don't fail
                    console.error(`     [WARN] Integration module not found: ${moduleId} (${sourcePath})`);
                    continue;
                }
            }
            const destPath = path.join(integrationsDir, dirName);
            const filesCopied = copyDirRecursive(sourcePath, destPath);
            totalFiles += filesCopied;
            copiedModules.push(moduleId);
            allArtifacts.push(destPath);
            // Extract dependencies for merging
            const pkgJsonPath = path.join(integrationsRepoPath, mod.repoPath, 'typescript', 'package.json');
            const deps = extractDependencies(pkgJsonPath);
            Object.assign(mergedDeps, deps);
        }
        // Generate barrel export
        const barrelContent = isRust
            ? generateRustBarrel(inferenceResult.selectedModules.filter(m => copiedModules.includes(m.id)))
            : generateTsBarrel(inferenceResult.selectedModules.filter(m => copiedModules.includes(m.id)));
        const barrelFileName = isRust ? 'mod.rs' : 'index.ts';
        const barrelPath = path.join(integrationsDir, barrelFileName);
        fs.writeFileSync(barrelPath, barrelContent, { encoding: 'utf-8', mode: FILE_MODE });
        allArtifacts.push(barrelPath);
        // Generate integration manifest
        const manifestPath = path.join(integrationsDir, 'manifest.json');
        fs.writeFileSync(manifestPath, JSON.stringify({
            inferred_integrations: inferenceResult.selectedModules
                .filter(m => copiedModules.includes(m.id))
                .map(m => ({
                id: m.id,
                category: m.category,
                score: m.score,
                rationale: m.rationale,
            })),
            total: copiedModules.length,
            total_files: totalFiles,
            inference_source: 'scenario+sparc+domain-model',
            language,
        }, null, 2) + '\n', { encoding: 'utf-8', mode: FILE_MODE });
        allArtifacts.push(manifestPath);
        // Merge dependencies into project package.json (TypeScript only)
        let mergedDependencies = 0;
        if (!isRust) {
            const projectPkgPath = path.join(projectDir, 'package.json');
            if (fs.existsSync(projectPkgPath)) {
                try {
                    const projectPkg = JSON.parse(fs.readFileSync(projectPkgPath, 'utf-8'));
                    const existingDeps = (projectPkg['dependencies'] ?? {});
                    for (const [dep, ver] of Object.entries(mergedDeps)) {
                        if (!existingDeps[dep]) {
                            existingDeps[dep] = ver;
                            mergedDependencies++;
                        }
                    }
                    projectPkg['dependencies'] = existingDeps;
                    fs.writeFileSync(projectPkgPath, JSON.stringify(projectPkg, null, 2) + '\n', {
                        encoding: 'utf-8', mode: FILE_MODE,
                    });
                }
                catch {
                    // Non-fatal — dependency merging is best-effort
                }
            }
        }
        const result = {
            copiedModules,
            barrelExportPath: barrelPath,
            manifestPath,
            mergedDependencies,
        };
        const finalSpan = endSpan(span, 'ok', {
            copiedCount: copiedModules.length,
            totalFiles,
            mergedDependencies,
        });
        emitSpan(finalSpan);
        context.telemetrySpans.push(finalSpan);
        const timing = Date.now() - startTime;
        return {
            stageOutput: {
                stage: 'integration-injection',
                status: 'completed',
                data: result,
                artifacts: allArtifacts,
                timing,
                summary: `     Injected ${copiedModules.length} integration modules (${totalFiles} files, ${mergedDependencies} deps merged)`,
            },
        };
    }
    catch (err) {
        const errorMessage = err instanceof Error ? err.message : String(err);
        const finalSpan = endSpan(span, 'error', { error: errorMessage });
        emitSpan(finalSpan);
        context.telemetrySpans.push(finalSpan);
        return {
            stageOutput: {
                stage: 'integration-injection',
                status: 'failed',
                data: { copiedModules: [], barrelExportPath: '', manifestPath: '', mergedDependencies: 0 },
                artifacts: [],
                timing: Date.now() - startTime,
                summary: `  ERROR: Integration injection failed: ${errorMessage}`,
            },
        };
    }
}
//# sourceMappingURL=integration-injector.js.map