/**
 * Service Bundler - Creates optimized, independent microservice packages
 * Target: Each service < 1MB for serverless deployment
 */
import { Logger } from '../config/logger.js';
import fs from '../node-singletons/fs.js';
import * as path from '../node-singletons/path.js';
/**
 * Service bundler for independent deployment
 */
export const ServiceBundler = Object.freeze(() => {
    let instance;
    return {
        getInstance() {
            instance ??= this.create();
            return instance;
        },
        async bundleService(config) {
            return this.getInstance().bundleService(config);
        },
        async bundleAll(domain, services, outputDir) {
            return this.getInstance().bundleAll(domain, services, outputDir);
        },
        async createServiceImage(serviceName, domain, registry) {
            return this.getInstance().createServiceImage(serviceName, domain, registry);
        },
        /**
         * Create a new service bundler instance
         */
        create() {
            return {
                /**
                 * Bundle a single microservice
                 */
                async bundleService(config) {
                    return runBundleService(config);
                },
                /**
                 * Bundle multiple services
                 */
                async bundleAll(domain, services, outputDir = 'dist/services') {
                    return runBundleAll(domain, services, outputDir, async (c) => this.bundleService(c));
                },
                /**
                 * Create Docker image for service
                 */
                async createServiceImage(serviceName, domain, registry = 'localhost:5000') {
                    return runCreateServiceImage(serviceName, domain, registry);
                },
            };
        },
    };
})();
/**
 * Run bundle for a single service
 */
// eslint-disable-next-line @typescript-eslint/require-await
async function runBundleService(config) {
    const { serviceName, domain, outputDir, targetSize = 1 } = config;
    Logger.info(`\n📦 Bundling service: ${serviceName}`);
    const serviceDir = `services/${domain}/${serviceName}`;
    const bundleDir = path.join(outputDir, `${domain}-${serviceName}`);
    prepareBundleDirectory(bundleDir);
    const { totalSize, fileCount } = copyServiceFiles(serviceDir, bundleDir);
    const metadata = generateBundleMetadata(serviceName, domain, totalSize, fileCount, targetSize);
    fs.writeFileSync(path.join(bundleDir, 'bundle.json'), JSON.stringify(metadata, null, 2));
    logBundleSummary(totalSize, fileCount, targetSize);
    return {
        serviceName,
        sizeBytes: totalSize,
        sizeMB: Number.parseFloat((totalSize / (1024 * 1024)).toFixed(2)),
        files: fileCount,
        location: bundleDir,
        optimized: totalSize < targetSize * 1024 * 1024,
    };
}
/**
 * Run bundle for all services
 */
async function runBundleAll(domain, services, outputDir, bundleServiceFn) {
    Logger.info(`\n📦 Bundling ${services.length} services for domain: ${domain}`);
    const promises = services.map(async (service) => {
        try {
            return await bundleServiceFn({
                serviceName: service,
                domain,
                outputDir,
            });
        }
        catch (error) {
            Logger.error(`Failed to bundle ${service}:`, error);
            return null;
        }
    });
    const allResults = await Promise.all(promises);
    const results = allResults.filter((r) => r !== null);
    // Print summary
    printBundleSummary(results);
    return results;
}
/**
 * Run create service image
 */
// eslint-disable-next-line @typescript-eslint/require-await
async function runCreateServiceImage(serviceName, domain, registry) {
    const serviceDir = `services/${domain}/${serviceName}`;
    const imageTag = `${registry}/${domain}-${serviceName}:latest`;
    Logger.info(`\n🐳 Creating Docker image: ${imageTag}`);
    // Generate minimal Dockerfile
    const dockerfile = generateDockerfile(serviceName);
    fs.writeFileSync(path.join(serviceDir, 'Dockerfile'), dockerfile);
    Logger.info(`  ✓ Dockerfile created at ${serviceDir}/Dockerfile`);
    Logger.info(`  To build: docker build -t ${imageTag} ${serviceDir}`);
    return imageTag;
}
/**
 * Copy directory recursively
 */
function copyDirectory(src, dest) {
    if (fs.existsSync(dest) === false) {
        fs.mkdirSync(dest, { recursive: true });
    }
    const files = fs.readdirSync(src);
    for (const file of files) {
        const srcPath = path.join(src, file);
        const destPath = path.join(dest, file);
        const stats = fs.statSync(srcPath);
        if (stats.isDirectory() === true) {
            copyDirectory(srcPath, destPath);
        }
        else {
            fs.copyFileSync(srcPath, destPath);
        }
    }
}
/**
 * Get directory size
 */
function getDirectorySize(dir) {
    if (fs.existsSync(dir) === false)
        return 0;
    let size = 0;
    const files = fs.readdirSync(dir);
    for (const file of files) {
        const filePath = path.join(dir, file);
        const stats = fs.statSync(filePath);
        if (stats.isDirectory() === true) {
            size += getDirectorySize(filePath);
        }
        else {
            size += stats.size;
        }
    }
    return size;
}
/**
 * Count files in directory
 */
function countFiles(dir) {
    if (fs.existsSync(dir) === false)
        return 0;
    let count = 0;
    const files = fs.readdirSync(dir);
    for (const file of files) {
        const filePath = path.join(dir, file);
        const stats = fs.statSync(filePath);
        if (stats.isDirectory() === true) {
            count += countFiles(filePath);
        }
        else {
            count++;
        }
    }
    return count;
}
/**
 * Prepare bundle directory
 */
function prepareBundleDirectory(bundleDir) {
    if (fs.existsSync(bundleDir) === true) {
        fs.rmSync(bundleDir, { recursive: true });
    }
    fs.mkdirSync(bundleDir, { recursive: true });
}
/**
 * Copy service files to bundle directory
 */
function copyServiceFiles(serviceDir, bundleDir) {
    const filesToCopy = [
        { src: `${serviceDir}/dist`, dest: `${bundleDir}/dist` },
        { src: `${serviceDir}/package.json`, dest: `${bundleDir}/package.json` },
        {
            src: `${serviceDir}/service.config.json`,
            dest: `${bundleDir}/service.config.json`,
        },
        { src: `${serviceDir}/.env.example`, dest: `${bundleDir}/.env.example` },
    ];
    let totalSize = 0;
    let fileCount = 0;
    for (const { src, dest } of filesToCopy) {
        if (fs.existsSync(src) === true) {
            const stats = fs.statSync(src);
            if (stats.isDirectory() === true) {
                copyDirectory(src, dest);
                totalSize += getDirectorySize(dest);
                fileCount += countFiles(dest);
            }
            else {
                fs.copyFileSync(src, dest);
                totalSize += stats.size;
                fileCount++;
            }
        }
    }
    return { totalSize, fileCount };
}
/**
 * Generate bundle metadata
 */
function generateBundleMetadata(serviceName, domain, totalSize, fileCount, targetSize) {
    return {
        service: serviceName,
        domain,
        timestamp: new Date().toISOString(),
        sizeBytes: totalSize,
        files: fileCount,
        optimized: totalSize < targetSize * 1024 * 1024,
    };
}
/**
 * Log bundle summary
 */
function logBundleSummary(totalSize, fileCount, targetSize) {
    const sizeMB = (totalSize / (1024 * 1024)).toFixed(2);
    Logger.info(`  ✓ Size: ${sizeMB} MB (${fileCount} files)`);
    if (totalSize > targetSize * 1024 * 1024) {
        Logger.warn(`  ⚠️  Bundle exceeds ${targetSize} MB target - consider optimizing`);
    }
}
/**
 * Print bundle summary
 */
function printBundleSummary(results) {
    Logger.info('\n📊 Bundle Summary');
    Logger.info('━'.repeat(60));
    let totalSize = 0;
    let totalFiles = 0;
    for (const result of results) {
        const status = result.optimized === true ? '✅' : '⚠️ ';
        Logger.info(`${status} ${result.serviceName.padEnd(20)} ${result.sizeMB.toFixed(2)} MB (${result.files} files)`);
        totalSize += result.sizeBytes;
        totalFiles += result.files;
    }
    Logger.info('━'.repeat(60));
    const totalMB = (totalSize / (1024 * 1024)).toFixed(2);
    Logger.info(`Total: ${totalMB} MB across ${totalFiles} files\n`);
}
/**
 * Generate minimal Dockerfile
 */
function generateDockerfile(serviceName) {
    return String.raw `FROM node:20-alpine

WORKDIR /app

COPY package.json ./
RUN npm install --production --ignore-scripts

COPY dist ./dist

ENV NODE_ENV=production
ENV SERVICE_NAME=${serviceName}

HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
  CMD node -e "require('node:http').get('http://localhost:7777/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"

EXPOSE 3000

CMD ["node", "dist/src/Kernel.js"]
`;
}
/**
 * CLI command to bundle services
 */
export async function bundleServices(domain, services) {
    const serviceList = services.split(',').map((s) => s.trim());
    const results = await ServiceBundler.getInstance().bundleAll(domain, serviceList, 'dist/services');
    const allOptimized = results.every((r) => r.optimized);
    if (allOptimized) {
        Logger.info('✅ All services optimized for serverless deployment!');
    }
    else {
        Logger.info('⚠️  Some services exceed 1MB target - consider further optimization');
    }
}
export const bundleService = async (config) => ServiceBundler.getInstance().bundleService(config);
export const bundleAll = async (domain, services, outputDir) => ServiceBundler.getInstance().bundleAll(domain, services, outputDir);
export const createServiceImage = async (serviceName, domain, registry) => ServiceBundler.getInstance().createServiceImage(serviceName, domain, registry);
export default ServiceBundler;
