/**
 * JavaScript tests for safe-fetch.js
 * Run with: npm test tests/infrastructure_tests/test_safe_fetch.test.js
 *
 * Tests the safeFetch function which wraps fetch() with URL validation.
 */

// Mock fetch
global.fetch = jest.fn(() => Promise.resolve({ ok: true }));

// Mock SafeLogger (used by url-validator.js)
global.SafeLogger = {
    warn: jest.fn(),
    error: jest.fn(),
    info: jest.fn(),
    debug: jest.fn()
};

// Mock window.location for URL parsing
global.window = {
    location: {
        href: 'http://localhost:5000/',
        origin: 'http://localhost:5000'
    }
};

// Load the URL validator first (dependency) and expose as global
const URLValidator = require('../../src/local_deep_research/web/static/js/security/url-validator.js');
global.URLValidator = URLValidator;

// Load safe-fetch module and expose as global
const { safeFetch } = require('../../src/local_deep_research/web/static/js/security/safe-fetch.js');
global.safeFetch = safeFetch;

describe('safeFetch', () => {
    beforeEach(() => {
        jest.clearAllMocks();
        global.fetch.mockClear();
    });

    describe('Internal URLs (starting with /)', () => {
        test('should allow internal API URLs without validation', async () => {
            await safeFetch('/api/settings');
            expect(global.fetch).toHaveBeenCalledWith('/api/settings', {});
        });

        test('should allow internal page URLs', async () => {
            await safeFetch('/history');
            expect(global.fetch).toHaveBeenCalledWith('/history', {});
        });

        test('should pass options through for internal URLs', async () => {
            const options = { method: 'POST', body: JSON.stringify({ key: 'value' }) };
            await safeFetch('/api/data', options);
            expect(global.fetch).toHaveBeenCalledWith('/api/data', options);
        });

        test('should allow deep internal paths', async () => {
            await safeFetch('/api/v1/research/123/status');
            expect(global.fetch).toHaveBeenCalledWith('/api/v1/research/123/status', {});
        });
    });

    describe('External URLs with safe schemes', () => {
        test('should allow https URLs', async () => {
            await safeFetch('https://example.com/api');
            expect(global.fetch).toHaveBeenCalledWith('https://example.com/api', {});
        });

        test('should allow http URLs', async () => {
            await safeFetch('http://example.com/api');
            expect(global.fetch).toHaveBeenCalledWith('http://example.com/api', {});
        });
    });

    describe('Unsafe URLs', () => {
        test('should block javascript: URLs', async () => {
            await expect(safeFetch('javascript:alert(1)')).rejects.toThrow('Blocked unsafe URL');
            expect(global.fetch).not.toHaveBeenCalled();
        });

        test('should block data: URLs', async () => {
            await expect(safeFetch('data:text/html,<script>alert(1)</script>')).rejects.toThrow('Blocked unsafe URL');
            expect(global.fetch).not.toHaveBeenCalled();
        });

        test('should block vbscript: URLs', async () => {
            await expect(safeFetch('vbscript:msgbox(1)')).rejects.toThrow('Blocked unsafe URL');
            expect(global.fetch).not.toHaveBeenCalled();
        });

        test('should block javascript: with mixed case', async () => {
            await expect(safeFetch('JavaScript:alert(1)')).rejects.toThrow('Blocked unsafe URL');
            expect(global.fetch).not.toHaveBeenCalled();
        });

        test('should block javascript: with leading whitespace', async () => {
            await expect(safeFetch('  javascript:alert(1)')).rejects.toThrow('Blocked unsafe URL');
            expect(global.fetch).not.toHaveBeenCalled();
        });
    });

    describe('Edge cases', () => {
        test('should handle empty options object', async () => {
            await safeFetch('/api/test', {});
            expect(global.fetch).toHaveBeenCalledWith('/api/test', {});
        });

        test('should handle URLs with query parameters', async () => {
            await safeFetch('/api/search?q=test&limit=10');
            expect(global.fetch).toHaveBeenCalledWith('/api/search?q=test&limit=10', {});
        });

        test('should handle URLs with fragments', async () => {
            await safeFetch('/page#section');
            expect(global.fetch).toHaveBeenCalledWith('/page#section', {});
        });
    });
});

describe('URLValidator integration', () => {
    test('URLValidator should be available globally', () => {
        expect(global.URLValidator).toBeDefined();
        expect(typeof global.URLValidator.isSafeUrl).toBe('function');
    });

    test('URLValidator.isSafeUrl should validate https URLs', () => {
        expect(global.URLValidator.isSafeUrl('https://example.com')).toBe(true);
    });

    test('URLValidator.isSafeUrl should reject javascript URLs', () => {
        expect(global.URLValidator.isSafeUrl('javascript:alert(1)')).toBe(false);
    });

    test('URLValidator.isUnsafeScheme should detect unsafe schemes', () => {
        expect(global.URLValidator.isUnsafeScheme('javascript:void(0)')).toBe(true);
        expect(global.URLValidator.isUnsafeScheme('data:text/html,test')).toBe(true);
        expect(global.URLValidator.isUnsafeScheme('https://example.com')).toBe(false);
    });
});
