/**
 * Unit tests for tracking utilities
 */
import { 
    extractTrackingParametersFromUrl,
    getAllTrackingParameters,
    getStoredTrackingParameters,
    storeTrackingParameters,
    clearStoredTrackingParameters,
    getTrackingParametersForWidget,
    TrackingParameters,
    TrackingConfig 
} from './tracking';

// Mock localStorage
const localStorageMock = (() => {
    let store: Record<string, string> = {};
    
    return {
        getItem: (key: string) => store[key] || null,
        setItem: (key: string, value: string) => { store[key] = value; },
        removeItem: (key: string) => { delete store[key]; },
        clear: () => { store = {}; },
        key: (index: number) => Object.keys(store)[index] || null,
        get length() { return Object.keys(store).length; }
    };
})();

Object.defineProperty(window, 'localStorage', { value: localStorageMock });

// Mock console.warn to test error handling
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});

describe('Tracking Utilities', () => {
    beforeEach(() => {
        localStorageMock.clear();
        consoleSpy.mockClear();
        jest.clearAllMocks();
    });

    describe('extractTrackingParametersFromUrl', () => {
        it('should extract UTM parameters from a valid URL', () => {
            const url = 'https://example.com?utm_source=google&utm_medium=cpc&utm_campaign=spring_sale';
            const result = extractTrackingParametersFromUrl(url);
            
            expect(result).toEqual({
                utm_source: 'google',
                utm_medium: 'cpc',
                utm_campaign: 'spring_sale'
            });
        });

        it('should extract click ID parameters', () => {
            const url = 'https://example.com?gclid=abc123&fbclid=def456';
            const result = extractTrackingParametersFromUrl(url);
            
            expect(result).toEqual({
                gclid: 'abc123',
                fbclid: 'def456'
            });
        });

        it('should extract custom tracking parameters with default prefix', () => {
            const url = 'https://example.com?track_custom=value1&track_another=value2';
            const result = extractTrackingParametersFromUrl(url);
            
            expect(result).toEqual({
                track_custom: 'value1',
                track_another: 'value2'
            });
        });

        it('should handle custom parameter prefix configuration', () => {
            const url = 'https://example.com?custom_param=value1&other_param=value2';
            const config: TrackingConfig = { customParameterPrefix: 'custom_' };
            const result = extractTrackingParametersFromUrl(url, config);
            
            expect(result).toEqual({
                custom_param: 'value1'
            });
        });

        it('should handle invalid URLs gracefully', () => {
            const result = extractTrackingParametersFromUrl('invalid-url');
            
            expect(result).toEqual({});
            expect(consoleSpy).toHaveBeenCalledWith('Invalid URL provided to tracking parameter extraction:', 'invalid-url');
        });

        it('should filter out invalid parameter values', () => {
            const url = 'https://example.com?utm_source=valid&utm_medium=<script>&utm_campaign=' + 'x'.repeat(300);
            const result = extractTrackingParametersFromUrl(url);
            
            expect(result).toEqual({
                utm_source: 'valid'
            });
        });

        it('should respect configuration flags', () => {
            const url = 'https://example.com?utm_source=google&gclid=abc123&track_custom=value';
            
            const noUtm = extractTrackingParametersFromUrl(url, { extractUtmParameters: false });
            expect(noUtm.utm_source).toBeUndefined();
            expect(noUtm.gclid).toBe('abc123');
            
            const noClickIds = extractTrackingParametersFromUrl(url, { extractClickIds: false });
            expect(noClickIds.utm_source).toBe('google');
            expect(noClickIds.gclid).toBeUndefined();
        });
    });

    describe('storeTrackingParameters and getStoredTrackingParameters', () => {
        it('should store and retrieve parameters with expiration', () => {
            const params: TrackingParameters = {
                utm_source: 'google',
                utm_medium: 'cpc'
            };
            
            storeTrackingParameters(params);
            
            const retrieved = getStoredTrackingParameters();
            expect(retrieved).toEqual(params);
        });

        it('should not retrieve expired parameters', () => {
            const params: TrackingParameters = { utm_source: 'google' };
            
            // Store with very short expiration
            storeTrackingParameters(params, { persistenceDays: 0 });
            
            // Wait a bit to ensure expiration
            const retrieved = getStoredTrackingParameters();
            expect(retrieved).toEqual({});
        });

        it('should handle malformed localStorage data', () => {
            localStorage.setItem('xa_tracking_utm_source', 'invalid-json');
            
            const result = getStoredTrackingParameters();
            expect(result).toEqual({});
            expect(localStorage.getItem('xa_tracking_utm_source')).toBeNull();
        });

        it('should not store when persistence is disabled', () => {
            const params: TrackingParameters = { utm_source: 'google' };
            
            storeTrackingParameters(params, { persistToLocalStorage: false });
            
            expect(localStorage.length).toBe(0);
        });
    });

    describe('getAllTrackingParameters', () => {
        it('should combine URL and stored parameters correctly', () => {
            // Store some parameters first
            storeTrackingParameters({ utm_source: 'stored', utm_medium: 'email' });
            
            // URL with overlapping and new parameters
            const url = 'https://example.com?utm_source=url&utm_campaign=new';
            
            const result = getAllTrackingParameters(url);
            
            // URL parameters should take precedence by default
            expect(result).toEqual({
                utm_source: 'url',
                utm_medium: 'email',
                utm_campaign: 'new'
            });
        });

        it('should prefer stored parameters when configured', () => {
            storeTrackingParameters({ utm_source: 'stored' });
            
            const url = 'https://example.com?utm_source=url';
            const config: TrackingConfig = { preferUrlParameters: false };
            
            const result = getAllTrackingParameters(url, config);
            
            expect(result.utm_source).toBe('stored');
        });

        it('should store new URL parameters automatically', () => {
            const url = 'https://example.com?utm_source=google';
            
            getAllTrackingParameters(url);
            
            const stored = getStoredTrackingParameters();
            expect(stored.utm_source).toBe('google');
        });
    });

    describe('clearStoredTrackingParameters', () => {
        it('should clear all tracking parameters from localStorage', () => {
            storeTrackingParameters({ utm_source: 'google', gclid: 'abc123' });
            localStorage.setItem('other_key', 'should_remain');
            
            clearStoredTrackingParameters();
            
            expect(getStoredTrackingParameters()).toEqual({});
            expect(localStorage.getItem('other_key')).toBe('should_remain');
        });
    });

    describe('getTrackingParametersForWidget', () => {
        it('should return parameters as string attributes', () => {
            const url = 'https://example.com?utm_source=google&utm_campaign=test';
            
            const result = getTrackingParametersForWidget(url);
            
            expect(result).toEqual({
                utm_source: 'google',
                utm_campaign: 'test'
            });
            
            // Ensure all values are strings
            Object.values(result).forEach(value => {
                expect(typeof value).toBe('string');
            });
        });

        it('should filter out empty values', () => {
            const params: TrackingParameters = {
                utm_source: 'google',
                utm_medium: '',
                utm_campaign: undefined
            };
            
            storeTrackingParameters(params);
            
            const result = getTrackingParametersForWidget();
            
            expect(result).toEqual({
                utm_source: 'google'
            });
        });
    });

    describe('Input Validation', () => {
        it('should reject parameters with dangerous characters', () => {
            const dangerousUrls = [
                'https://example.com?utm_source=<script>alert("xss")</script>',
                'https://example.com?utm_medium=javascript:alert(1)',
                'https://example.com?utm_campaign=' + '"'.repeat(10),
                'https://example.com?utm_term=' + 'x'.repeat(300) // Too long
            ];
            
            dangerousUrls.forEach(url => {
                const result = extractTrackingParametersFromUrl(url);
                Object.values(result).forEach(value => {
                    expect(value).not.toMatch(/[<>"']/);
                    expect(value.length).toBeLessThanOrEqual(255);
                });
            });
        });

        it('should reject parameters with control characters', () => {
            const url = 'https://example.com?utm_source=value%00withNullByte&utm_medium=valid';
            const result = extractTrackingParametersFromUrl(url);
            
            expect(result.utm_source).toBeUndefined();
            expect(result.utm_medium).toBe('valid');
        });
    });

    describe('Performance Optimizations', () => {
        it('should handle large numbers of parameters efficiently', () => {
            const params: Record<string, string> = {};
            for (let i = 0; i < 100; i++) {
                params[`track_param${i}`] = `value${i}`;
            }
            
            const url = 'https://example.com?' + Object.entries(params).map(([k, v]) => `${k}=${v}`).join('&');
            
            const start = performance.now();
            const result = extractTrackingParametersFromUrl(url);
            const end = performance.now();
            
            expect(Object.keys(result).length).toBe(100);
            expect(end - start).toBeLessThan(100); // Should complete in less than 100ms
        });
    });
});