// url-validator.js attaches to window.URLValidator
import '@js/security/url-validator.js';

const { URLValidator } = window;

describe('URLValidator', () => {
  describe('isUnsafeScheme', () => {
    it('detects javascript: scheme', () => {
      expect(URLValidator.isUnsafeScheme('javascript:alert(1)')).toBe(true);
    });

    it('detects data: scheme', () => {
      expect(URLValidator.isUnsafeScheme('data:text/html,<h1>hi</h1>')).toBe(true);
    });

    it('is case-insensitive', () => {
      expect(URLValidator.isUnsafeScheme('JAVASCRIPT:void(0)')).toBe(true);
    });

    it('returns false for safe schemes', () => {
      expect(URLValidator.isUnsafeScheme('https://example.com')).toBe(false);
    });

    it('returns false for empty/null input', () => {
      expect(URLValidator.isUnsafeScheme('')).toBe(false);
      expect(URLValidator.isUnsafeScheme(null)).toBe(false);
    });
  });

  describe('isSafeUrl', () => {
    it('accepts https URLs', () => {
      expect(URLValidator.isSafeUrl('https://example.com')).toBe(true);
    });

    it('accepts http URLs', () => {
      expect(URLValidator.isSafeUrl('http://example.com')).toBe(true);
    });

    it('rejects javascript: URLs', () => {
      expect(URLValidator.isSafeUrl('javascript:alert(1)')).toBe(false);
    });

    it('rejects non-string input', () => {
      expect(URLValidator.isSafeUrl(null)).toBe(false);
      expect(URLValidator.isSafeUrl(123)).toBe(false);
      expect(URLValidator.isSafeUrl(undefined)).toBe(false);
    });

    it('handles fragment-only URLs based on option', () => {
      expect(URLValidator.isSafeUrl('#section', { allowFragments: true })).toBe(true);
      expect(URLValidator.isSafeUrl('#section', { allowFragments: false })).toBe(false);
    });

    it('rejects mailto by default', () => {
      expect(URLValidator.isSafeUrl('mailto:user@example.com')).toBe(false);
    });

    it('allows mailto when option is set', () => {
      expect(URLValidator.isSafeUrl('mailto:user@example.com', { allowMailto: true })).toBe(true);
    });

    it('validates trusted domains', () => {
      const opts = { trustedDomains: ['example.com'] };
      expect(URLValidator.isSafeUrl('https://example.com/page', opts)).toBe(true);
      expect(URLValidator.isSafeUrl('https://sub.example.com/page', opts)).toBe(true);
      expect(URLValidator.isSafeUrl('https://evil.com/page', opts)).toBe(false);
    });
  });

  describe('sanitizeUrl', () => {
    it('returns null for unsafe schemes', () => {
      expect(URLValidator.sanitizeUrl('javascript:alert(1)')).toBeNull();
    });

    it('adds default https scheme when missing', () => {
      expect(URLValidator.sanitizeUrl('example.com')).toBe('https://example.com');
    });

    it('preserves existing scheme', () => {
      expect(URLValidator.sanitizeUrl('http://example.com')).toBe('http://example.com');
    });

    it('returns null for empty input', () => {
      expect(URLValidator.sanitizeUrl('')).toBeNull();
      expect(URLValidator.sanitizeUrl(null)).toBeNull();
    });
  });

  describe('safeAssign', () => {
    it('allows internal paths starting with /', () => {
      const el = {};
      expect(URLValidator.safeAssign(el, 'href', '/settings')).toBe(true);
      expect(el.href).toBe('/settings');
    });

    it('allows fragment URLs', () => {
      const el = {};
      expect(URLValidator.safeAssign(el, 'href', '#top')).toBe(true);
      expect(el.href).toBe('#top');
    });

    it('allows blob URLs for downloads', () => {
      const el = {};
      const blobUrl = 'blob:http://localhost/abc-123';
      expect(URLValidator.safeAssign(el, 'href', blobUrl)).toBe(true);
      expect(el.href).toBe(blobUrl);
    });

    it('blocks unsafe external URLs', () => {
      const el = {};
      expect(URLValidator.safeAssign(el, 'href', 'javascript:alert(1)')).toBe(false);
      expect(el.href).toBeUndefined();
    });

    it('allows safe external URLs', () => {
      const el = {};
      expect(URLValidator.safeAssign(el, 'href', 'https://example.com')).toBe(true);
      expect(el.href).toBe('https://example.com');
    });
  });
});
