/**
 * Utility functions for handling UTM parameters and tracking data
 * Following industry standards for marketing attribution tracking
 */

export interface TrackingParameters {
    // Standard UTM parameters (Google Analytics standard)
    utm_source?: string;      // Traffic source (e.g., "google", "facebook", "newsletter")
    utm_medium?: string;      // Marketing medium (e.g., "cpc", "email", "social")
    utm_campaign?: string;    // Campaign name (e.g., "spring_sale", "product_launch")
    utm_term?: string;        // Paid keywords (for paid search)
    utm_content?: string;     // Ad content (for A/B testing different ads)
    
    // Platform-specific click IDs
    gclid?: string;          // Google Click ID (Google Ads auto-tagging)
    fbclid?: string;         // Facebook Click ID
    msclkid?: string;        // Microsoft Click ID (Bing Ads)
    
    // Custom tracking parameters with configurable prefix
    [key: `track_${string}`]: string;
}

/**
 * Configuration for tracking parameter extraction
 */
export interface TrackingConfig {
    /**
     * Whether to extract UTM parameters from the URL
     * @default true
     */
    extractUtmParameters?: boolean;
    
    /**
     * Whether to extract platform-specific click IDs
     * @default true
     */
    extractClickIds?: boolean;
    
    /**
     * Custom parameter prefix to look for (in addition to utm_)
     * @default "track_"
     */
    customParameterPrefix?: string;
    
    /**
     * Whether to persist parameters to localStorage
     * @default true
     */
    persistToLocalStorage?: boolean;
    
    /**
     * How long to persist parameters in localStorage (in days)
     * @default 30
     */
    persistenceDays?: number;
    
    /**
     * Whether to prefer URL parameters over stored ones when both exist
     * @default true
     */
    preferUrlParameters?: boolean;
}

const DEFAULT_CONFIG: Required<TrackingConfig> = {
    extractUtmParameters: true,
    extractClickIds: true,
    customParameterPrefix: "track_",
    persistToLocalStorage: true,
    persistenceDays: 30,
    preferUrlParameters: true,
};

/**
 * Standard UTM parameter names
 */
const UTM_PARAMETERS = [
    'utm_source',
    'utm_medium', 
    'utm_campaign',
    'utm_term',
    'utm_content'
] as const;

/**
 * Platform-specific click ID parameter names
 */
const CLICK_ID_PARAMETERS = [
    'gclid',    // Google Ads
    'fbclid',   // Facebook
    'msclkid'   // Microsoft/Bing
] as const;

/**
 * Storage key prefix for localStorage
 */
const STORAGE_KEY_PREFIX = 'xa_tracking_';

/**
 * Validates if a parameter value is safe to store and use
 */
function isValidParameterValue(value: string): boolean {
    // Check length limit and basic XSS prevention
    return value.length <= 255 && !/[<>"'\x00-\x1f]/.test(value);
}

/**
 * Extracts tracking parameters from a URL
 */
export function extractTrackingParametersFromUrl(
    url: string = window.location.href,
    config: TrackingConfig = {}
): TrackingParameters {
    const finalConfig = { ...DEFAULT_CONFIG, ...config };
    
    let urlObj: URL;
    try {
        urlObj = new URL(url);
    } catch (error) {
        console.warn('Invalid URL provided to tracking parameter extraction:', url);
        return {};
    }
    
    const searchParams = urlObj.searchParams;
    const parameters: TrackingParameters = {};

    // Extract UTM parameters
    if (finalConfig.extractUtmParameters) {
        UTM_PARAMETERS.forEach(param => {
            const value = searchParams.get(param);
            if (value && isValidParameterValue(value)) {
                parameters[param] = value;
            }
        });
    }

    // Extract platform-specific click IDs
    if (finalConfig.extractClickIds) {
        CLICK_ID_PARAMETERS.forEach(param => {
            const value = searchParams.get(param);
            if (value && isValidParameterValue(value)) {
                parameters[param] = value;
            }
        });
    }

    // Extract custom tracking parameters
    if (finalConfig.customParameterPrefix) {
        searchParams.forEach((value, key) => {
            if (key.startsWith(finalConfig.customParameterPrefix) && isValidParameterValue(value)) {
                // Type-safe assignment for custom parameters
                (parameters as Record<string, string>)[key] = value;
            }
        });
    }

    return parameters;
}

/**
 * Gets tracking parameters from localStorage
 */
export function getStoredTrackingParameters(): TrackingParameters {
    const parameters: TrackingParameters = {};
    
    // Check each potential parameter in localStorage
    [...UTM_PARAMETERS, ...CLICK_ID_PARAMETERS].forEach(param => {
        const stored = localStorage.getItem(`${STORAGE_KEY_PREFIX}${param}`);
        if (stored) {
            try {
                const data = JSON.parse(stored);
                // Check if data hasn't expired
                if (data.expires > Date.now()) {
                    parameters[param] = data.value;
                }
            } catch (e) {
                // Clean up invalid data
                localStorage.removeItem(`${STORAGE_KEY_PREFIX}${param}`);
            }
        }
    });

    // Check for custom tracking parameters
    Object.keys(localStorage).forEach(key => {
        if (key.startsWith(`${STORAGE_KEY_PREFIX}track_`)) {
            try {
                const data = JSON.parse(localStorage.getItem(key) || '{}');
                if (data.expires > Date.now()) {
                    const paramName = key.replace(STORAGE_KEY_PREFIX, '') as keyof TrackingParameters;
                    parameters[paramName] = data.value;
                }
            } catch (e) {
                localStorage.removeItem(key);
            }
        }
    });

    return parameters;
}

/**
 * Stores tracking parameters in localStorage with expiration
 */
export function storeTrackingParameters(
    parameters: TrackingParameters,
    config: TrackingConfig = {}
): void {
    const finalConfig = { ...DEFAULT_CONFIG, ...config };
    
    if (!finalConfig.persistToLocalStorage) {
        return;
    }

    const expirationTime = Date.now() + (finalConfig.persistenceDays * 24 * 60 * 60 * 1000);

    Object.entries(parameters).forEach(([key, value]) => {
        if (value) {
            const data = {
                value,
                expires: expirationTime,
                stored: Date.now()
            };
            localStorage.setItem(`${STORAGE_KEY_PREFIX}${key}`, JSON.stringify(data));
        }
    });
}

/**
 * Main function to get all tracking parameters, combining URL and stored parameters
 */
export function getAllTrackingParameters(
    url: string = window.location.href,
    config: TrackingConfig = {}
): TrackingParameters {
    const finalConfig = { ...DEFAULT_CONFIG, ...config };
    
    const urlParameters = extractTrackingParametersFromUrl(url, config);
    const storedParameters = getStoredTrackingParameters();

    // Combine parameters, with URL parameters taking precedence if configured
    const parameters: TrackingParameters = finalConfig.preferUrlParameters
        ? { ...storedParameters, ...urlParameters }
        : { ...urlParameters, ...storedParameters };

    // Store any new URL parameters
    if (Object.keys(urlParameters).length > 0) {
        storeTrackingParameters(urlParameters, config);
    }

    return parameters;
}

/**
 * Clears all stored tracking parameters
 */
export function clearStoredTrackingParameters(): void {
    Object.keys(localStorage).forEach(key => {
        if (key.startsWith(STORAGE_KEY_PREFIX)) {
            localStorage.removeItem(key);
        }
    });
}

/**
 * Gets tracking parameters formatted for form widget attributes
 */
export function getTrackingParametersForWidget(
    url: string = window.location.href,
    config: TrackingConfig = {}
): Record<string, string> {
    const parameters = getAllTrackingParameters(url, config);
    const attributes: Record<string, string> = {};

    // Convert to string attributes for widget consumption
    Object.entries(parameters).forEach(([key, value]) => {
        if (value) {
            attributes[key] = String(value);
        }
    });

    return attributes;
}