import { describe, it, expect, vi } from 'vitest';

/**
 * Tests unitarios para funciones de seguridad de MathFormula
 *
 * Estos tests verifican que las funciones de validación protejan contra:
 * - XSS via \href con javascript:
 * - Inyección de HTML via \htmlData
 * - Inyección de URLs peligrosas
 * - Inclusión de archivos
 * - Macros maliciosos
 */

import { validateFormula, escapeHtml } from '@/components/math/MathFormula';

describe('SECURITY: validateFormula', () => {
  describe('Fórmulas válidas (deben pasar)', () => {
    it('debe aceptar fórmulas matemáticas básicas', () => {
      const formulas = [
        '\\frac{1}{2}',
        '\\sqrt{x^2 + y^2}',
        '\\sum_{i=1}^{n} i',
        '\\int_{0}^{\\infty} e^{-x} dx',
        '\\alpha + \\beta = \\gamma',
        'E = mc^2',
        'x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}',
      ];

      formulas.forEach(formula => {
        const result = validateFormula(formula);
        expect(result.isValid).toBe(true);
        expect(result.error).toBeUndefined();
      });
    });

    it('debe aceptar fórmulas largas pero dentro del límite', () => {
      const longFormula = '\\frac{' + 'x'.repeat(100) + '}{' + 'y'.repeat(100) + '}';
      const result = validateFormula(longFormula);
      expect(result.isValid).toBe(true);
    });
  });

  describe('Detección de XSS via \\href', () => {
    it('debe bloquear \\href con javascript:', () => {
      const malicious = '\\href{javascript:alert("XSS")}{Click Me}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
      expect(result.error).toContain('Comandos no permitidos');
    });

    it('debe bloquear \\href con data:', () => {
      const malicious = '\\href{data:text/html,<script>alert("XSS")</script>}{Click}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });

    it('debe bloquear \\href con vbscript:', () => {
      const malicious = '\\href{vbscript:msgbox("XSS")}{Click}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });

    it('debe bloquear \\href con espacios alrededor', () => {
      const malicious = '\\href { javascript:alert(1) } {link}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });
  });

  describe('Detección de inyección HTML', () => {
    it('debe bloquear \\htmlData', () => {
      const malicious = '\\htmlData{key=value}{content}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });

    it('debe bloquear \\htmlStyle', () => {
      const malicious = '\\htmlStyle{color: red}{text}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });

    it('debe bloquear \\htmlId', () => {
      const malicious = '\\htmlId{myId}{content}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });

    it('debe bloquear \\htmlClass', () => {
      const malicious = '\\htmlClass{myClass}{content}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });
  });

  describe('Detección de \\url peligroso', () => {
    it('debe bloquear \\url con protocolos peligrosos', () => {
      const malicious = '\\url{javascript:alert(1)}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });
  });

  describe('Detección de inclusión de archivos', () => {
    it('debe bloquear \\input', () => {
      const malicious = '\\input{/etc/passwd}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });

    it('debe bloquear \\include', () => {
      const malicious = '\\include{malicious.tex}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });

    it('debe bloquear \\includegraphics', () => {
      const malicious = '\\includegraphics{image.png}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });

    it('debe bloquear \\verbatiminput', () => {
      const malicious = '\\verbatiminput{/etc/passwd}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });
  });

  describe('Detección de macros peligrosos', () => {
    it('debe bloquear \\def', () => {
      const malicious = '\\def\\x{malicious}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });

    it('debe bloquear \\newcommand', () => {
      const malicious = '\\newcommand{\\x}{malicious}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });

    it('debe bloquear \\renewcommand', () => {
      const malicious = '\\renewcommand{\\x}{malicious}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });

    it('debe bloquear \\let', () => {
      const malicious = '\\let\\x\\y';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });

    it('debe bloquear \\global', () => {
      const malicious = '\\global\\def\\x{malicious}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });

    it('debe bloquear \\edef', () => {
      const malicious = '\\edef\\x{malicious}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });
  });

  describe('Detección de CSS/Style injection', () => {
    it('debe bloquear \\cssId', () => {
      const malicious = '\\cssId{myId}{content}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });
  });

  describe('Detección de caracteres de control', () => {
    it('debe bloquear caracteres nulos', () => {
      const malicious = '\\frac{1}{2}\x00';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
      expect(result.error).toContain('Caracteres de control');
    });

    it('debe bloquear caracteres de control ASCII', () => {
      const malicious = '\\frac{1}{2}\x01\x02\x03';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });
  });

  describe('Detección de Unicode bidireccional', () => {
    it('debe bloquear caracteres de dirección bidireccional (CVE-2021-42574)', () => {
      const bidiChar = '\u202A'; // LRE - Left-to-Right Embedding
      const malicious = `if (isAdmin) ${bidiChar} // verificar si admin`;
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
      expect(result.error).toContain('dirección bidireccional');
    });
  });

  describe('Límite de tamaño', () => {
    it('debe bloquear fórmulas mayores a 5000 caracteres', () => {
      const longFormula = 'x'.repeat(5001);
      const result = validateFormula(longFormula);
      expect(result.isValid).toBe(false);
      expect(result.error).toContain('5000');
    });

    it('debe aceptar fórmulas exactamente de 5000 caracteres', () => {
      const exactFormula = 'x'.repeat(5000);
      const result = validateFormula(exactFormula);
      expect(result.isValid).toBe(true);
    });
  });

  describe('Casos de edge y obfuscación', () => {
    it('debe detectar \\href con case variations', () => {
      const variations = [
        '\\HREF{javascript:alert(1)}{x}',
        '\\Href{javascript:alert(1)}{x}',
        '\\hReF{javascript:alert(1)}{x}',
      ];

      variations.forEach(formula => {
        const result = validateFormula(formula);
        expect(result.isValid).toBe(false);
      });
    });

    it('debe detectar múltiples patrones peligrosos', () => {
      const malicious = '\\href{javascript:alert(1)}{x} \\htmlData{x}{y}';
      const result = validateFormula(malicious);
      expect(result.isValid).toBe(false);
    });
  });
});

describe('SECURITY: escapeHtml', () => {
  it('debe escapar caracteres HTML peligrosos', () => {
    const input = '<script>alert("XSS")</script>';
    const expected = '&lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;';
    expect(escapeHtml(input)).toBe(expected);
  });

  it('debe escapar ampersand', () => {
    expect(escapeHtml('a & b')).toBe('a &amp; b');
  });

  it('debe escapar comillas simples', () => {
    expect(escapeHtml("it's")).toBe('it&#039;s');
  });

  it('debe manejar strings vacíos', () => {
    expect(escapeHtml('')).toBe('');
  });

  it('debe manejar strings sin caracteres especiales', () => {
    expect(escapeHtml('normal text')).toBe('normal text');
  });
});
