import { AutoReporter } from '../reporters/AutoReporter.js';
/**
 * TESTS SÉCURITÉ - API Controller
 * Tests de sécurité, injection, et validation
 */

const { describe, it, before, after } = require('node:test');
const assert = require('node:assert');
const http = require('node:http');
const { ManualServer } = require('../../lib/modes/ManualServer');

// Helper pour requêtes HTTP
function makeRequest(options, postData = null) {
  return new Promise((resolve, reject) => {
    const req = http.request(options, (res) => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => {
        try {
          const parsed = res.headers['content-type']?.includes('application/json')
            ? JSON.parse(data)
            : data;
          resolve({ statusCode: res.statusCode, headers: res.headers, data: parsed });
        } catch (e) {
          resolve({ statusCode: res.statusCode, headers: res.headers, data });
        }
      });
    });

    req.on('error', reject);
    req.setTimeout(5000, () => {
      req.destroy();
      reject(new Error('Request timeout'));
    });

    if (postData) {
      req.write(typeof postData === 'object' ? JSON.stringify(postData) : postData);
    }
    req.end();
  });
}


// Auto-Reporter Configuration
const autoReporter = new AutoReporter();

describe('API Security Tests - Tests de Sécurité', () => {
  let server;
  let baseUrl;
  const testPort = 3097;

  before(async () => {
    server = new ManualServer({ port: testPort, wsPort: 8097 });
    await server.start();
    baseUrl = `http://localhost:${testPort}`;
    console.log(`🔒 Serveur sécurité démarré sur ${baseUrl}`);
  });

  after(async () => {
    if (server) {
      await server.stop();
      console.log('🛑 Serveur sécurité arrêté');
    }
  });

  describe('🛡️ Injection et XSS', () => {
    it('should handle SQL injection attempts safely', async () => {
      const sqlPayloads = [
        "'; DROP TABLE projects; --",
        "1' OR '1'='1",
        "admin'/*",
        "1; SELECT * FROM users; --",
        "' UNION SELECT * FROM passwords --"
      ];

      for (const payload of sqlPayloads) {
        const response = await makeRequest({
          hostname: 'localhost',
          port: testPort,
          path: '/api/projects',
          method: 'POST',
          headers: { 'Content-Type': 'application/json' }
        }, {
          name: payload,
          description: 'Test injection SQL'
        });

        // Doit traiter comme chaîne normale
        assert.ok([201, 400].includes(response.statusCode));

        if (response.statusCode === 201) {
          assert.strictEqual(response.data.data.name, payload);
          assert.strictEqual(response.data.success, true);
        }
      }
    });

    it('should handle XSS payloads safely', async () => {
      const xssPayloads = [
        '<script>alert("XSS")</script>',
        'javascript:alert(1)',
        '<img src=x onerror=alert(1)>',
        '<svg onload=alert(1)>',
        '"><script>alert(document.cookie)</script>',
        "';alert(String.fromCharCode(88,83,83))//';alert(String.fromCharCode(88,83,83))//",
        '"><img src=x onerror=alert(1)>',
        '<iframe src="javascript:alert(1)"></iframe>'
      ];

      for (const payload of xssPayloads) {
        const response = await makeRequest({
          hostname: 'localhost',
          port: testPort,
          path: '/api/templates',
          method: 'POST',
          headers: { 'Content-Type': 'application/json' }
        }, {
          name: 'Test XSS',
          content: payload,
          description: 'Template avec payload XSS'
        });

        assert.ok([201, 400].includes(response.statusCode));

        if (response.statusCode === 201) {
          // Le payload doit être stocké tel quel, pas d'exécution
          assert.strictEqual(response.data.data.content, payload);
        }
      }
    });

    it('should handle path traversal attempts', async () => {
      const pathTraversals = [
        '../../../etc/passwd',
        '..\\..\\..\\windows\\system32\\config',
        '%2e%2e%2f%2e%2e%2f%2e%2e%2f',
        '....//....//....//etc/passwd',
        '..%2F..%2F..%2Fetc%2Fpasswd'
      ];

      for (const path of pathTraversals) {
        try {
          const response = await makeRequest({
            hostname: 'localhost',
            port: testPort,
            path: `/api/articles/${path}`,
            method: 'GET'
          });

          // Doit retourner 404 ou 500, pas accéder aux fichiers système
          assert.ok([404, 500].includes(response.statusCode));

        } catch (error) {
          // Erreurs d'URL malformée acceptables
          assert.ok(error.message.includes('Invalid') || error.message.includes('URI'));
        }
      }
    });
  });

  describe('🔍 Validation des Données', () => {
    it('should validate required fields strictly', async () => {
      const invalidPayloads = [
        null,
        undefined,
        {},
        { name: '' },
        { name: null },
        { name: undefined },
        { description: 'Sans nom' },
        { name: '   ' }, // Espaces seulement
      ];

      for (const payload of invalidPayloads) {
        const response = await makeRequest({
          hostname: 'localhost',
          port: testPort,
          path: '/api/projects',
          method: 'POST',
          headers: { 'Content-Type': 'application/json' }
        }, payload);

        assert.strictEqual(response.statusCode, 400);
        assert.strictEqual(response.data.success, false);
        assert.ok(response.data.error);
      }
    });

    it('should handle malformed JSON gracefully', async () => {
      const malformedJsons = [
        '{"name":}',
        '{"name":"test",}',
        '{name:"test"}', // Clés sans guillemets
        '{"name":"test"', // JSON incomplet
        'null',
        'undefined',
        '',
        '[]', // Array au lieu d'objet
        'true', // Boolean au lieu d'objet
        '{"name":"test","config":{invalid}}' // Objet imbriqué malformé
      ];

      for (const json of malformedJsons) {
        const response = await makeRequest({
          hostname: 'localhost',
          port: testPort,
          path: '/api/projects',
          method: 'POST',
          headers: { 'Content-Type': 'application/json' }
        }, json);

        assert.strictEqual(response.statusCode, 400);
        assert.strictEqual(response.data.success, false);
      }
    });

    it('should handle extreme data types', async () => {
      const extremePayloads = [
        { name: 123 }, // Number au lieu de string
        { name: true }, // Boolean au lieu de string
        { name: [] }, // Array au lieu de string
        { name: {} }, // Object au lieu de string
        { name: 'Test', description: 123 }, // Number en description
        { name: 'Test', config: 'not an object' } // String au lieu d'object
      ];

      for (const payload of extremePayloads) {
        const response = await makeRequest({
          hostname: 'localhost',
          port: testPort,
          path: '/api/projects',
          method: 'POST',
          headers: { 'Content-Type': 'application/json' }
        }, payload);

        // L'API peut soit accepter (conversion automatique) soit rejeter
        assert.ok([201, 400].includes(response.statusCode));

        if (response.statusCode === 201) {
          // Vérifier que les types sont convertis proprement
          assert.strictEqual(typeof response.data.data.name, 'string');
        }
      }
    });
  });

  describe('🌐 Protocole HTTP', () => {
    it('should handle missing Content-Type header', async () => {
      const response = await makeRequest({
        hostname: 'localhost',
        port: testPort,
        path: '/api/projects',
        method: 'POST'
        // Pas de Content-Type
      }, '{"name":"Test"}');

      // Express devrait gérer gracieusement
      assert.ok([400, 415, 500].includes(response.statusCode));
    });

    it('should handle wrong Content-Type header', async () => {
      const response = await makeRequest({
        hostname: 'localhost',
        port: testPort,
        path: '/api/projects',
        method: 'POST',
        headers: { 'Content-Type': 'text/plain' }
      }, '{"name":"Test"}');

      assert.ok([400, 415, 500].includes(response.statusCode));
    });

    it('should handle unsupported HTTP methods', async () => {
      const methods = ['PATCH', 'DELETE', 'HEAD', 'TRACE'];

      for (const method of methods) {
        try {
          const response = await makeRequest({
            hostname: 'localhost',
            port: testPort,
            path: '/api/projects',
            method
          });

          // 405 Method Not Allowed ou 404
          assert.ok([404, 405].includes(response.statusCode));
        } catch (error) {
          // Certaines méthodes peuvent être rejetées par Node.js
          assert.ok(error.message.includes('Method') || error.message.includes('method'));
        }
      }
    });

    it('should handle invalid URLs', async () => {
      const invalidPaths = [
        '/api/projects with spaces',
        '/api/проекты', // Caractères non-ASCII
        '/api/projects?' + 'x'.repeat(1000), // Query string très longue
        '/api/projects#fragment'
      ];

      for (const path of invalidPaths) {
        try {
          const response = await makeRequest({
            hostname: 'localhost',
            port: testPort,
            path,
            method: 'GET'
          });

          assert.ok([400, 404].includes(response.statusCode));
        } catch (error) {
          // Erreurs de parsing d'URL attendues
          assert.ok(error.message.includes('Invalid') || error.message.includes('URI'));
        }
      }
    });
  });

  describe('📊 Limites et Performance', () => {
    it('should handle very large payloads', async () => {
      // Payload de 100KB
      const largePayload = {
        name: 'Large Test',
        description: 'X'.repeat(100 * 1024), // 100KB
        config: {
          data: 'Y'.repeat(10000)
        }
      };

      const response = await makeRequest({
        hostname: 'localhost',
        port: testPort,
        path: '/api/projects',
        method: 'POST',
        headers: { 'Content-Type': 'application/json' }
      }, largePayload);

      // Peut accepter ou rejeter selon les limites
      assert.ok([201, 400, 413].includes(response.statusCode));
    });

    it('should handle concurrent requests safely', async () => {
      // 10 requêtes simultanées
      const promises = Array(10).fill().map((_, i) =>
        makeRequest({
          hostname: 'localhost',
          port: testPort,
          path: '/api/projects',
          method: 'POST',
          headers: { 'Content-Type': 'application/json' }
        }, {
          name: `Concurrent Project ${i}`,
          description: `Test concurrence ${i}`
        })
      );

      const results = await Promise.allSettled(promises);

      // Vérifier succès
      const successes = results.filter(r =>
        r.status === 'fulfilled' && r.value.statusCode === 201
      );

      assert.ok(successes.length > 0, 'Au moins une requête doit réussir');

      // Vérifier unicité des IDs
      const ids = successes.map(r => r.value.data.data.id);
      const uniqueIds = new Set(ids);
      assert.strictEqual(ids.length, uniqueIds.size, 'IDs doivent être uniques');
    });

    it('should maintain stability under rapid requests', async () => {
      const requests = [];

      // 20 requêtes health check rapides
      for (let i = 0; i < 20; i++) {
        requests.push(
          makeRequest({
            hostname: 'localhost',
            port: testPort,
            path: '/api/health',
            method: 'GET'
          })
        );
      }

      const results = await Promise.allSettled(requests);

      const successes = results.filter(r =>
        r.status === 'fulfilled' && r.value.statusCode === 200
      );

      // Au moins 90% doivent réussir
      const successRate = (successes.length / results.length) * 100;
      assert.ok(successRate >= 90, `Success rate too low: ${successRate}%`);
    });
  });

  describe('🔒 Headers et CORS', () => {
    it('should handle CORS preflight requests', async () => {
      const response = await makeRequest({
        hostname: 'localhost',
        port: testPort,
        path: '/api/health',
        method: 'OPTIONS',
        headers: {
          'Origin': 'http://localhost:3000',
          'Access-Control-Request-Method': 'POST'
        }
      });

      // CORS devrait être géré
      assert.ok([200, 204].includes(response.statusCode));
    });

    it('should set appropriate security headers', async () => {
      const response = await makeRequest({
        hostname: 'localhost',
        port: testPort,
        path: '/api/health',
        method: 'GET'
      });

      assert.strictEqual(response.statusCode, 200);

      // Vérifier headers de sécurité de base
      assert.ok(response.headers['content-type']);

      // Les headers de sécurité peuvent être ajoutés par Express/middleware
      console.log('📋 Headers reçus:', Object.keys(response.headers));
    });
  });
});

console.log('🔒 Tests Sécurité API - Validation injection, XSS et limites');