/**
 * AnalyticsService — Centralised GA4 event tracking for Mould Detect.
 *
 * Architecture:
 *   All gtag() / dataLayer calls are made through this service.
 *   Business components call AnalyticsService.track*() methods only —
 *   they never reference gtag or dataLayer directly.
 *
 * Two-property GA4 strategy:
 *   - Promotional website: separate GA4 property (future)
 *   - This app:            G-63NPGJ2XZS  (app_type: 'web_app')
 *
 * SPA page view tracking:
 *   MemoryRouter does not trigger GA4 automatic page views.
 *   App.jsx calls AnalyticsService.pageView(path, title) on every route change.
 *
 * Safe by default:
 *   All methods guard against missing window.gtag — safe in test/SSR environments.
 *
 * Public API:
 *   AnalyticsService.init(config)                  — boot-time context push
 *   AnalyticsService.pageView(path, title)          — SPA virtual page view
 *   AnalyticsService.scanInitiated(params)          — user taps "Check for Mould"
 *   AnalyticsService.scanCompleted(params)          — results shown
 *   AnalyticsService.scanError(params)              — analysis failed
 *   AnalyticsService.scanSaved(params)              — scan persisted to store
 *   AnalyticsService.reportsViewed(params)          — reports page loaded
 *   AnalyticsService.findProfessionalViewed(params) — specialists page loaded
 *   AnalyticsService.specialistProfileViewed(params)— profile page loaded
 *   AnalyticsService.contactProfessionalInitiated(p)— contact form opened
 *   AnalyticsService.contactProfessionalSubmitted(p)— quote request sent
 *   AnalyticsService.signInInitiated(params)        — sign-in CTA tapped
 *   AnalyticsService.signUpInitiated(params)        — sign-up CTA tapped
 *   AnalyticsService.appError(params)               — ErrorBoundary / app-level error
 *   AnalyticsService.setUserType(type)              — update user_type dimension
 *
 *   Mould Edu
 *   AnalyticsService.eduTopicExpanded(params)       — topic accordion opened
 *   AnalyticsService.eduVideoOpened(params)         — partner video opened on YouTube
 *
 *   Miss Mould
 *   AnalyticsService.chatQuestionAsked(params)      — question submitted (text | voice)
 *   AnalyticsService.chatAnswerReceived(params)     — answer finished streaming
 *   AnalyticsService.chatError(params)              — assistant failed
 *   AnalyticsService.chatQuotaReached(params)       — demo quota spent (NOT an error)
 *   AnalyticsService.chatSourceOpened(params)       — citation followed
 *   AnalyticsService.chatVoiceToggled(params)       — spoken answers on/off
 *
 *   Local Weather
 *   AnalyticsService.weatherLoaded(params)          — conditions rendered
 *   AnalyticsService.weatherUnavailable(params)     — conditions could not be shown
 *   AnalyticsService.weatherLocationDenied(params)  — location prompt declined
 *   AnalyticsService.weatherDetailOpened(params)    — dashboard card tapped through
 *   AnalyticsService.weatherRefreshed(params)       — user asked for fresh data
 *
 * Environment scoping:
 *   Every event, the page view and the GA4 config carry app_env and app_domain.
 *   app_env is the one to filter on — production | s3_direct | local | other —
 *   so our own testing does not sit in the same numbers as real usage. See
 *   _resolveEnv() for what maps to what.
 */
var AnalyticsService = (function () {

    var GA_MEASUREMENT_ID = 'G-63NPGJ2XZS';
    var APP_VERSION       = '2.5.0';

    /**
     * Classifies the host the app is being served from, so real usage can be
     * told apart from our own.
     *
     * app_domain alone is the raw hostname, which is precise but awkward to
     * filter on — every report needs a hostname list kept in step by hand.
     * app_env collapses it into the four cases that actually matter:
     *
     *   production   mycology.molddetect.app — the CloudFront distribution
     *                real users reach. This is the only traffic worth
     *                reporting on externally.
     *   s3_direct    the bucket's own *.s3*.amazonaws.com URL. Reachable and
     *                functional, but it bypasses CloudFront, so it is us
     *                testing, not a customer.
     *   local        localhost / 127.0.0.1 / a LAN IP — development.
     *   other        anything unrecognised, so a new host shows up as a gap
     *                rather than being silently counted as production.
     *
     * Both are sent: app_env for filtering, app_domain when you need to know
     * exactly which host it was.
     */
    function _resolveEnv(host) {
        var h = (host || '').toLowerCase();
        if (!h) return 'unknown';
        if (h === 'mycology.molddetect.app') return 'production';
        if (h.indexOf('.s3.') !== -1 || h.indexOf('.s3-website') !== -1 ||
            h.indexOf('.amazonaws.com') !== -1 || h.indexOf('.cloudfront.net') !== -1) return 's3_direct';
        if (h === 'localhost' || h === '127.0.0.1' || h === '::1' ||
            /^192\.168\./.test(h) || /^10\./.test(h) || h.indexOf('.local') !== -1) return 'local';
        return 'other';
    }

    var APP_DOMAIN = (typeof window !== 'undefined' && window.location && window.location.hostname) || 'unknown';
    var APP_ENV    = _resolveEnv(APP_DOMAIN);

    // Internal: safe gtag wrapper — no-ops if gtag not loaded
    function _gtag() {
        if (typeof window.gtag !== 'function') return;
        window.gtag.apply(window, arguments);
    }

    // Internal: push to dataLayer directly (for GTM compatibility)
    function _push(obj) {
        window.dataLayer = window.dataLayer || [];
        window.dataLayer.push(obj);
    }

    // Internal: merge global app dimensions into every event
    function _event(eventName, params) {
        var merged = {
            app_type:    'web_app',
            app_version: APP_VERSION,
            app_domain:  APP_DOMAIN,
            app_env:     APP_ENV,
        };
        if (params) {
            for (var k in params) { merged[k] = params[k]; }
        }
        _gtag('event', eventName, merged);
    }

    /**
     * init() — Called once during App.jsx boot after FeatureFlags.init().
     * Pushes app context to dataLayer and configures GA4 with app dimensions.
     *
     * @param {object} config
     *   @param {string} config.aiEngine   — active engine: 'local_cnn' | 'cloud_ai' | 'nyckel' | 'none'
     *   @param {string} config.userType   — 'guest' | 'registered'
     */
    function init(config) {
        var aiEngine = (config && config.aiEngine) || 'unknown';
        var userType = (config && config.userType) || 'guest';

        // Push app context to dataLayer for GTM access
        _push({
            event:       'app_init',
            app_type:    'web_app',
            app_version: APP_VERSION,
            app_domain:  APP_DOMAIN,
            app_env:     APP_ENV,
            ai_engine:   aiEngine,
            user_type:   userType,
        });

        // Set GA4 user properties for segmentation in reports
        _gtag('config', GA_MEASUREMENT_ID, {
            app_type:           'web_app',
            app_version:        APP_VERSION,
            app_domain:         APP_DOMAIN,
            app_env:            APP_ENV,
            send_page_view:     false, // disable auto — we fire manually via pageView()
            custom_map: {
                dimension1: 'app_type',
                dimension2: 'ai_engine',
                dimension3: 'user_type',
                dimension4: 'app_env',
                dimension5: 'app_domain',
            },
        });

        // Also a user property, so a GA4 audience or report filter can exclude
        // our own traffic without every event needing the parameter.
        _gtag('set', 'user_properties', { app_env: APP_ENV, app_domain: APP_DOMAIN });

        console.info('[Analytics] Initialised. app_type=web_app, app_env=' + APP_ENV + ' (' + APP_DOMAIN + '), ai_engine=' + aiEngine + ', user_type=' + userType);
    }

    /**
     * pageView() — Fire a virtual page view for SPA route changes.
     * Must be called from App.jsx on every MemoryRouter location change.
     *
     * @param {string} path  — e.g. '/analysis'
     * @param {string} title — e.g. 'Scan'
     */
    function pageView(path, title) {
        _gtag('event', 'page_view', {
            page_title:    title || path,
            page_location: window.location.origin + path,
            page_path:     path,
            app_type:      'web_app',
            app_domain:    APP_DOMAIN,
            app_env:       APP_ENV,
        });
    }

    // ── Scan Journey ──────────────────────────────────────────────────────────

    /**
     * scanInitiated — User taps "Check for Mould" with an image ready.
     * @param {{ ai_engine: string, image_source: string }} params
     *   image_source: 'camera' | 'gallery' | 'drag_drop'
     */
    function scanInitiated(params) {
        _event('scan_initiated', params);
    }

    /**
     * scanCompleted — Analysis results displayed to user.
     * @param {{ ai_engine, verdict, severity, confidence_pct, species, is_mould }} params
     */
    function scanCompleted(params) {
        _event('scan_completed', params);
    }

    /**
     * scanError — Analysis failed (all engines exhausted).
     * @param {{ ai_engine, error_type, error_message }} params
     */
    function scanError(params) {
        _event('scan_error', params);
    }

    /**
     * scanSaved — Scan record persisted to IndexedDB.
     * @param {{ severity, has_location, has_property }} params
     */
    function scanSaved(params) {
        _event('scan_saved', params);
    }

    // ── Reports ───────────────────────────────────────────────────────────────

    /**
     * reportsViewed — Reports page loaded.
     * @param {{ scan_count, property_count }} params
     */
    function reportsViewed(params) {
        _event('reports_viewed', params);
    }

    // ── Specialist Journey ────────────────────────────────────────────────────

    /**
     * findProfessionalViewed — Specialist directory page loaded.
     * @param {{ search_query, category_filter, result_count }} params
     */
    function findProfessionalViewed(params) {
        _event('find_professional_viewed', params);
    }

    /**
     * specialistProfileViewed — Individual specialist profile viewed.
     * @param {{ specialist_category, specialist_city }} params
     */
    function specialistProfileViewed(params) {
        _event('specialist_profile_viewed', params);
    }

    /**
     * contactProfessionalInitiated — Contact form page opened.
     * @param {{ specialist_category }} params
     */
    function contactProfessionalInitiated(params) {
        _event('contact_professional_initiated', params);
    }

    /**
     * contactProfessionalSubmitted — Quote request form submitted.
     * @param {{ specialist_category, has_scan_attached, contact_method }} params
     */
    function contactProfessionalSubmitted(params) {
        _event('contact_professional_submitted', params);
    }

    // ── Auth Journey ──────────────────────────────────────────────────────────

    /**
     * signInInitiated — Sign In CTA tapped.
     * @param {{ trigger_location: string }} params
     *   trigger_location: 'header' | 'home_panel' | 'menu' | 'upgrade_panel'
     */
    function signInInitiated(params) {
        _event('sign_in_initiated', params);
    }

    /**
     * signUpInitiated — Sign Up CTA tapped.
     * @param {{ trigger_location: string }} params
     */
    function signUpInitiated(params) {
        _event('sign_up_initiated', params);
    }

    // ── Errors ────────────────────────────────────────────────────────────────

    /**
     * appError — Application-level error (ErrorBoundary, boot failure).
     * @param {{ error_type, component, message }} params
     */
    function appError(params) {
        _event('app_error', params);
    }

    // ── Mould Edu ─────────────────────────────────────────────────────────────

    /**
     * eduTopicExpanded — A learning topic accordion was opened.
     * Only the open is recorded, not the close: which topics people choose to
     * read is the signal; collapsing one is housekeeping.
     * @param {{ topic_title: string, topic_index: number }} params
     */
    function eduTopicExpanded(params) {
        _event('edu_topic_expanded', params);
    }

    /**
     * eduVideoOpened — A partner video was opened on YouTube.
     * This leaves the app, so it is the last thing we can observe in the
     * session — worth knowing which titles earn the exit.
     * @param {{ video_id: string, video_title: string, partner: string }} params
     */
    function eduVideoOpened(params) {
        _event('edu_video_opened', params);
    }

    // ── Miss Mould (assistant) ────────────────────────────────────────────────

    /**
     * chatQuestionAsked — A question was submitted.
     * @param {{ input_mode: string, question_length: number }} params
     *   input_mode: 'text' | 'voice'
     */
    function chatQuestionAsked(params) {
        _event('chat_question_asked', params);
    }

    /**
     * chatAnswerReceived — An answer finished streaming.
     * source_count is the retrieval quality signal worth watching: answers that
     * cite nothing are the ones to go and look at.
     * @param {{ source_count: number, latency_ms: number, spoken: boolean }} params
     */
    function chatAnswerReceived(params) {
        _event('chat_answer_received', params);
    }

    /**
     * chatError — The assistant failed to answer.
     * @param {{ reason: string, stage: string }} params
     *   stage: 'session' | 'ask' | 'listen' | 'load'
     */
    function chatError(params) {
        _event('chat_error', params);
    }

    /**
     * chatQuotaReached — The session's demo question quota was exhausted (429).
     * Separated from chatError on purpose: this one is a product signal about
     * demand and cost, not a fault, and it should not sit in an error rate.
     * @param {{ questions_asked: number }} params
     */
    function chatQuotaReached(params) {
        _event('chat_quota_reached', params);
    }

    /**
     * chatSourceOpened — A citation link was followed.
     * The strongest available evidence that answers are trusted enough to check.
     * @param {{ source_title: string, authority: string }} params
     */
    function chatSourceOpened(params) {
        _event('chat_source_opened', params);
    }

    /**
     * chatVoiceToggled — Spoken answers turned on or off.
     * Speech is the steepest per-unit cost in the platform, so the opt-out rate
     * is a cost input, not just a preference.
     * @param {{ enabled: boolean }} params
     */
    function chatVoiceToggled(params) {
        _event('chat_voice_toggled', params);
    }

    // ── Local Weather ─────────────────────────────────────────────────────────

    /**
     * weatherLoaded — Conditions rendered successfully.
     * @param {{ surface: string, approximate: boolean, stale: boolean, humidity_pct: number, damp_risk: string }} params
     *   surface: 'panel' (dashboard card) | 'page' (detail view)
     */
    function weatherLoaded(params) {
        _event('weather_loaded', params);
    }

    /**
     * weatherUnavailable — Conditions could not be shown.
     * @param {{ surface: string, reason: string }} params
     *   reason: 'request_failed' | 'no_data' | 'timeout' | 'location_unavailable'
     */
    function weatherUnavailable(params) {
        _event('weather_unavailable', params);
    }

    /**
     * weatherLocationDenied — The browser location prompt was declined.
     * Not an error: we fall back to a default city and label the reading
     * approximate. Tracked because it explains a chunk of "approximate" data
     * that would otherwise look like a bug in the location service.
     * @param {{ surface: string }} params
     */
    function weatherLocationDenied(params) {
        _event('weather_location_denied', params);
    }

    /**
     * weatherDetailOpened — The dashboard card was tapped through to the page.
     * @param {{ damp_risk: string }} params
     */
    function weatherDetailOpened(params) {
        _event('weather_detail_opened', params);
    }

    /**
     * weatherRefreshed — The user asked for fresh conditions.
     * @param {{ surface: string, was_stale: boolean }} params
     */
    function weatherRefreshed(params) {
        _event('weather_refreshed', params);
    }

    // ── User Properties ───────────────────────────────────────────────────────

    /**
     * setUserType — Update the user_type dimension (guest → registered on sign-in).
     * @param {string} type — 'guest' | 'registered'
     */
    function setUserType(type) {
        _gtag('set', 'user_properties', { user_type: type });
        _push({ user_type: type });
    }

    return {
        init:                          init,
        pageView:                      pageView,
        scanInitiated:                 scanInitiated,
        scanCompleted:                 scanCompleted,
        scanError:                     scanError,
        scanSaved:                     scanSaved,
        reportsViewed:                 reportsViewed,
        findProfessionalViewed:        findProfessionalViewed,
        specialistProfileViewed:       specialistProfileViewed,
        contactProfessionalInitiated:  contactProfessionalInitiated,
        contactProfessionalSubmitted:  contactProfessionalSubmitted,
        signInInitiated:               signInInitiated,
        signUpInitiated:               signUpInitiated,
        appError:                      appError,
        setUserType:                   setUserType,
        // Mould Edu
        eduTopicExpanded:              eduTopicExpanded,
        eduVideoOpened:                eduVideoOpened,
        // Miss Mould
        chatQuestionAsked:             chatQuestionAsked,
        chatAnswerReceived:            chatAnswerReceived,
        chatError:                     chatError,
        chatQuotaReached:              chatQuotaReached,
        chatSourceOpened:              chatSourceOpened,
        chatVoiceToggled:              chatVoiceToggled,
        // Local Weather
        weatherLoaded:                 weatherLoaded,
        weatherUnavailable:            weatherUnavailable,
        weatherLocationDenied:         weatherLocationDenied,
        weatherDetailOpened:           weatherDetailOpened,
        weatherRefreshed:              weatherRefreshed,
    };
})();

window.AnalyticsService = AnalyticsService;
