import { test, describe, beforeEach, afterEach } from 'node:test';
import { strict as assert } from 'node:assert';
import { createMockDOM, cleanupMockDOM, createLogCapture, delay } from '../utils/test-helpers.js';

// Tests de stress et edge cases d'intégration
describe('Tests de Stress et Edge Cases d\'Intégration', () => {
    let logCapture;

    // Helper pour faire des requêtes HTTP réelles
    async function makeRequest(path, options = {}) {
        const fetch = (await import('node-fetch')).default;
        const url = `http://localhost:8083${path}`;

        try {
            const response = await fetch(url, {
                timeout: options.timeout || 5000,
                ...options
            });

            return {
                ok: response.ok,
                status: response.status,
                data: response.ok ? await response.text() : await response.text(),
                headers: Object.fromEntries(response.headers.entries())
            };
        } catch (error) {
            return {
                ok: false,
                error: error.message,
                timeout: error.code === 'TIMEOUT'
            };
        }
    }

    beforeEach(() => {
        createMockDOM();
        logCapture = createLogCapture();
    });

    afterEach(() => {
        logCapture.restore();
        cleanupMockDOM();
    });

    describe('Tests de Charge du Proxy', () => {
        test('devrait gérer 100 requêtes simultanées', async () => {
            const concurrentRequests = 100;
            const promises = [];

            console.log(`🔥 Lancement de ${concurrentRequests} requêtes simultanées...`);

            for (let i = 0; i < concurrentRequests; i++) {
                promises.push(makeRequest('/do-proxy/sbs-level-7-8-new.json', {
                    timeout: 10000 // Timeout plus long pour la charge
                }));
            }

            const startTime = Date.now();
            const results = await Promise.allSettled(promises);
            const endTime = Date.now();

            const successful = results.filter(r =>
                r.status === 'fulfilled' && r.value.ok
            ).length;

            const failed = results.filter(r =>
                r.status === 'rejected' || !r.value.ok
            ).length;

            const timeouts = results.filter(r =>
                r.status === 'fulfilled' && r.value.timeout
            ).length;

            console.log(`✅ Résultats: ${successful} succès, ${failed} échecs, ${timeouts} timeouts`);
            console.log(`⏱️  Temps total: ${endTime - startTime}ms`);
            console.log(`📊 Moyenne: ${(endTime - startTime) / concurrentRequests}ms par requête`);

            // Au moins 80% de succès attendu
            assert.ok(successful >= concurrentRequests * 0.8,
                `Taux de succès trop faible: ${successful}/${concurrentRequests}`);

            // Temps total raisonnable (moins de 30 secondes)
            assert.ok(endTime - startTime < 30000,
                `Temps total trop long: ${endTime - startTime}ms`);
        });

        test('devrait gérer les requêtes avec différentes tailles de payload', async () => {
            const requests = [
                '/do-proxy/sbs-level-7-8-new.json',      // ~9KB
                '/do-proxy/english-class-demo.json',      // ~12KB
                '/do-proxy/nonexistent-small.json',       // 404
                '/do-proxy/nonexistent-large.json'        // 404
            ];

            const results = await Promise.all(
                requests.map(path => makeRequest(path))
            );

            // Vérifier que les différentes tailles sont gérées
            const validResponses = results.filter(r => r.ok);
            assert.ok(validResponses.length >= 2, 'Should handle multiple payload sizes');

            // Vérifier que les 404 sont correctement gérées
            const notFoundResponses = results.filter(r => !r.ok && r.status === 404);
            assert.ok(notFoundResponses.length >= 0, 'Should handle 404s gracefully');
        });

        test('devrait maintenir les performances sous charge continue', async () => {
            const duration = 10000; // 10 secondes
            const requestInterval = 100; // Une requête toutes les 100ms

            console.log('🔄 Test de charge continue pendant 10 secondes...');

            const startTime = Date.now();
            const results = [];
            let requestCount = 0;

            while (Date.now() - startTime < duration) {
                const requestStart = Date.now();

                try {
                    const result = await makeRequest('/do-proxy/sbs-level-7-8-new.json', {
                        timeout: 2000
                    });

                    const requestTime = Date.now() - requestStart;
                    results.push({
                        success: result.ok,
                        time: requestTime,
                        requestNumber: requestCount
                    });

                } catch (error) {
                    results.push({
                        success: false,
                        time: Date.now() - requestStart,
                        error: error.message,
                        requestNumber: requestCount
                    });
                }

                requestCount++;
                await delay(requestInterval);
            }

            const totalTime = Date.now() - startTime;
            const successfulRequests = results.filter(r => r.success).length;
            const avgResponseTime = results
                .filter(r => r.success)
                .reduce((sum, r) => sum + r.time, 0) / successfulRequests;

            console.log(`📊 Résultats charge continue:`);
            console.log(`   • Durée totale: ${totalTime}ms`);
            console.log(`   • Requêtes totales: ${requestCount}`);
            console.log(`   • Requêtes réussies: ${successfulRequests}`);
            console.log(`   • Taux de succès: ${(successfulRequests/requestCount*100).toFixed(1)}%`);
            console.log(`   • Temps de réponse moyen: ${avgResponseTime.toFixed(1)}ms`);

            // Assertions de performance
            assert.ok(successfulRequests / requestCount >= 0.9, 'Au moins 90% de succès');
            assert.ok(avgResponseTime < 1000, 'Temps de réponse moyen < 1s');
        });
    });

    describe('Tests de Robustesse Réseau', () => {
        test('devrait gérer les interruptions de connexion', async () => {
            // Simuler des requêtes rapides qui pourraient être interrompues
            const rapidRequests = Array(20).fill().map((_, i) =>
                makeRequest('/do-proxy/sbs-level-7-8-new.json', {
                    timeout: 100 + i * 10 // Timeouts variables
                })
            );

            const results = await Promise.allSettled(rapidRequests);

            // Compter les différents types de résultats
            const successful = results.filter(r =>
                r.status === 'fulfilled' && r.value.ok
            ).length;

            const timeouts = results.filter(r =>
                r.status === 'fulfilled' && r.value.timeout
            ).length;

            console.log(`🌐 Requêtes rapides: ${successful} succès, ${timeouts} timeouts`);

            // Même avec des timeouts courts, certaines requêtes devraient passer
            assert.ok(successful > 0, 'Au moins quelques requêtes devraient réussir');
        });

        test('devrait récupérer après des erreurs temporaires', async () => {
            // Tester la récupération en faisant plusieurs tentatives
            let consecutiveSuccesses = 0;
            const maxAttempts = 10;

            for (let i = 0; i < maxAttempts; i++) {
                const result = await makeRequest('/do-proxy/sbs-level-7-8-new.json');

                if (result.ok) {
                    consecutiveSuccesses++;
                } else {
                    consecutiveSuccesses = 0;
                    console.log(`⚠️ Échec temporaire (tentative ${i + 1}): ${result.error || result.status}`);
                }

                // Si on a 3 succès consécutifs, le système s'est récupéré
                if (consecutiveSuccesses >= 3) {
                    console.log(`✅ Récupération réussie après ${i + 1} tentatives`);
                    break;
                }

                await delay(500); // Attendre entre les tentatives
            }

            assert.ok(consecutiveSuccesses >= 3, 'Le système devrait pouvoir se récupérer');
        });
    });

    describe('Tests de Limites Mémoire', () => {
        test('devrait gérer des réponses très volumineuses', async () => {
            // Tester avec un fichier qui pourrait être volumineux
            const result = await makeRequest('/do-proxy/sbs-level-7-8-new.json');

            if (result.ok) {
                const dataSize = result.data.length;
                console.log(`📏 Taille des données reçues: ${(dataSize/1024).toFixed(1)}KB`);

                // Vérifier que même de gros fichiers sont gérés
                assert.ok(dataSize > 0, 'Should receive data');
                assert.ok(dataSize < 10 * 1024 * 1024, 'Should not exceed 10MB'); // Limite raisonnable

                // Vérifier que c'est du JSON valide
                try {
                    JSON.parse(result.data);
                } catch (error) {
                    assert.fail('Les données devraient être du JSON valide');
                }
            }
        });

        test('devrait nettoyer la mémoire entre les requêtes', async () => {
            // Faire plusieurs requêtes séquentielles pour tester le nettoyage
            const requests = 20;
            let maxMemoryUsage = 0;

            for (let i = 0; i < requests; i++) {
                const result = await makeRequest('/do-proxy/sbs-level-7-8-new.json');

                if (result.ok) {
                    const currentUsage = process.memoryUsage().heapUsed;
                    maxMemoryUsage = Math.max(maxMemoryUsage, currentUsage);
                }

                // Petite pause pour permettre le garbage collection
                if (i % 5 === 0) {
                    await delay(10);
                }
            }

            console.log(`🧠 Utilisation mémoire maximale: ${(maxMemoryUsage/1024/1024).toFixed(1)}MB`);

            // La mémoire ne devrait pas exploser (limite arbitraire de 500MB)
            assert.ok(maxMemoryUsage < 500 * 1024 * 1024, 'Memory usage should stay reasonable');
        });
    });

    describe('Tests de Sécurité Edge Cases', () => {
        test('devrait rejeter les tentatives d\'injection de path', async () => {
            const maliciousPaths = [
                '/do-proxy/../../../etc/passwd',
                '/do-proxy/..\\\\..\\\\..\\\\windows\\\\system32\\\\config\\\\sam',
                '/do-proxy/%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd',
                '/do-proxy/....//....//....//etc//passwd',
                '/do-proxy/\\x2e\\x2e\\x2f\\x2e\\x2e\\x2f\\x2e\\x2e\\x2fetc\\x2fpasswd'
            ];

            for (const path of maliciousPaths) {
                const result = await makeRequest(path);

                // Ces requêtes devraient échouer (404 ou 403)
                assert.ok(!result.ok || result.status === 404 || result.status === 403,
                    `Path injection should be blocked: ${path}`);

                // Ne devrait pas retourner de contenu système
                if (result.data) {
                    assert.ok(!result.data.includes('root:'), 'Should not expose system files');
                    assert.ok(!result.data.includes('Administrator'), 'Should not expose system files');
                }
            }
        });

        test('devrait limiter la taille des requêtes', async () => {
            // Tenter d'envoyer une requête avec des headers très longs
            const longHeader = 'x'.repeat(10000);

            try {
                const result = await makeRequest('/do-proxy/test.json', {
                    headers: {
                        'X-Very-Long-Header': longHeader
                    }
                });

                // La requête peut échouer (ce qui est bien) ou réussir en ignorant l'header
                if (!result.ok) {
                    assert.ok([400, 413, 431].includes(result.status),
                        'Should reject oversized headers appropriately');
                }
            } catch (error) {
                // C'est acceptable que ça lève une exception
                assert.ok(error.message.includes('header') ||
                         error.message.includes('too large') ||
                         error.message.includes('ECONNRESET'));
            }
        });

        test('devrait gérer les caractères spéciaux dans les URLs', async () => {
            const specialCharPaths = [
                '/do-proxy/file with spaces.json',
                '/do-proxy/file%20with%20encoded%20spaces.json',
                '/do-proxy/file+with+plus.json',
                '/do-proxy/файл-на-русском.json',
                '/do-proxy/文件中文.json',
                '/do-proxy/file&with&ampersands.json',
                '/do-proxy/file?with?questions.json'
            ];

            for (const path of specialCharPaths) {
                const result = await makeRequest(path);

                // Ces requêtes peuvent échouer (404) mais ne devraient pas crasher le serveur
                assert.ok(typeof result.status === 'number',
                    `Should handle special characters gracefully: ${path}`);

                // Le serveur devrait toujours répondre
                assert.ok(result.status >= 200 && result.status < 600,
                    'Should return valid HTTP status');
            }
        });
    });

    describe('Tests de Compatibilité', () => {
        test('devrait fonctionner avec différents User-Agents', async () => {
            const userAgents = [
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/91.0.4472.124',
                'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Safari/537.36',
                'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Firefox/89.0',
                'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) Mobile/15E148',
                'curl/7.68.0',
                'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)',
                'PostmanRuntime/7.28.0'
            ];

            for (const userAgent of userAgents) {
                const result = await makeRequest('/do-proxy/sbs-level-7-8-new.json', {
                    headers: {
                        'User-Agent': userAgent
                    }
                });

                // Toutes les requêtes valides devraient fonctionner indépendamment du User-Agent
                if (result.ok) {
                    assert.ok(result.data.length > 0,
                        `Should work with User-Agent: ${userAgent.substring(0, 50)}...`);
                }
            }
        });

        test('devrait gérer différents types de Accept headers', async () => {
            const acceptHeaders = [
                'application/json',
                'application/json, text/plain, */*',
                'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                '*/*',
                'application/json;charset=utf-8',
                'text/plain'
            ];

            for (const accept of acceptHeaders) {
                const result = await makeRequest('/do-proxy/sbs-level-7-8-new.json', {
                    headers: {
                        'Accept': accept
                    }
                });

                // Les requêtes JSON devraient fonctionner avec tous les Accept headers raisonnables
                if (result.ok) {
                    try {
                        JSON.parse(result.data);
                    } catch (error) {
                        assert.fail(`Should return valid JSON regardless of Accept header: ${accept}`);
                    }
                }
            }
        });
    });

    describe('Tests de Récupération d\'Erreurs', () => {
        test('devrait récupérer après des erreurs en série', async () => {
            // Faire plusieurs requêtes vers des ressources inexistantes
            const badRequests = Array(5).fill().map((_, i) =>
                makeRequest(`/do-proxy/nonexistent-${i}.json`)
            );

            const badResults = await Promise.all(badRequests);

            // Toutes devraient échouer
            assert.ok(badResults.every(r => !r.ok), 'Bad requests should fail');

            // Puis tester qu'une bonne requête fonctionne encore
            await delay(100); // Petite pause

            const goodResult = await makeRequest('/do-proxy/sbs-level-7-8-new.json');

            if (goodResult.ok) {
                assert.ok(goodResult.data.length > 0,
                    'Le serveur devrait récupérer après des erreurs');
            } else {
                console.log('⚠️ Le serveur peut être temporairement indisponible');
            }
        });

        test('devrait maintenir les connexions après des timeouts', async () => {
            // Faire des requêtes avec des timeouts très courts
            const shortTimeoutRequests = Array(3).fill().map(() =>
                makeRequest('/do-proxy/sbs-level-7-8-new.json', {
                    timeout: 1 // 1ms - presque garanti de timeout
                })
            );

            await Promise.allSettled(shortTimeoutRequests);

            // Puis tester avec un timeout normal
            const normalResult = await makeRequest('/do-proxy/sbs-level-7-8-new.json', {
                timeout: 5000
            });

            if (normalResult.ok) {
                assert.ok(normalResult.data.length > 0,
                    'Les connexions devraient être maintenues après des timeouts');
            }
        });
    });
});