// Frontend Debug-Server für MYP
const express = require('express');
const path = require('path');
const http = require('http');
const si = require('systeminformation');
const os = require('os');
const osUtils = require('os-utils');
const { exec } = require('child_process');
const fetch = require('node-fetch');
const socketIo = require('socket.io');

// Konfiguration
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
const PORT = 6666;
const FRONTEND_PORT = 3000;
const FRONTEND_HOST = 'localhost';
const BACKEND_HOST = 'localhost';
const BACKEND_PORT = 5000;

// View Engine einrichten
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, '../public/views'));
app.use(express.static(path.join(__dirname, '../public')));
app.use(express.json());

// Hauptseite rendern
app.get('/', async (req, res) => {
    const hostname = os.hostname();
    const networkInterfaces = os.networkInterfaces();
    const ipAddresses = {};
    
    // IP-Adressen sammeln
    Object.keys(networkInterfaces).forEach(interfaceName => {
        const interfaceInfo = networkInterfaces[interfaceName];
        const ipv4Addresses = interfaceInfo.filter(info => info.family === 'IPv4');
        if (ipv4Addresses.length > 0) {
            ipAddresses[interfaceName] = ipv4Addresses[0].address;
        }
    });
    
    // Rendere die Hauptseite mit Basisdaten
    res.render('index', {
        hostname: hostname,
        ipAddresses: ipAddresses,
        timestamp: new Date().toLocaleString('de-DE'),
    });
});

// API-Endpunkte

// Systeminformationen
app.get('/api/system', async (req, res) => {
    try {
        const [cpu, mem, osInfo, diskLayout, fsSize] = await Promise.all([
            si.cpu(),
            si.mem(),
            si.osInfo(),
            si.diskLayout(),
            si.fsSize()
        ]);
        
        const data = {
            cpu: {
                manufacturer: cpu.manufacturer,
                brand: cpu.brand,
                speed: cpu.speed,
                cores: cpu.cores,
                physicalCores: cpu.physicalCores
            },
            memory: {
                total: formatBytes(mem.total),
                free: formatBytes(mem.free),
                used: formatBytes(mem.used),
                usedPercent: Math.round(mem.used / mem.total * 100)
            },
            os: {
                platform: osInfo.platform,
                distro: osInfo.distro,
                release: osInfo.release,
                arch: osInfo.arch,
                uptime: formatUptime(os.uptime())
            },
            filesystem: fsSize.map(fs => ({
                fs: fs.fs,
                type: fs.type,
                size: formatBytes(fs.size),
                used: formatBytes(fs.used),
                available: formatBytes(fs.available),
                mount: fs.mount,
                usePercent: Math.round(fs.use)
            })),
            disks: diskLayout.map(disk => ({
                device: disk.device,
                type: disk.type,
                name: disk.name,
                size: formatBytes(disk.size)
            }))
        };
        
        res.json(data);
    } catch (error) {
        console.error('Fehler beim Abrufen der Systemdaten:', error);
        res.status(500).json({ error: 'Fehler beim Abrufen der Systemdaten' });
    }
});

// Netzwerkinformationen
app.get('/api/network', async (req, res) => {
    try {
        const [netInterfaces, netStats] = await Promise.all([
            si.networkInterfaces(),
            si.networkStats()
        ]);
        
        const dns = await getDnsServers();
        const gateway = await getDefaultGateway();
        
        const data = {
            interfaces: netInterfaces.map(iface => ({
                iface: iface.iface,
                ip4: iface.ip4,
                ip6: iface.ip6,
                mac: iface.mac,
                internal: iface.internal,
                operstate: iface.operstate,
                type: iface.type,
                speed: iface.speed,
                dhcp: iface.dhcp
            })),
            stats: netStats.map(stat => ({
                iface: stat.iface,
                rx_bytes: formatBytes(stat.rx_bytes),
                tx_bytes: formatBytes(stat.tx_bytes),
                rx_sec: formatBytes(stat.rx_sec),
                tx_sec: formatBytes(stat.tx_sec)
            })),
            dns: dns,
            gateway: gateway
        };
        
        res.json(data);
    } catch (error) {
        console.error('Fehler beim Abrufen der Netzwerkdaten:', error);
        res.status(500).json({ error: 'Fehler beim Abrufen der Netzwerkdaten' });
    }
});

// Dienststatus
app.get('/api/services', async (req, res) => {
    try {
        // Prüfen ob Frontend (Next.js) läuft
        const frontendStatus = await checkServiceStatus(FRONTEND_HOST, FRONTEND_PORT);
        
        // Prüfen ob Backend (Flask) läuft
        const backendStatus = await checkServiceStatus(BACKEND_HOST, BACKEND_PORT);
        
        // Docker-Container Status abrufen
        const containers = await getDockerContainers();
        
        const data = {
            frontend: {
                name: 'Next.js Frontend',
                status: frontendStatus ? 'online' : 'offline',
                port: FRONTEND_PORT,
                host: FRONTEND_HOST
            },
            backend: {
                name: 'Flask Backend',
                status: backendStatus ? 'online' : 'offline',
                port: BACKEND_PORT,
                host: BACKEND_HOST
            },
            docker: {
                containers: containers
            }
        };
        
        res.json(data);
    } catch (error) {
        console.error('Fehler beim Abrufen der Dienststatus:', error);
        res.status(500).json({ error: 'Fehler beim Abrufen der Dienststatus' });
    }
});

// Ping-Endpunkt für Netzwerkdiagnose
app.get('/api/ping/:host', (req, res) => {
    const host = req.params.host;
    
    // Sicherheitscheck für den Hostnamen
    if (!isValidHostname(host)) {
        return res.status(400).json({ error: 'Ungültiger Hostname oder IP-Adresse' });
    }
    
    // Ping-Befehl ausführen
    exec(`ping -n 4 ${host}`, (error, stdout, stderr) => {
        if (error) {
            return res.json({
                success: false,
                output: stderr || stdout,
                error: error.message
            });
        }
        
        res.json({
            success: true,
            output: stdout
        });
    });
});

// Traceroute-Endpunkt für Netzwerkdiagnose
app.get('/api/traceroute/:host', (req, res) => {
    const host = req.params.host;
    
    // Sicherheitscheck für den Hostnamen
    if (!isValidHostname(host)) {
        return res.status(400).json({ error: 'Ungültiger Hostname oder IP-Adresse' });
    }
    
    // Traceroute-Befehl ausführen (Windows: tracert, Unix: traceroute)
    const command = process.platform === 'win32' ? 'tracert' : 'traceroute';
    exec(`${command} ${host}`, (error, stdout, stderr) => {
        // Traceroute kann einen Nicht-Null-Exit-Code zurückgeben, selbst wenn es teilweise erfolgreich ist
        res.json({
            success: true,
            output: stdout,
            error: stderr
        });
    });
});

// DNS-Lookup-Endpunkt für Netzwerkdiagnose
app.get('/api/nslookup/:host', (req, res) => {
    const host = req.params.host;
    
    // Sicherheitscheck für den Hostnamen
    if (!isValidHostname(host)) {
        return res.status(400).json({ error: 'Ungültiger Hostname oder IP-Adresse' });
    }
    
    // NSLookup-Befehl ausführen
    exec(`nslookup ${host}`, (error, stdout, stderr) => {
        if (error) {
            return res.json({
                success: false,
                output: stderr || stdout,
                error: error.message
            });
        }
        
        res.json({
            success: true,
            output: stdout
        });
    });
});

// Echtzeit-Updates über WebSockets
io.on('connection', (socket) => {
    console.log('Neue WebSocket-Verbindung');
    
    // CPU- und Arbeitsspeichernutzung im Intervall senden
    const systemMonitorInterval = setInterval(async () => {
        try {
            const [cpu, mem] = await Promise.all([
                si.currentLoad(),
                si.mem()
            ]);
            
            socket.emit('system-stats', {
                cpu: {
                    load: Math.round(cpu.currentLoad),
                    cores: cpu.cpus.map(core => Math.round(core.load))
                },
                memory: {
                    total: mem.total,
                    used: mem.used,
                    free: mem.free,
                    usedPercent: Math.round(mem.used / mem.total * 100)
                }
            });
        } catch (error) {
            console.error('Fehler beim Senden der Systemstatistiken:', error);
        }
    }, 2000);
    
    // Netzwerkstatistiken im Intervall senden
    const networkMonitorInterval = setInterval(async () => {
        try {
            const netStats = await si.networkStats();
            
            socket.emit('network-stats', {
                stats: netStats.map(stat => ({
                    iface: stat.iface,
                    rx_bytes: stat.rx_bytes,
                    tx_bytes: stat.tx_bytes,
                    rx_sec: stat.rx_sec,
                    tx_sec: stat.tx_sec
                }))
            });
        } catch (error) {
            console.error('Fehler beim Senden der Netzwerkstatistiken:', error);
        }
    }, 2000);
    
    // Aufräumen, wenn die Verbindung getrennt wird
    socket.on('disconnect', () => {
        console.log('WebSocket-Verbindung getrennt');
        clearInterval(systemMonitorInterval);
        clearInterval(networkMonitorInterval);
    });
});

// Hilfsfunktionen

// Bytes in lesbare Größen formatieren
function formatBytes(bytes, decimals = 2) {
    if (bytes === 0) return '0 B';
    
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
    
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i];
}

// Uptime in lesbare Zeit formatieren
function formatUptime(seconds) {
    const days = Math.floor(seconds / 86400);
    const hours = Math.floor((seconds % 86400) / 3600);
    const minutes = Math.floor((seconds % 3600) / 60);
    const secs = Math.floor(seconds % 60);
    
    return `${days} Tage, ${hours} Stunden, ${minutes} Minuten, ${secs} Sekunden`;
}

// Service-Status überprüfen
async function checkServiceStatus(host, port) {
    return new Promise(resolve => {
        const socket = new (require('net').Socket)();
        
        socket.setTimeout(1000);
        
        socket.on('connect', () => {
            socket.destroy();
            resolve(true);
        });
        
        socket.on('timeout', () => {
            socket.destroy();
            resolve(false);
        });
        
        socket.on('error', () => {
            socket.destroy();
            resolve(false);
        });
        
        socket.connect(port, host);
    });
}

// Docker-Container abfragen
async function getDockerContainers() {
    return new Promise((resolve) => {
        exec('docker ps --format "{{.ID}},{{.Image}},{{.Status}},{{.Ports}},{{.Names}}"', (error, stdout) => {
            if (error) {
                resolve([]);
                return;
            }
            
            const containers = [];
            const lines = stdout.trim().split('\n');
            
            for (const line of lines) {
                if (line) {
                    const [id, image, status, ports, name] = line.split(',');
                    containers.push({ id, image, status, ports, name });
                }
            }
            
            resolve(containers);
        });
    });
}

// DNS-Server abfragen
async function getDnsServers() {
    return new Promise((resolve) => {
        if (process.platform === 'win32') {
            // Windows: DNS-Server über PowerShell abfragen
            exec('powershell.exe -Command "Get-DnsClientServerAddress -AddressFamily IPv4 | Select-Object -ExpandProperty ServerAddresses"', (error, stdout) => {
                if (error) {
                    resolve(['DNS-Server konnten nicht ermittelt werden']);
                    return;
                }
                
                const servers = stdout.trim().split('\r\n').filter(Boolean);
                resolve(servers);
            });
        } else {
            // Unix: DNS-Server aus /etc/resolv.conf lesen
            exec('cat /etc/resolv.conf | grep nameserver | cut -d " " -f 2', (error, stdout) => {
                if (error) {
                    resolve(['DNS-Server konnten nicht ermittelt werden']);
                    return;
                }
                
                const servers = stdout.trim().split('\n').filter(Boolean);
                resolve(servers);
            });
        }
    });
}

// Standard-Gateway abfragen
async function getDefaultGateway() {
    return new Promise((resolve) => {
        if (process.platform === 'win32') {
            // Windows: Gateway über PowerShell abfragen
            exec('powershell.exe -Command "Get-NetRoute -DestinationPrefix 0.0.0.0/0 | Select-Object -ExpandProperty NextHop"', (error, stdout) => {
                if (error) {
                    resolve('Gateway konnte nicht ermittelt werden');
                    return;
                }
                
                resolve(stdout.trim());
            });
        } else {
            // Unix: Gateway aus den Routentabellen lesen
            exec("ip route | grep default | awk '{print $3}'", (error, stdout) => {
                if (error) {
                    resolve('Gateway konnte nicht ermittelt werden');
                    return;
                }
                
                resolve(stdout.trim());
            });
        }
    });
}

// Validierung des Hostnamens für Sicherheit
function isValidHostname(hostname) {
    // Längenprüfung
    if (!hostname || hostname.length > 255) {
        return false;
    }
    
    // Erlaubte Hostnamen
    if (hostname === 'localhost' || hostname === '127.0.0.1') {
        return true;
    }
    
    // IPv4-Prüfung
    const ipv4Regex = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
    if (ipv4Regex.test(hostname)) {
        return true;
    }
    
    // Hostname-Prüfung
    const hostnameRegex = /^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$/;
    return hostnameRegex.test(hostname);
}

// Server starten
server.listen(PORT, () => {
    console.log(`MYP Frontend Debug-Server läuft auf http://localhost:${PORT}`);
}); 