"use strict";
/**
 * Standalone beacon script - this file is bundled and minified
 * to create the production analytics.js served at /_agentuity/webanalytics/analytics.js
 *
 * This is the single source of truth for the beacon logic.
 */
const COLLECT_ENDPOINT = '/_agentuity/webanalytics/collect';
const MAX_CUSTOM_EVENTS = 1000;
/**
 * Safely stringify an object, handling circular references and other errors.
 * Returns the JSON string on success, or a fallback string on failure.
 */
function safeStringify(obj) {
    if (obj === undefined || obj === null) {
        return '';
    }
    try {
        const seen = new WeakSet();
        return JSON.stringify(obj, (_key, value) => {
            if (typeof value === 'object' && value !== null) {
                if (seen.has(value)) {
                    return '[Circular]';
                }
                seen.add(value);
            }
            return value;
        });
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        console.warn('[Agentuity Analytics] Failed to stringify properties:', message);
        return `[unserializable: ${message}]`;
    }
}
(() => {
    const w = window;
    const d = document;
    const configRaw = w.__AGENTUITY_ANALYTICS__;
    if (!configRaw || !configRaw.enabled)
        return;
    // Prevent duplicate initialization (e.g., from HMR)
    const initFlag = w;
    if (configRaw.isDevmode) {
        console.debug('[Agentuity Analytics] Script loaded, init flag:', initFlag.__AGENTUITY_BEACON_INIT__, 'path:', location.pathname);
    }
    if (initFlag.__AGENTUITY_BEACON_INIT__) {
        if (configRaw.isDevmode) {
            console.debug('[Agentuity Analytics] Already initialized, skipping');
        }
        return;
    }
    initFlag.__AGENTUITY_BEACON_INIT__ = true;
    // Store in a non-nullable variable after the guard
    const c = configRaw;
    let geo = null;
    let sent = false;
    let pageStart = Date.now();
    let userId = '';
    let userTraits = {};
    const pv = {
        id: '',
        timestamp: 0,
        timezone_offset: 0,
        url: '',
        path: '',
        referrer: '',
        title: '',
        screen_width: 0,
        screen_height: 0,
        viewport_width: 0,
        viewport_height: 0,
        device_pixel_ratio: 1,
        user_agent: '',
        language: '',
        scroll_depth: 0,
        time_on_page: 0,
        scroll_events: [],
        custom_events: [],
    };
    function generateId() {
        return crypto.randomUUID
            ? crypto.randomUUID()
            : `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    }
    function getUTMParams() {
        const params = new URLSearchParams(location.search);
        const utm = {};
        ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'].forEach((key) => {
            const value = params.get(key);
            if (value)
                utm[key] = value;
        });
        return utm;
    }
    // Strip query string from URL to prevent sensitive data leakage
    function stripQueryString(url) {
        if (!url)
            return '';
        try {
            const parsed = new URL(url);
            return parsed.origin + parsed.pathname;
        }
        catch {
            // If URL parsing fails, try simple string split
            const parts = url.split('?');
            return parts[0] ?? url;
        }
    }
    // Full init - called on page load and SPA navigation
    function init() {
        pv.id = generateId();
        pv.timestamp = Date.now();
        pv.timezone_offset = new Date().getTimezoneOffset();
        pv.url = stripQueryString(location.href);
        pv.path = location.pathname;
        pv.referrer = stripQueryString(d.referrer);
        pv.title = d.title || '';
        pv.screen_width = screen.width || 0;
        pv.screen_height = screen.height || 0;
        pv.viewport_width = innerWidth || 0;
        pv.viewport_height = innerHeight || 0;
        pv.device_pixel_ratio = devicePixelRatio || 1;
        pv.user_agent = navigator.userAgent || '';
        pv.language = navigator.language || '';
        const utm = getUTMParams();
        for (const k in utm) {
            pv[k] = utm[k];
        }
        pv.scroll_events = [];
        pv.custom_events = [];
        pv.scroll_depth = 0;
        sent = false;
        pageStart = Date.now();
        if (typeof performance !== 'undefined' && performance.getEntriesByType) {
            const nav = performance.getEntriesByType('navigation')[0];
            if (nav) {
                pv.dom_ready = Math.round(nav.domContentLoadedEventEnd - nav.startTime);
                pv.ttfb = Math.round(nav.responseStart - nav.requestStart);
                // loadEventEnd is 0 during the load event, so defer reading it
                if (nav.loadEventEnd > 0) {
                    pv.load_time = Math.round(nav.loadEventEnd - nav.startTime);
                }
                else {
                    setTimeout(() => {
                        const navAfter = performance.getEntriesByType('navigation')[0];
                        if (navAfter && navAfter.loadEventEnd > 0) {
                            pv.load_time = Math.round(navAfter.loadEventEnd - navAfter.startTime);
                        }
                    }, 0);
                }
            }
        }
        if (c.isDevmode) {
            console.debug('[Agentuity Analytics] Session started (full init):', pv.id);
        }
    }
    // Soft reset - called when user returns to page (keeps page-level metrics)
    function resetSession() {
        pv.id = generateId();
        pv.timestamp = Date.now();
        pv.scroll_events = [];
        pv.custom_events = [];
        pv.scroll_depth = 0;
        pv.time_on_page = 0;
        sent = false;
        pageStart = Date.now();
        if (c.isDevmode) {
            console.debug('[Agentuity Analytics] Session started (soft reset):', pv.id);
        }
    }
    // Fetch geo data
    fetch('https://agentuity.sh/location')
        .then((r) => r.json())
        .then((g) => {
        geo = g;
        try {
            sessionStorage.setItem('agentuity_geo', JSON.stringify(g));
        }
        catch {
            // Ignore
        }
    })
        .catch(() => {
        try {
            const cached = sessionStorage.getItem('agentuity_geo');
            if (cached)
                geo = JSON.parse(cached);
        }
        catch {
            // Ignore
        }
    });
    // Try to load cached geo immediately
    try {
        const cached = sessionStorage.getItem('agentuity_geo');
        if (cached)
            geo = JSON.parse(cached);
    }
    catch {
        // Ignore
    }
    function getSession() {
        return w.__AGENTUITY_SESSION__;
    }
    function send(force = false) {
        if (sent && !force) {
            if (c.isDevmode) {
                console.debug('[Agentuity Analytics] send() skipped - already sent');
            }
            return;
        }
        if (c.sampleRate !== undefined && c.sampleRate < 1 && Math.random() > c.sampleRate)
            return;
        sent = true;
        pv.time_on_page = Date.now() - pageStart;
        if (geo) {
            pv.country = geo.country || '';
            if (geo.country_latitude)
                pv.country_latitude = parseFloat(String(geo.country_latitude));
            if (geo.country_longitude)
                pv.country_longitude = parseFloat(String(geo.country_longitude));
            pv.region = geo.region || '';
            if (geo.region_latitude)
                pv.region_latitude = parseFloat(String(geo.region_latitude));
            if (geo.region_longitude)
                pv.region_longitude = parseFloat(String(geo.region_longitude));
            pv.city = geo.city || '';
            if (geo.city_latitude)
                pv.city_latitude = parseFloat(String(geo.city_latitude));
            if (geo.city_longitude)
                pv.city_longitude = parseFloat(String(geo.city_longitude));
            pv.timezone = geo.timezone || '';
            if (geo.latitude)
                pv.latitude = parseFloat(String(geo.latitude));
            if (geo.longitude)
                pv.longitude = parseFloat(String(geo.longitude));
        }
        if (pv.cls) {
            pv.cls = Math.round(pv.cls * 1000) / 1000;
        }
        const s = getSession();
        const vid = localStorage.getItem('agentuity_visitor_id') || 'vid_' + generateId();
        try {
            localStorage.setItem('agentuity_visitor_id', vid);
        }
        catch {
            // Ignore
        }
        const payload = {
            org_id: c.orgId,
            project_id: c.projectId,
            thread_id: s?.threadId || '',
            visitor_id: vid,
            user_id: userId,
            user_traits: userTraits,
            is_devmode: c.isDevmode,
            pageview: pv,
        };
        // Clear pending data since we're sending now
        try {
            sessionStorage.removeItem('agentuity_pending_pageview');
        }
        catch {
            // Storage may be unavailable
        }
        if (c.isDevmode) {
            console.debug('[Agentuity Analytics]', JSON.stringify(payload, null, 2));
            return;
        }
        const body = JSON.stringify(payload);
        if (navigator.sendBeacon) {
            navigator.sendBeacon(COLLECT_ENDPOINT, body);
        }
        else {
            fetch(COLLECT_ENDPOINT, {
                method: 'POST',
                body,
                keepalive: true,
            }).catch(() => {
                // Silent failure
            });
        }
    }
    // Send on page hide, reset session on page visible
    d.addEventListener('visibilitychange', () => {
        if (c.isDevmode) {
            console.debug('[Agentuity Analytics] visibilitychange:', d.visibilityState, 'sent:', sent);
        }
        if (d.visibilityState === 'hidden') {
            send();
        }
        else if (d.visibilityState === 'visible') {
            // User returned to the page - start a new attention session
            // Keep page-level metrics (url, geo, vitals) but reset session-level metrics
            resetSession();
        }
    });
    w.addEventListener('pagehide', () => {
        if (c.isDevmode) {
            console.debug('[Agentuity Analytics] pagehide event');
            try {
                sessionStorage.setItem('agentuity_last_event', `pagehide:${Date.now()}:${pv.path}`);
            }
            catch {
                // Storage may be unavailable
            }
        }
        send();
    });
    // Catch hard navigations (URL change, link click to new page, refresh)
    w.addEventListener('beforeunload', () => {
        if (c.isDevmode) {
            console.debug('[Agentuity Analytics] beforeunload event');
            try {
                sessionStorage.setItem('agentuity_last_event', `beforeunload:${Date.now()}:${pv.path}`);
            }
            catch {
                // Storage may be unavailable
            }
        }
        send();
    });
    // In devmode, check if previous page sent data (helps verify unload events work)
    if (c.isDevmode) {
        try {
            const lastEvent = sessionStorage.getItem('agentuity_last_event');
            if (lastEvent) {
                console.debug('[Agentuity Analytics] Previous page event:', lastEvent);
                sessionStorage.removeItem('agentuity_last_event');
            }
        }
        catch {
            // Storage may be unavailable
        }
    }
    // Fallback: check for unsent data from previous page (in case unload events didn't fire)
    try {
        const pendingData = sessionStorage.getItem('agentuity_pending_pageview');
        if (pendingData) {
            sessionStorage.removeItem('agentuity_pending_pageview');
            const pending = JSON.parse(pendingData);
            // Only send if it's from a different page
            if (pending.pageview?.path !== location.pathname) {
                if (c.isDevmode) {
                    console.debug('[Agentuity Analytics] Sending unsent data from previous page:', pending.pageview?.path);
                    console.debug('[Agentuity Analytics]', JSON.stringify(pending, null, 2));
                }
                else {
                    const body = JSON.stringify(pending);
                    if (navigator.sendBeacon) {
                        navigator.sendBeacon(COLLECT_ENDPOINT, body);
                    }
                }
            }
        }
    }
    catch {
        // Storage or JSON parsing may fail
    }
    // Store current pageview data periodically so it can be recovered if unload events don't fire
    function savePendingData() {
        try {
            pv.time_on_page = Date.now() - pageStart;
            const s = getSession();
            const vid = localStorage.getItem('agentuity_visitor_id') || 'vid_' + generateId();
            const payload = {
                org_id: c.orgId,
                project_id: c.projectId,
                thread_id: s?.threadId || '',
                visitor_id: vid,
                user_id: userId,
                user_traits: userTraits,
                is_devmode: c.isDevmode,
                pageview: { ...pv },
            };
            sessionStorage.setItem('agentuity_pending_pageview', JSON.stringify(payload));
        }
        catch {
            // Storage may be unavailable
        }
    }
    // Save pending data every 2 seconds
    setInterval(savePendingData, 2000);
    // Also save on any interaction
    d.addEventListener('click', savePendingData, { passive: true });
    d.addEventListener('scroll', savePendingData, { passive: true, once: true });
    if (c.isDevmode) {
        console.debug('[Agentuity Analytics] Beacon initialized, visibility:', d.visibilityState);
    }
    // Scroll tracking
    if (c.trackScroll !== false) {
        const scrolled = new Set();
        function getScrollDepth() {
            const st = w.scrollY || d.documentElement.scrollTop;
            const sh = d.documentElement.scrollHeight - d.documentElement.clientHeight;
            return sh <= 0 ? 100 : Math.min(100, Math.round((st / sh) * 100));
        }
        w.addEventListener('scroll', () => {
            const dp = getScrollDepth();
            if (dp > pv.scroll_depth)
                pv.scroll_depth = dp;
            [25, 50, 75, 100].forEach((m) => {
                if (dp >= m && !scrolled.has(m)) {
                    scrolled.add(m);
                    pv.scroll_events.push({
                        depth: m,
                        timestamp: Date.now() - pageStart,
                    });
                }
            });
        }, { passive: true });
    }
    // Web Vitals tracking
    if (c.trackWebVitals !== false && typeof PerformanceObserver !== 'undefined') {
        // FCP
        try {
            const fcpObs = new PerformanceObserver((list) => {
                list.getEntries().forEach((entry) => {
                    if (entry.name === 'first-contentful-paint') {
                        pv.fcp = Math.round(entry.startTime);
                        fcpObs.disconnect();
                    }
                });
            });
            fcpObs.observe({ type: 'paint', buffered: true });
        }
        catch {
            // Not supported
        }
        // LCP
        try {
            new PerformanceObserver((list) => {
                const entries = list.getEntries();
                const lastEntry = entries[entries.length - 1];
                if (lastEntry) {
                    pv.lcp = Math.round(lastEntry.startTime);
                }
            }).observe({ type: 'largest-contentful-paint', buffered: true });
        }
        catch {
            // Not supported
        }
        // CLS
        try {
            new PerformanceObserver((list) => {
                list.getEntries().forEach((entry) => {
                    const layoutShift = entry;
                    if (!layoutShift.hadRecentInput && layoutShift.value) {
                        pv.cls = (pv.cls || 0) + layoutShift.value;
                    }
                });
            }).observe({ type: 'layout-shift', buffered: true });
        }
        catch {
            // Not supported
        }
        // INP
        try {
            new PerformanceObserver((list) => {
                list.getEntries().forEach((entry) => {
                    const eventEntry = entry;
                    if (eventEntry.duration && eventEntry.duration > (pv.inp || 0)) {
                        pv.inp = Math.round(eventEntry.duration);
                    }
                });
            }).observe({ type: 'event', buffered: true });
        }
        catch {
            // Not supported
        }
    }
    // SPA navigation tracking
    if (c.trackSPANavigation !== false) {
        const origPush = history.pushState;
        const origReplace = history.replaceState;
        let currentPath = location.pathname + location.search;
        let lastHref = location.href;
        if (c.isDevmode) {
            console.debug('[Agentuity Analytics] SPA tracking enabled, initial path:', currentPath);
        }
        function handleNav() {
            const newPath = location.pathname + location.search;
            if (newPath !== currentPath) {
                if (c.isDevmode) {
                    console.debug('[Agentuity Analytics] SPA navigation:', currentPath, '->', newPath);
                }
                send(true); // Force send on SPA navigation
                currentPath = newPath;
                lastHref = location.href;
                init();
            }
        }
        history.pushState = function (...args) {
            origPush.apply(this, args);
            setTimeout(handleNav, 0);
        };
        history.replaceState = function (...args) {
            origReplace.apply(this, args);
            setTimeout(handleNav, 0);
        };
        w.addEventListener('popstate', handleNav);
        // Fallback: poll for URL changes in case router bypasses history API
        setInterval(() => {
            if (location.href !== lastHref) {
                lastHref = location.href;
                handleNav();
            }
        }, 200);
    }
    // Click tracking
    if (c.trackClicks !== false) {
        d.addEventListener('click', (e) => {
            const target = e.target;
            if (!target)
                return;
            const analyticsEl = target.closest('[data-analytics]');
            if (!analyticsEl)
                return;
            if (pv.custom_events.length < MAX_CUSTOM_EVENTS) {
                pv.custom_events.push({
                    timestamp: Date.now(),
                    name: 'click:' + analyticsEl.getAttribute('data-analytics'),
                    data: '',
                });
            }
        }, true);
    }
    // Error tracking
    if (c.trackErrors !== false) {
        w.addEventListener('error', (e) => {
            if (pv.custom_events.length < MAX_CUSTOM_EVENTS) {
                pv.custom_events.push({
                    timestamp: Date.now(),
                    name: 'error:js_error',
                    data: JSON.stringify({
                        message: e.message || 'Unknown',
                        filename: e.filename || '',
                        lineno: e.lineno || 0,
                    }),
                });
            }
        });
        w.addEventListener('unhandledrejection', (e) => {
            if (pv.custom_events.length < MAX_CUSTOM_EVENTS) {
                pv.custom_events.push({
                    timestamp: Date.now(),
                    name: 'error:unhandled_rejection',
                    data: JSON.stringify({
                        message: e.reason instanceof Error ? e.reason.message : String(e.reason),
                    }),
                });
            }
        });
    }
    // Initialize on load
    if (d.readyState === 'complete') {
        init();
    }
    else {
        w.addEventListener('load', init);
    }
    // Public API
    w.agentuityAnalytics = {
        track(name, properties) {
            if (pv.custom_events.length < MAX_CUSTOM_EVENTS) {
                pv.custom_events.push({
                    timestamp: Date.now(),
                    name,
                    data: safeStringify(properties),
                });
            }
        },
        identify(id, traits) {
            userId = id;
            if (traits) {
                userTraits = {};
                for (const [key, value] of Object.entries(traits)) {
                    userTraits[key] = String(value);
                }
            }
        },
        flush: () => send(true),
    };
})();
//# sourceMappingURL=beacon-standalone.js.map