/**
 * Rate Limiting Middleware für Management Portal API
 * 
 * Features:
 * - IP-basiertes Rate-Limiting: 10 Requests pro Stunde
 * - Brute-Force-Schutz: 24h Block nach 20 fehlgeschlagenen Token-Validierungen
 * - In-Memory-Storage (für Production: Redis empfohlen)
 */

// In-Memory Storage für Rate-Limiting
const requestCounts = new Map(); // IP -> { count, resetTime }
const blockedIPs = new Map();    // IP -> { reason, blockedUntil, failedAttempts }

// Konfiguration
const RATE_LIMIT = {
    MAX_REQUESTS_PER_HOUR: process.env.NODE_ENV === 'production' ? 10 : 100, // 100 für Dev, 10 für Production
    WINDOW_MS: 60 * 60 * 1000, // 1 Stunde
    BRUTE_FORCE_THRESHOLD: 20,
    BLOCK_DURATION_MS: 24 * 60 * 60 * 1000 // 24 Stunden
};

// Public Upload Rate Limiting (strengere Limits für öffentliche Uploads)
const PUBLIC_UPLOAD_LIMIT = {
    MAX_UPLOADS_PER_HOUR: parseInt(process.env.PUBLIC_UPLOAD_RATE_LIMIT || '20', 10),
    WINDOW_MS: parseInt(process.env.PUBLIC_UPLOAD_RATE_WINDOW || '3600000', 10) // 1 Stunde
};

// In-Memory Storage für Public Upload Rate-Limiting
const publicUploadCounts = new Map(); // IP -> { count, resetTime }

/**
 * Extrahiere Client-IP aus Request
 */
function getClientIP(req) {
    return req.headers['x-forwarded-for']?.split(',')[0].trim() || 
           req.headers['x-real-ip'] || 
           req.connection.remoteAddress || 
           req.socket.remoteAddress ||
           'unknown';
}

/**
 * Rate-Limiting Middleware
 * Begrenzt Requests pro IP auf 10 pro Stunde
 */
function rateLimitMiddleware(req, res, next) {
    const clientIP = getClientIP(req);
    const now = Date.now();

    // Prüfe ob IP blockiert ist
    if (blockedIPs.has(clientIP)) {
        const blockInfo = blockedIPs.get(clientIP);
        
        if (now < blockInfo.blockedUntil) {
            const remainingTime = Math.ceil((blockInfo.blockedUntil - now) / 1000 / 60 / 60);
            return res.status(429).json({
                success: false,
                error: 'IP temporarily blocked',
                message: `Your IP has been blocked due to ${blockInfo.reason}. Try again in ${remainingTime} hours.`,
                blockedUntil: new Date(blockInfo.blockedUntil).toISOString()
            });
        } else {
            // Block abgelaufen - entfernen
            blockedIPs.delete(clientIP);
        }
    }

    // Hole oder erstelle Request-Counter für IP
    let requestInfo = requestCounts.get(clientIP);

    if (!requestInfo || now > requestInfo.resetTime) {
        // Neues Zeitfenster
        requestInfo = {
            count: 0,
            resetTime: now + RATE_LIMIT.WINDOW_MS,
            failedAttempts: requestInfo?.failedAttempts || 0
        };
        requestCounts.set(clientIP, requestInfo);
    }

    // Prüfe Rate-Limit
    if (requestInfo.count >= RATE_LIMIT.MAX_REQUESTS_PER_HOUR) {
        const resetIn = Math.ceil((requestInfo.resetTime - now) / 1000 / 60);
        return res.status(429).json({
            success: false,
            error: 'Rate limit exceeded',
            message: `Too many requests. You can make ${RATE_LIMIT.MAX_REQUESTS_PER_HOUR} requests per hour. Try again in ${resetIn} minutes.`,
            limit: RATE_LIMIT.MAX_REQUESTS_PER_HOUR,
            resetIn: resetIn
        });
    }

    // Erhöhe Counter
    requestInfo.count++;
    requestCounts.set(clientIP, requestInfo);

    // Request durchlassen
    next();
}

/**
 * Registriere fehlgeschlagene Token-Validierung
 * Wird von Management-Routes aufgerufen bei 404 Token-Errors
 */
function recordFailedTokenValidation(req) {
    const clientIP = getClientIP(req);
    const now = Date.now();

    let requestInfo = requestCounts.get(clientIP);
    if (!requestInfo) {
        requestInfo = {
            count: 0,
            resetTime: now + RATE_LIMIT.WINDOW_MS,
            failedAttempts: 0
        };
    }

    requestInfo.failedAttempts++;
    requestCounts.set(clientIP, requestInfo);

    // Prüfe Brute-Force-Schwelle
    if (requestInfo.failedAttempts >= RATE_LIMIT.BRUTE_FORCE_THRESHOLD) {
        blockedIPs.set(clientIP, {
            reason: 'brute force attack (multiple failed token validations)',
            blockedUntil: now + RATE_LIMIT.BLOCK_DURATION_MS,
            failedAttempts: requestInfo.failedAttempts
        });

        console.warn(`⚠️  IP ${clientIP} blocked for 24h due to ${requestInfo.failedAttempts} failed token validations`);
        
        // Reset failed attempts
        requestInfo.failedAttempts = 0;
        requestCounts.set(clientIP, requestInfo);
    }
}

/**
 * Cleanup-Funktion: Entfernt abgelaufene Einträge
 * Sollte periodisch aufgerufen werden (z.B. alle 1h)
 */
function cleanupExpiredEntries() {
    const now = Date.now();
    let cleaned = 0;

    // Cleanup requestCounts
    for (const [ip, info] of requestCounts.entries()) {
        if (now > info.resetTime && info.failedAttempts === 0) {
            requestCounts.delete(ip);
            cleaned++;
        }
    }

    // Cleanup blockedIPs
    for (const [ip, blockInfo] of blockedIPs.entries()) {
        if (now > blockInfo.blockedUntil) {
            blockedIPs.delete(ip);
            cleaned++;
        }
    }

    if (cleaned > 0) {
        console.log(`🧹 Rate-Limiter: Cleaned up ${cleaned} expired entries`);
    }
}

// Auto-Cleanup alle 60 Minuten
setInterval(cleanupExpiredEntries, 60 * 60 * 1000);

/**
 * Statistiken für Monitoring
 */
function getStatistics() {
    return {
        activeIPs: requestCounts.size,
        blockedIPs: blockedIPs.size,
        blockedIPsList: Array.from(blockedIPs.entries()).map(([ip, info]) => ({
            ip,
            reason: info.reason,
            blockedUntil: new Date(info.blockedUntil).toISOString(),
            failedAttempts: info.failedAttempts
        })),
        publicUploadActiveIPs: publicUploadCounts.size
    };
}

/**
 * Public Upload Rate Limiter Middleware
 * Strengere Limits für öffentliche Uploads (20 pro Stunde pro IP)
 * Wird nur auf public Host angewendet
 */
function publicUploadLimiter(req, res, next) {
    // Skip wenn nicht public Host oder Feature disabled
    if (!req.isPublicHost || process.env.NODE_ENV === 'test') {
        return next();
    }

    const clientIP = getClientIP(req);
    const now = Date.now();

    // Hole oder erstelle Upload-Counter für IP
    let uploadInfo = publicUploadCounts.get(clientIP);

    if (!uploadInfo || now > uploadInfo.resetTime) {
        // Neues Zeitfenster
        uploadInfo = {
            count: 0,
            resetTime: now + PUBLIC_UPLOAD_LIMIT.WINDOW_MS
        };
        publicUploadCounts.set(clientIP, uploadInfo);
    }

    // Prüfe Upload-Limit
    if (uploadInfo.count >= PUBLIC_UPLOAD_LIMIT.MAX_UPLOADS_PER_HOUR) {
        const resetIn = Math.ceil((uploadInfo.resetTime - now) / 1000 / 60);
        console.warn(`🚫 Public upload limit exceeded for IP ${clientIP} (${uploadInfo.count}/${PUBLIC_UPLOAD_LIMIT.MAX_UPLOADS_PER_HOUR})`);
        
        return res.status(429).json({
            success: false,
            error: 'Upload limit exceeded',
            message: `You have reached the maximum of ${PUBLIC_UPLOAD_LIMIT.MAX_UPLOADS_PER_HOUR} uploads per hour. Please try again in ${resetIn} minutes.`,
            limit: PUBLIC_UPLOAD_LIMIT.MAX_UPLOADS_PER_HOUR,
            resetIn: resetIn
        });
    }

    // Erhöhe Upload-Counter
    uploadInfo.count++;
    publicUploadCounts.set(clientIP, uploadInfo);

    // Request durchlassen
    next();
}

module.exports = {
    rateLimitMiddleware,
    recordFailedTokenValidation,
    cleanupExpiredEntries,
    getStatistics,
    publicUploadLimiter
};
