#!/usr/bin/env node

/**
 * Asset-Komprimierungsskript für MYP Backend
 * Komprimiert CSS und JS-Dateien mit gzip für bessere Performance
 */

const fs = require('fs');
const path = require('path');
const zlib = require('zlib');
const { promisify } = require('util');

const gzip = promisify(zlib.gzip);
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
const stat = promisify(fs.stat);

// Konfiguration
const STATIC_DIR = path.join(__dirname, '..', 'static');
const COMPRESSION_LEVEL = 9; // Maximale Komprimierung

// Dateierweiterungen die komprimiert werden sollen
const COMPRESSIBLE_EXTENSIONS = ['.css', '.js', '.html', '.svg', '.json'];

// Mindestgröße für Komprimierung (in Bytes)
const MIN_SIZE_FOR_COMPRESSION = 1024; // 1KB

/**
 * Komprimiert eine einzelne Datei
 */
async function compressFile(filePath) {
    try {
        const stats = await stat(filePath);
        
        // Überspringe kleine Dateien
        if (stats.size < MIN_SIZE_FOR_COMPRESSION) {
            console.log(`⏭️  Überspringe ${filePath} (zu klein: ${stats.size} bytes)`);
            return;
        }

        const data = await readFile(filePath);
        const compressed = await gzip(data, { level: COMPRESSION_LEVEL });
        
        const gzPath = filePath + '.gz';
        await writeFile(gzPath, compressed);
        
        const compressionRatio = ((stats.size - compressed.length) / stats.size * 100).toFixed(1);
        const originalSize = formatBytes(stats.size);
        const compressedSize = formatBytes(compressed.length);
        
        console.log(`✅ ${path.basename(filePath)}: ${originalSize} → ${compressedSize} (-${compressionRatio}%)`);
    } catch (error) {
        console.error(`❌ Fehler beim Komprimieren von ${filePath}:`, error.message);
    }
}

/**
 * Durchsucht Verzeichnis rekursiv nach komprimierbaren Dateien
 */
async function findCompressibleFiles(dir) {
    const files = [];
    
    try {
        const entries = await fs.promises.readdir(dir, { withFileTypes: true });
        
        for (const entry of entries) {
            const fullPath = path.join(dir, entry.name);
            
            if (entry.isDirectory()) {
                // Rekursiv in Unterverzeichnisse
                const subFiles = await findCompressibleFiles(fullPath);
                files.push(...subFiles);
            } else if (entry.isFile()) {
                const ext = path.extname(entry.name);
                
                // Überspringe bereits komprimierte Dateien
                if (entry.name.endsWith('.gz')) {
                    continue;
                }
                
                // Prüfe ob Dateiart komprimierbar ist
                if (COMPRESSIBLE_EXTENSIONS.includes(ext)) {
                    files.push(fullPath);
                }
            }
        }
    } catch (error) {
        console.warn(`⚠️  Kann Verzeichnis nicht lesen: ${dir} - ${error.message}`);
    }
    
    return files;
}

/**
 * Formatiert Byte-Größen lesbar
 */
function formatBytes(bytes) {
    if (bytes === 0) return '0 B';
    
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    
    return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
}

/**
 * Bereinigt alte .gz-Dateien
 */
async function cleanupOldGzFiles(dir) {
    try {
        const entries = await fs.promises.readdir(dir, { withFileTypes: true });
        let cleaned = 0;
        
        for (const entry of entries) {
            const fullPath = path.join(dir, entry.name);
            
            if (entry.isDirectory()) {
                cleaned += await cleanupOldGzFiles(fullPath);
            } else if (entry.name.endsWith('.gz')) {
                const originalPath = fullPath.slice(0, -3); // Entferne .gz
                
                try {
                    await stat(originalPath);
                    // Original existiert, behalte .gz
                } catch (error) {
                    // Original existiert nicht, lösche .gz
                    await fs.promises.unlink(fullPath);
                    console.log(`🗑️  Bereinigt: ${entry.name}`);
                    cleaned++;
                }
            }
        }
        
        return cleaned;
    } catch (error) {
        console.warn(`⚠️  Fehler beim Bereinigen von ${dir}: ${error.message}`);
        return 0;
    }
}

/**
 * Hauptfunktion
 */
async function main() {
    console.log('🚀 MYP Asset-Komprimierung gestartet...\n');
    
    const startTime = Date.now();
    
    // Bereinige alte .gz-Dateien
    console.log('🧹 Bereinige alte komprimierte Dateien...');
    const cleanedFiles = await cleanupOldGzFiles(STATIC_DIR);
    if (cleanedFiles > 0) {
        console.log(`✅ ${cleanedFiles} verwaiste .gz-Dateien bereinigt\n`);
    } else {
        console.log('✅ Keine Bereinigung erforderlich\n');
    }
    
    // Finde alle komprimierbaren Dateien
    console.log('🔍 Suche nach komprimierbaren Dateien...');
    const files = await findCompressibleFiles(STATIC_DIR);
    console.log(`📁 ${files.length} komprimierbare Dateien gefunden\n`);
    
    if (files.length === 0) {
        console.log('ℹ️  Keine Dateien zum Komprimieren gefunden.');
        return;
    }
    
    // Komprimiere alle Dateien parallel (aber begrenzt)
    console.log('📦 Komprimiere Dateien...');
    const BATCH_SIZE = 10; // Maximale parallele Verarbeitung
    
    let totalOriginalSize = 0;
    let totalCompressedSize = 0;
    let processedFiles = 0;
    
    for (let i = 0; i < files.length; i += BATCH_SIZE) {
        const batch = files.slice(i, i + BATCH_SIZE);
        const promises = batch.map(async (file) => {
            try {
                const stats = await stat(file);
                if (stats.size >= MIN_SIZE_FOR_COMPRESSION) {
                    const data = await readFile(file);
                    const compressed = await gzip(data, { level: COMPRESSION_LEVEL });
                    await writeFile(file + '.gz', compressed);
                    
                    totalOriginalSize += stats.size;
                    totalCompressedSize += compressed.length;
                    processedFiles++;
                    
                    return {
                        file: path.basename(file),
                        original: stats.size,
                        compressed: compressed.length
                    };
                }
            } catch (error) {
                console.error(`❌ ${path.basename(file)}: ${error.message}`);
                return null;
            }
        });
        
        const results = await Promise.all(promises);
        
        // Zeige Fortschritt für diese Batch
        results.filter(r => r).forEach(result => {
            const ratio = ((result.original - result.compressed) / result.original * 100).toFixed(1);
            console.log(`✅ ${result.file}: ${formatBytes(result.original)} → ${formatBytes(result.compressed)} (-${ratio}%)`);
        });
    }
    
    const endTime = Date.now();
    const duration = ((endTime - startTime) / 1000).toFixed(2);
    
    console.log('\n🎉 Komprimierung abgeschlossen!');
    console.log(`📊 Statistiken:`);
    console.log(`   • Verarbeitete Dateien: ${processedFiles}`);
    console.log(`   • Originalgröße: ${formatBytes(totalOriginalSize)}`);
    console.log(`   • Komprimierte Größe: ${formatBytes(totalCompressedSize)}`);
    
    if (totalOriginalSize > 0) {
        const totalSavings = ((totalOriginalSize - totalCompressedSize) / totalOriginalSize * 100).toFixed(1);
        console.log(`   • Gesamt-Einsparung: ${formatBytes(totalOriginalSize - totalCompressedSize)} (-${totalSavings}%)`);
    }
    
    console.log(`   • Dauer: ${duration}s`);
    console.log('\n💡 Tipp: Verwenden Sie gzip-komprimierte Dateien mit Flask-Compress für optimale Performance!');
}

// Führe Skript nur aus wenn direkt aufgerufen
if (require.main === module) {
    main().catch(error => {
        console.error('💥 Kritischer Fehler:', error);
        process.exit(1);
    });
}

module.exports = { compressFile, findCompressibleFiles, cleanupOldGzFiles }; 