/**
 * Comprehensive Unit Tests for Sanitization Utilities
 *
 * These tests ensure XSS and injection attack prevention
 */

import { describe, it, expect } from 'vitest';
import {
  sanitizeHTML,
  sanitizeURL,
  sanitizePlainText,
  containsSuspiciousContent
} from '@/lib/sanitize';

describe('sanitizeURL', () => {
  describe('Valid URLs', () => {
    it('should allow valid http URLs', () => {
      expect(sanitizeURL('http://example.com')).toBe('http://example.com');
    });

    it('should allow valid https URLs', () => {
      expect(sanitizeURL('https://example.com/path?query=value')).toBe('https://example.com/path?query=value');
    });

    it('should allow valid mailto URLs', () => {
      expect(sanitizeURL('mailto:user@example.com')).toBe('mailto:user@example.com');
    });

    it('should allow URLs with special characters in query strings', () => {
      const url = 'https://example.com/search?q=test%20query&sort=desc';
      expect(sanitizeURL(url)).toBe(url);
    });

    it('should allow URLs with fragments', () => {
      const url = 'https://example.com/page#section';
      expect(sanitizeURL(url)).toBe(url);
    });

    it('should allow URLs with authentication', () => {
      const url = 'https://user:pass@example.com/path';
      expect(sanitizeURL(url)).toBe(url);
    });

    it('should allow URLs with ports', () => {
      const url = 'https://example.com:8080/path';
      expect(sanitizeURL(url)).toBe(url);
    });
  });

  describe('Dangerous Protocols - XSS Prevention', () => {
    it('should block javascript: protocol', () => {
      expect(sanitizeURL('javascript:alert("XSS")')).toBe('#');
    });

    it('should block javascript: protocol with uppercase', () => {
      expect(sanitizeURL('JAVASCRIPT:alert("XSS")')).toBe('#');
    });

    it('should block javascript: protocol with mixed case', () => {
      expect(sanitizeURL('JaVaScRiPt:alert("XSS")')).toBe('#');
    });

    it('should block data: protocol', () => {
      expect(sanitizeURL('data:text/html,<script>alert("XSS")</script>')).toBe('#');
    });

    it('should block data: protocol with base64', () => {
      expect(sanitizeURL('data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=')).toBe('#');
    });

    it('should block vbscript: protocol', () => {
      expect(sanitizeURL('vbscript:msgbox("XSS")')).toBe('#');
    });

    it('should block file: protocol', () => {
      expect(sanitizeURL('file:///etc/passwd')).toBe('#');
    });

    it('should block ftp: protocol', () => {
      expect(sanitizeURL('ftp://example.com/file')).toBe('#');
    });
  });

  describe('Edge Cases', () => {
    it('should handle invalid URLs', () => {
      expect(sanitizeURL('not a url')).toBe('#');
      expect(sanitizeURL('')).toBe('#');
      expect(sanitizeURL('  ')).toBe('#');
    });

    it('should handle null/undefined gracefully', () => {
      expect(sanitizeURL(null as any)).toBe('#');
      expect(sanitizeURL(undefined as any)).toBe('#');
    });

    it('should handle malformed URLs', () => {
      expect(sanitizeURL('http://')).toBe('#');
      expect(sanitizeURL('https://')).toBe('#');
      expect(sanitizeURL('://')).toBe('#');
    });

    it('should handle URLs with only protocol', () => {
      expect(sanitizeURL('http:')).toBe('#');
      expect(sanitizeURL('https:')).toBe('#');
    });

    it('should handle relative URLs', () => {
      expect(sanitizeURL('/path/to/page')).toBe('#');
      expect(sanitizeURL('./relative')).toBe('#');
      expect(sanitizeURL('../parent')).toBe('#');
    });

    it('should handle URLs with whitespace (URL constructor allows it)', () => {
      // Note: URL constructor successfully parses URLs with surrounding whitespace
      const result = sanitizeURL(' https://example.com ');
      // Either returns the URL as-is or we could trim it first
      expect(result).toBe(' https://example.com ');
    });

    it('should handle empty or whitespace-only strings', () => {
      expect(sanitizeURL('')).toBe('#');
      expect(sanitizeURL('   ')).toBe('#');
      expect(sanitizeURL('\n\t')).toBe('#');
    });
  });
});

describe('sanitizePlainText', () => {
  describe('HTML Entity Escaping', () => {
    it('should escape script tags', () => {
      expect(sanitizePlainText('<script>alert("XSS")</script>'))
        .toBe('&lt;script&gt;alert(&quot;XSS&quot;)&lt;&#x2F;script&gt;');
    });

    it('should escape ampersands', () => {
      expect(sanitizePlainText('Tom & Jerry')).toBe('Tom &amp; Jerry');
    });

    it('should escape double quotes', () => {
      expect(sanitizePlainText('"Hello"')).toContain('&quot;');
    });

    it('should escape single quotes', () => {
      expect(sanitizePlainText("'World'")).toContain('&#x27;');
    });

    it('should escape less than symbols', () => {
      expect(sanitizePlainText('5 < 10')).toBe('5 &lt; 10');
    });

    it('should escape greater than symbols', () => {
      expect(sanitizePlainText('10 > 5')).toBe('10 &gt; 5');
    });

    it('should escape forward slashes', () => {
      expect(sanitizePlainText('path/to/file')).toBe('path&#x2F;to&#x2F;file');
    });

    it('should escape all special characters together', () => {
      const input = '<div class="test">&copy;</div>';
      const output = sanitizePlainText(input);
      expect(output).not.toContain('<');
      expect(output).not.toContain('>');
      expect(output).not.toContain('"');
    });
  });

  describe('XSS Attack Vectors', () => {
    it('should neutralize img onerror attacks', () => {
      const attack = '<img src=x onerror="alert(1)">';
      const result = sanitizePlainText(attack);
      // The escaped version will contain the text 'onerror' but in safe escaped form
      expect(result).toContain('&lt;img');
      expect(result).toContain('&quot;');
      // Ensure no executable HTML/script tags remain
      expect(result).not.toContain('<img');
      expect(result).not.toContain('>');
      expect(result).not.toContain('<');
    });

    it('should neutralize iframe attacks', () => {
      const attack = '<iframe src="javascript:alert(1)"></iframe>';
      const result = sanitizePlainText(attack);
      expect(result).not.toContain('<iframe');
      expect(result).toContain('&lt;iframe');
    });

    it('should neutralize event handler attacks', () => {
      const attack = '<button onclick="alert(1)">Click</button>';
      const result = sanitizePlainText(attack);
      expect(result).toContain('&lt;button');
      expect(result).toContain('&quot;');
      // Ensure no executable HTML/script tags remain
      expect(result).not.toContain('<button');
      expect(result).not.toContain('>');
      expect(result).not.toContain('<');
    });

    it('should neutralize SVG-based XSS', () => {
      const attack = '<svg onload="alert(1)"></svg>';
      const result = sanitizePlainText(attack);
      expect(result).toContain('&lt;svg');
      expect(result).toContain('&quot;');
      // Ensure no executable HTML/script tags remain
      expect(result).not.toContain('<svg');
      expect(result).not.toContain('>');
      expect(result).not.toContain('<');
    });
  });

  describe('Safe Content', () => {
    it('should handle plain text without changes', () => {
      expect(sanitizePlainText('Hello World')).toBe('Hello World');
    });

    it('should handle empty strings', () => {
      expect(sanitizePlainText('')).toBe('');
    });

    it('should handle text with numbers', () => {
      expect(sanitizePlainText('Price: $19.99')).toBe('Price: $19.99');
    });

    it('should handle text with newlines', () => {
      expect(sanitizePlainText('Line 1\nLine 2')).toBe('Line 1\nLine 2');
    });

    it('should handle Unicode characters', () => {
      expect(sanitizePlainText('Hello 世界 🌍')).toBe('Hello 世界 🌍');
    });
  });

  describe('Edge Cases', () => {
    it('should handle null/undefined', () => {
      expect(sanitizePlainText(null as any)).toBe('');
      expect(sanitizePlainText(undefined as any)).toBe('');
    });

    it('should handle numbers', () => {
      expect(sanitizePlainText(123 as any)).toBe('');
    });

    it('should handle objects', () => {
      expect(sanitizePlainText({} as any)).toBe('');
    });

    it('should handle very long strings', () => {
      const longString = 'a'.repeat(10000);
      const result = sanitizePlainText(longString);
      expect(result.length).toBe(10000);
    });
  });
});

describe('containsSuspiciousContent', () => {
  describe('Script Tag Detection', () => {
    it('should detect script tags', () => {
      expect(containsSuspiciousContent('<script>alert(1)</script>')).toBe(true);
    });

    it('should detect uppercase script tags', () => {
      expect(containsSuspiciousContent('<SCRIPT>alert(1)</SCRIPT>')).toBe(true);
    });

    it('should detect mixed case script tags', () => {
      expect(containsSuspiciousContent('<ScRiPt>alert(1)</ScRiPt>')).toBe(true);
    });

    it('should detect script tags with attributes', () => {
      expect(containsSuspiciousContent('<script src="evil.js"></script>')).toBe(true);
    });

    it('should detect self-closing script tags', () => {
      expect(containsSuspiciousContent('<script />')).toBe(true);
    });
  });

  describe('JavaScript Protocol Detection', () => {
    it('should detect javascript: protocol', () => {
      expect(containsSuspiciousContent('javascript:alert(1)')).toBe(true);
    });

    it('should detect uppercase JavaScript protocol', () => {
      expect(containsSuspiciousContent('JAVASCRIPT:alert(1)')).toBe(true);
    });

    it('should detect mixed case JavaScript protocol', () => {
      expect(containsSuspiciousContent('JaVaScRiPt:alert(1)')).toBe(true);
    });
  });

  describe('Event Handler Detection', () => {
    it('should detect onerror event handler', () => {
      expect(containsSuspiciousContent('<img onerror="alert(1)">')).toBe(true);
    });

    it('should detect onclick event handler', () => {
      expect(containsSuspiciousContent('<div onclick="alert(1)">')).toBe(true);
    });

    it('should detect onload event handler', () => {
      expect(containsSuspiciousContent('<body onload="alert(1)">')).toBe(true);
    });

    it('should detect onmouseover event handler', () => {
      expect(containsSuspiciousContent('<div onmouseover="alert(1)">')).toBe(true);
    });

    it('should detect event handlers with different whitespace', () => {
      expect(containsSuspiciousContent('<img onerror = "alert(1)">')).toBe(true);
      expect(containsSuspiciousContent('<img onerror= "alert(1)">')).toBe(true);
    });
  });

  describe('Dangerous Tag Detection', () => {
    it('should detect iframes', () => {
      expect(containsSuspiciousContent('<iframe src="evil.com"></iframe>')).toBe(true);
    });

    it('should detect object tags', () => {
      expect(containsSuspiciousContent('<object data="evil.swf"></object>')).toBe(true);
    });

    it('should detect embed tags', () => {
      expect(containsSuspiciousContent('<embed src="evil.swf">')).toBe(true);
    });

    it('should detect data URIs with HTML', () => {
      expect(containsSuspiciousContent('data:text/html,<script>alert(1)</script>')).toBe(true);
    });
  });

  describe('Safe Content', () => {
    it('should not flag safe content', () => {
      expect(containsSuspiciousContent('This is a safe message')).toBe(false);
    });

    it('should not flag email addresses', () => {
      expect(containsSuspiciousContent('Email: user@example.com')).toBe(false);
    });

    it('should not flag safe HTML-like text', () => {
      expect(containsSuspiciousContent('The tag <p> is safe')).toBe(false);
    });

    it('should not flag normal URLs', () => {
      expect(containsSuspiciousContent('https://example.com')).toBe(false);
    });

    it('should not flag markdown-like syntax', () => {
      expect(containsSuspiciousContent('[Link](https://example.com)')).toBe(false);
    });
  });

  describe('Edge Cases', () => {
    it('should handle null/undefined', () => {
      expect(containsSuspiciousContent(null as any)).toBe(false);
      expect(containsSuspiciousContent(undefined as any)).toBe(false);
    });

    it('should handle empty strings', () => {
      expect(containsSuspiciousContent('')).toBe(false);
    });

    it('should handle numbers', () => {
      expect(containsSuspiciousContent(123 as any)).toBe(false);
    });

    it('should handle objects', () => {
      expect(containsSuspiciousContent({} as any)).toBe(false);
    });
  });
});

describe('sanitizeHTML', () => {
  describe('Safe Tags', () => {
    it('should allow paragraph tags', () => {
      const html = '<p>Hello world</p>';
      const result = sanitizeHTML(html);
      expect(result).toContain('<p>');
      expect(result).toContain('Hello world');
    });

    it('should allow strong tags', () => {
      const html = '<p>Hello <strong>world</strong></p>';
      const result = sanitizeHTML(html);
      expect(result).toContain('<strong>');
    });

    it('should allow emphasis tags', () => {
      const html = '<p>Hello <em>world</em></p>';
      const result = sanitizeHTML(html);
      expect(result).toContain('<em>');
    });

    it('should allow underline tags', () => {
      const html = '<p>Hello <u>world</u></p>';
      const result = sanitizeHTML(html);
      expect(result).toContain('<u>');
    });

    it('should allow lists', () => {
      const html = '<ul><li>Item 1</li><li>Item 2</li></ul>';
      const result = sanitizeHTML(html);
      expect(result).toContain('<ul>');
      expect(result).toContain('<li>');
    });

    it('should allow ordered lists', () => {
      const html = '<ol><li>First</li><li>Second</li></ol>';
      const result = sanitizeHTML(html);
      expect(result).toContain('<ol>');
      expect(result).toContain('<li>');
    });

    it('should allow line breaks', () => {
      const html = 'Line 1<br>Line 2';
      const result = sanitizeHTML(html);
      expect(result).toContain('<br>');
    });
  });

  describe('Dangerous Content Removal', () => {
    it('should remove script tags', () => {
      const html = '<p>Hello</p><script>alert("XSS")</script>';
      const result = sanitizeHTML(html);
      expect(result).not.toContain('<script>');
      expect(result).toContain('<p>');
    });

    it('should remove event handlers', () => {
      const html = '<p onclick="alert(1)">Click me</p>';
      const result = sanitizeHTML(html);
      expect(result).not.toContain('onclick');
      expect(result).toContain('Click me');
    });

    it('should remove style tags', () => {
      const html = '<p>Text</p><style>body { display: none; }</style>';
      const result = sanitizeHTML(html);
      expect(result).not.toContain('<style>');
    });

    it('should remove iframe tags', () => {
      const html = '<p>Text</p><iframe src="evil.com"></iframe>';
      const result = sanitizeHTML(html);
      expect(result).not.toContain('<iframe>');
    });

    it('should remove object tags', () => {
      const html = '<p>Text</p><object data="evil.swf"></object>';
      const result = sanitizeHTML(html);
      expect(result).not.toContain('<object>');
    });

    it('should remove embed tags', () => {
      const html = '<p>Text</p><embed src="evil.swf">';
      const result = sanitizeHTML(html);
      expect(result).not.toContain('<embed>');
    });
  });

  describe('Link Handling', () => {
    it('should allow safe links', () => {
      const html = '<a href="https://example.com">Link</a>';
      const result = sanitizeHTML(html);
      expect(result).toContain('href');
      expect(result).toContain('https://example.com');
    });

    it('should allow target attribute', () => {
      const html = '<a href="https://example.com" target="_blank">Link</a>';
      const result = sanitizeHTML(html);
      expect(result).toContain('target');
    });

    it('should allow rel attribute', () => {
      const html = '<a href="https://example.com" rel="noopener">Link</a>';
      const result = sanitizeHTML(html);
      expect(result).toContain('rel');
    });

    it('should sanitize javascript: in links', () => {
      const html = '<a href="javascript:alert(1)">Click</a>';
      const result = sanitizeHTML(html);
      // DOMPurify should remove or neutralize the href
      expect(result).not.toContain('javascript:');
    });
  });

  describe('Edge Cases', () => {
    it('should handle empty strings', () => {
      expect(sanitizeHTML('')).toBe('');
    });

    it('should handle plain text', () => {
      const result = sanitizeHTML('Plain text');
      expect(result).toBe('Plain text');
    });

    it('should handle deeply nested tags', () => {
      const html = '<p><strong><em><u>Text</u></em></strong></p>';
      const result = sanitizeHTML(html);
      expect(result).toContain('<p>');
      expect(result).toContain('<strong>');
      expect(result).toContain('<em>');
      expect(result).toContain('<u>');
    });

    it('should handle malformed HTML', () => {
      const html = '<p>Unclosed tag';
      const result = sanitizeHTML(html);
      expect(result).toBeTruthy();
    });
  });
});
