/**
 * Bundle Optimizer for ZinTrust Framework
 * Reduces deployment package size by:
 * - Tree-shaking unused ORM adapters
 * - Minifying compiled JavaScript
 * - Removing dev dependencies
 * - Inlining configuration
 */
import { Logger } from '../config/logger.js';
import fs from '../node-singletons/fs.js';
import * as path from '../node-singletons/path.js';
/**
 * Bundle optimizer - reduces deployed package size
 * Sealed namespace for immutability
 */
export const BundleOptimizer = Object.freeze({
    /**
     * Create a new bundle optimizer instance
     */
    create(options) {
        const distDir = path.resolve('dist');
        return {
            /**
             * Run optimization for target platform
             */
            async optimize() {
                Logger.info(`\n🔧 Optimizing bundle for ${options.platform} platform...`);
                // Analyze current bundle
                const analysis = await analyze(distDir, options);
                if (options.analyzeOnly === true) {
                    printAnalysis(analysis);
                    return analysis;
                }
                // Apply platform-specific optimizations
                switch (options.platform) {
                    case 'lambda':
                        await optimizeForLambda(distDir, options);
                        break;
                    case 'cloudflare':
                        await optimizeForCloudflare(distDir, options);
                        break;
                    case 'deno':
                        await optimizeForDeno(distDir, options);
                        break;
                    case 'fargate':
                        await optimizeForFargate(distDir, options);
                        break;
                }
                // Re-analyze after optimizations
                const optimized = await analyze(distDir, options);
                printAnalysis(optimized);
                return optimized;
            },
        };
    },
});
/**
 * Get all files recursively
 */
async function getFilesRecursive(dir) {
    if (!fs.existsSync(dir)) {
        return [];
    }
    const entries = await fs.promises.readdir(dir, { withFileTypes: true });
    const nested = await Promise.all(entries.map(async (entry) => {
        const fullPath = path.join(dir, entry.name);
        if (entry.isDirectory()) {
            return getFilesRecursive(fullPath);
        }
        return [fullPath];
    }));
    return nested.flat();
}
/**
 * Generate optimization recommendations
 */
function generateRecommendations(files, totalSize, options) {
    const recommendations = [];
    const sizeInMb = totalSize / (1024 * 1024);
    if (sizeInMb > 100) {
        recommendations.push('❌ Bundle exceeds 100 MB - remove unnecessary files');
    }
    // Find largest files
    const largest = files.slice(0, 3);
    for (const file of largest) {
        if (file.percentage > 20) {
            recommendations.push(`⚠️  ${file.path} is ${file.percentage.toFixed(1)}% of bundle`);
        }
    }
    if (options.platform === 'cloudflare' && sizeInMb > 1) {
        recommendations.push('⚠️  Cloudflare Workers: Bundle > 1 MB, consider upgrading plan');
    }
    if (recommendations.length === 0) {
        recommendations.push('✅ Bundle is well-optimized');
    }
    return recommendations;
}
/**
 * Print analysis report
 */
function printAnalysis(analysis) {
    const sizeInMb = (analysis.totalSize / (1024 * 1024)).toFixed(2);
    const sizeInKb = (analysis.totalSize / 1024).toFixed(2);
    Logger.info(`\n📊 Bundle Analysis (${analysis.platform})`);
    Logger.info(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
    Logger.info(`Total Size: ${sizeInMb} MB (${sizeInKb} KB)`);
    Logger.info(`Files: ${analysis.files.length}\n`);
    // Show top 10 largest files
    const topFiles = analysis.files.slice(0, 10);
    for (const file of topFiles) {
        const bar = '█'.repeat(Math.round(file.percentage / 2));
        Logger.info(`  ${file.path.padEnd(40)} ${(file.size / 1024).toFixed(1).padStart(8)} KB  ${bar}`);
    }
    // Recommendations
    if (analysis.recommendations.length > 0) {
        Logger.info('\n💡 Recommendations:');
        for (const rec of analysis.recommendations) {
            Logger.info(`  ${rec}`);
        }
    }
    Logger.info('\n');
}
/**
 * Analyze bundle structure
 */
async function analyze(distDir, options) {
    const files = await getFilesRecursive(distDir);
    // Collect sizes concurrently but avoid shared mutable state by computing sizes
    // and then reducing the total afterwards to avoid race conditions.
    const fileAnalysis = await Promise.all(files.map(async (file) => {
        const stats = await fs.promises.stat(file);
        const size = stats.size;
        return {
            path: path.relative(distDir, file),
            size,
            percentage: 0,
        };
    }));
    // Compute total size in a single pass (no shared mutation)
    const totalSize = fileAnalysis.reduce((acc, f) => acc + f.size, 0);
    // Calculate percentages
    fileAnalysis.forEach((f) => {
        f.percentage = totalSize > 0 ? (f.size / totalSize) * 100 : 0;
    });
    // Sort by size descending
    fileAnalysis.sort((a, b) => b.size - a.size);
    return {
        platform: options.platform,
        totalSize,
        files: fileAnalysis,
        recommendations: generateRecommendations(fileAnalysis, totalSize, options),
    };
}
/**
 * Remove unused ORM adapters
 */
async function removeUnusedAdapters(distDir, options, ...adapters) {
    const adapterDir = path.join(distDir, 'orm', 'adapters');
    await Promise.all(adapters.map(async (adapter) => {
        const files = [`${adapter}Adapter.js`, `${adapter}Adapter.d.ts`];
        await Promise.all(files.map(async (file) => {
            const filePath = path.join(adapterDir, file);
            if (fs.existsSync(filePath)) {
                await fs.promises.unlink(filePath);
                if (options.verbose === true) {
                    Logger.info(`  ✓ Removed ${adapter} adapter`);
                }
            }
        }));
    }));
}
/**
 * Remove dev dependencies from node_modules
 */
async function removeDevDependencies(distDir, options) {
    const nmDir = path.join(distDir, '..', 'node_modules');
    const devDeps = [
        '@types',
        '@typescript-eslint',
        'typescript',
        'eslint',
        'prettier',
        'vitest',
        '@vitest/coverage-v8',
        'sonar-scanner',
        'tsx',
    ];
    if (!fs.existsSync(nmDir))
        return;
    await Promise.all(devDeps.map(async (dep) => {
        const depPath = path.join(nmDir, dep);
        if (fs.existsSync(depPath)) {
            await fs.promises.rm(depPath, { recursive: true });
            if (options.verbose === true) {
                Logger.info(`  ✓ Removed ${dep}`);
            }
        }
    }));
}
/**
 * Minify JavaScript files
 */
// eslint-disable-next-line @typescript-eslint/require-await
async function minifyJavaScript(_aggressive = false) {
    Logger.info('  → Minifying JavaScript...');
    // In production, would use esbuild or terser
    // This is a placeholder showing the pattern
    Logger.info('  ✓ JavaScript minified');
}
/**
 * Remove a specific module
 */
async function removeModule(distDir, options, modulePath) {
    const distModule = modulePath.replace('src/', `${distDir}/`).replace('.ts', '.js');
    if (fs.existsSync(distModule)) {
        await fs.promises.unlink(distModule);
        const dtsPath = distModule.replace('.js', '.d.ts');
        if (fs.existsSync(dtsPath)) {
            await fs.promises.unlink(dtsPath);
        }
        if (options.verbose === true) {
            Logger.info(`  ✓ Removed module: ${modulePath}`);
        }
    }
}
/**
 * Check if module is used
 */
function hasUsedModule(_moduleName) {
    // Placeholder - would check imports in compiled code
    return true;
}
/**
 * Remove unused middleware
 */
async function removeUnusedMiddleware(distDir, options) {
    const middlewareDir = path.join(distDir, 'middleware');
    // Keep only essential middleware, remove optional ones
    const optionalMiddleware = ['logging.js', 'profiling.js', 'rateLimit.js'];
    if (!fs.existsSync(middlewareDir))
        return;
    await Promise.all(optionalMiddleware.map(async (file) => {
        const filePath = path.join(middlewareDir, file);
        if (fs.existsSync(filePath)) {
            await fs.promises.unlink(filePath);
            if (options.verbose === true) {
                Logger.info(`  ✓ Removed middleware: ${file}`);
            }
        }
    }));
}
/**
 * Inline small files to reduce overhead
 */
function inlineSmallFiles(threshold) {
    Logger.info(`  → Inlining files smaller than ${(threshold / 1024).toFixed(0)} KB...`);
    // Placeholder for actual inlining logic
}
/**
 * Remove files matching patterns
 */
async function removeFiles(distDir, options, patterns) {
    const files = await getFilesRecursive(distDir);
    const filesToRemove = files.filter((file) => patterns.some((pattern) => file.includes(pattern)));
    await Promise.all(filesToRemove.map(async (file) => {
        await fs.promises.unlink(file);
        if (options.verbose === true) {
            Logger.info(`  ✓ Removed ${path.relative(distDir, file)}`);
        }
    }));
}
/**
 * Optimize for AWS Lambda (2-3 MB limit for direct upload)
 */
async function optimizeForLambda(distDir, options) {
    Logger.info('📦 Optimizing for Lambda...');
    // Remove unused ORM adapters
    await removeUnusedAdapters(distDir, options, 'mysql', 'sqlserver', 'd1');
    // Remove dev dependencies from node_modules
    await removeDevDependencies(distDir, options);
    // Minify all JS files
    await minifyJavaScript();
    // Remove unused security modules if not needed
    if (!hasUsedModule('CsrfTokenManager')) {
        await removeModule(distDir, options, 'src/security/CsrfTokenManager.ts');
    }
    Logger.info('✅ Lambda optimization complete');
}
/**
 * Optimize for Cloudflare Workers (<1 MB limit)
 */
async function optimizeForCloudflare(distDir, options) {
    Logger.info('⚡ Optimizing for Cloudflare Workers (strict <1 MB limit)...');
    // Remove ALL unused adapters except cloudflare
    await removeUnusedAdapters(distDir, options, 'postgresql', 'mysql', 'sqlserver');
    // Remove Node.js HTTP server adapter
    await removeModule(distDir, options, 'src/runtime/adapters/NodeServerAdapter.ts');
    // Minify aggressively
    await minifyJavaScript(true);
    // Tree-shake unused middleware
    await removeUnusedMiddleware(distDir, options);
    // Inline small files
    inlineSmallFiles(10240); // 10 KB threshold
    // Check size limit
    const optimized = await analyze(distDir, options);
    const sizeInMb = optimized.totalSize / (1024 * 1024);
    if (sizeInMb > 1) {
        Logger.warn(`⚠️  Bundle size ${sizeInMb.toFixed(2)} MB exceeds 1 MB limit. Consider using Workers paid plan.`);
    }
    Logger.info('✅ Cloudflare Workers optimization complete');
}
/**
 * Optimize for Deno Deploy
 */
async function optimizeForDeno(distDir, options) {
    Logger.info('🦕 Optimizing for Deno Deploy...');
    // Remove Node.js-specific modules
    await removeModule(distDir, options, 'src/runtime/adapters/NodeServerAdapter.ts');
    await removeModule(distDir, options, 'src/runtime/adapters/LambdaAdapter.ts');
    // Keep Deno adapter only
    await removeModule(distDir, options, 'src/runtime/adapters/CloudflareAdapter.ts');
    // Minify
    await minifyJavaScript();
    Logger.info('✅ Deno optimization complete');
}
/**
 * Optimize for Fargate (can be larger, but faster startup)
 */
async function optimizeForFargate(distDir, options) {
    Logger.info('🐳 Optimizing for Fargate...');
    // Keep all adapters for flexibility
    // Only remove unnecessary test files
    await removeFiles(distDir, options, ['.test.ts', '.spec.ts', '.test.js', '.spec.js']);
    // Light minification
    await minifyJavaScript(false);
    Logger.info('✅ Fargate optimization complete');
}
/**
 * CLI command for bundle optimization
 */
export async function runOptimizer() {
    const platform = process.argv[2] || 'lambda';
    const targetSize = process.argv[3] ? Number.parseInt(process.argv[3], 10) : undefined;
    const optimizer = BundleOptimizer.create({
        platform,
        targetSize,
        verbose: true,
    });
    const analysis = await optimizer.optimize();
    const sizeInMb = (analysis.totalSize / (1024 * 1024)).toFixed(2);
    Logger.info(`\n✅ Optimization complete. Final size: ${sizeInMb} MB`);
}
