"use strict";
/**
 * Analytics Events Utility
 */
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@roomstay/core");
const AnalyticsEventsAPI_1 = require("../../api/AnalyticsEventsAPI");
const AddAddonToCartEvent_1 = require("../../events/actions/AddAddonToCartEvent");
const AddToCartEvent_1 = require("../../events/actions/AddToCartEvent");
const RoomstayEventManager_1 = __importDefault(require("../../events/RoomstayEventManager"));
const CommittedBookingEvent_1 = require("../../events/views/CommittedBookingEvent");
const StepCheckoutViewEvent_1 = require("../../events/views/StepCheckoutViewEvent");
const helpers_1 = require("./helpers");
/**
 * Validate session from storage
 */
function isValidSession(obj) {
    if (!obj || typeof obj !== 'object')
        return false;
    const session = obj;
    return typeof session.sessionId === 'string' && typeof session.createdAt === 'number' && typeof session.hotelFrontendId === 'string' && typeof session.currency === 'string';
}
const STORAGE_KEY_PREFIX = 'roomstay-analytics-session';
const SESSION_MAX_AGE = 12 * 60 * 60 * 1000; // 12 hours
/**
 * Get storage key for a specific hotel's session
 */
function getStorageKey(hotelId) {
    return `${STORAGE_KEY_PREFIX}-${hotelId}`;
}
function generateUUID() {
    return crypto.randomUUID();
}
/**
 * Fire a Meta Pixel event with Event ID for deduplication
 */
function firePixelEvent(eventName, eventId, customData, userData, pixelId) {
    const metaPixelIsAvailable = typeof window !== 'undefined' && typeof window.fbq === 'function';
    if (!metaPixelIsAvailable)
        return;
    try {
        // Re-init with user data before firing the event. I can't find any other way of achieving this.
        // Meta seems to assume you already have all the user data when the pixel is initially loaded,
        // I guess this is more of an issue with SPA apps.
        if (userData && pixelId) {
            window.fbq('init', pixelId, userData);
        }
        window.fbq('track', eventName, customData, { eventID: eventId });
    }
    catch (error) {
        console.error('[AnalyticsEvents] Error firing pixel event:', error);
    }
}
class AnalyticsEvents {
    constructor() {
        this.session = null;
        this.config = {};
        this.initialized = false;
        this.listenersRegistered = false;
        this.sessionReady = null;
    }
    /**
     * Initialize analytics with configuration
     */
    initialize(config) {
        var _a, _b, _c, _d;
        if (this.initialized) {
            return;
        }
        this.config = config || {};
        // Try to restore existing session
        const existingSession = this.loadSession();
        const cookies = (0, helpers_1.getMetaCookies)();
        // Resolve hotelId and currency from config or existing session
        const hotelFrontendId = (_a = this.config.hotelId) !== null && _a !== void 0 ? _a : existingSession === null || existingSession === void 0 ? void 0 : existingSession.hotelFrontendId;
        const currency = (_b = this.config.currency) !== null && _b !== void 0 ? _b : existingSession === null || existingSession === void 0 ? void 0 : existingSession.currency;
        // Cannot initialize without required fields
        if (!hotelFrontendId || !currency) {
            this.log('Cannot initialize: hotelId and currency are required');
            return;
        }
        if (existingSession) {
            this.session = Object.assign(Object.assign({}, existingSession), { fbc: (_c = cookies.fbc) !== null && _c !== void 0 ? _c : existingSession.fbc, fbp: (_d = cookies.fbp) !== null && _d !== void 0 ? _d : existingSession.fbp, hotelFrontendId,
                currency });
            this.sessionReady = Promise.resolve(true);
            this.log('Session restored:', this.session.sessionId);
        }
        else {
            this.session = {
                sessionId: generateUUID(),
                fbc: cookies.fbc,
                fbp: cookies.fbp,
                createdAt: Date.now(),
                hotelFrontendId,
                currency,
            };
            this.sessionReady = this.createServerSession();
        }
        this.saveSession();
        this.initialized = true;
        this.registerEventListeners();
        this.log('Session initialized:', this.session.sessionId);
    }
    /**
     * Create session on the server
     */
    createServerSession() {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this.session)
                return false;
            try {
                const response = yield AnalyticsEventsAPI_1.analyticsEventsAPI.createSession({
                    sessionId: this.session.sessionId,
                    hotelFrontendId: this.session.hotelFrontendId,
                    fbc: this.session.fbc,
                    fbp: this.session.fbp,
                });
                if (!response.success) {
                    console.warn('[AnalyticsEvents] Failed to create server session:', response.message);
                    return false;
                }
                this.log('Server session ready:', this.session.sessionId);
                return true;
            }
            catch (error) {
                console.error('[AnalyticsEvents] Error creating server session:', error);
                return false;
            }
        });
    }
    /**
     * Register listeners for Roomstay events
     */
    registerEventListeners() {
        if (this.listenersRegistered)
            return;
        // View/Checkout event
        RoomstayEventManager_1.default.addEvent(StepCheckoutViewEvent_1.StepCheckoutViewEvent, (event) => __awaiter(this, void 0, void 0, function* () {
            this.trackInitiateCheckout(event.basketRows, event.basketAddonRows);
        }), 'analytics-events-checkout');
        // Basket/Add event (room)
        RoomstayEventManager_1.default.addEvent(AddToCartEvent_1.AddToCartEvent, (event) => __awaiter(this, void 0, void 0, function* () {
            this.trackAddToCart(event.basketRow);
        }), 'analytics-events-add-to-cart');
        // Basket/AddAddon event (addon)
        RoomstayEventManager_1.default.addEvent(AddAddonToCartEvent_1.AddAddonToCartEvent, (event) => __awaiter(this, void 0, void 0, function* () {
            this.trackAddonAddToCart(event.basketAddonRow);
        }), 'analytics-events-add-addon');
        // Booking/Committed event
        RoomstayEventManager_1.default.addEvent(CommittedBookingEvent_1.CommittedBookingEvent, (event) => __awaiter(this, void 0, void 0, function* () {
            this.trackPurchase(event.reservation, event.basketRows, event.basketAddonRows);
        }), 'analytics-events-purchase');
        this.listenersRegistered = true;
        this.log('Event listeners registered');
    }
    /**
     * Track View/Checkout event
     */
    trackInitiateCheckout(basketRows, addonRows) {
        const session = this.getValidatedSession();
        if (!session)
            return;
        const { currency } = session;
        const items = (0, helpers_1.serializeBasketRows)(basketRows);
        const addons = (addonRows === null || addonRows === void 0 ? void 0 : addonRows.length) ? (0, helpers_1.serializeBasketAddonRows)(addonRows) : undefined;
        // Build event data in Roomstay format
        const eventData = {
            items,
            addons,
            currency,
            totalValue: (0, helpers_1.calculateTotalValue)(items, addons),
        };
        const payload = this.createEventPayload(core_1.ERoomstayEventName.ViewCheckout, eventData);
        // Fire Meta Pixel with transformed data
        if (this.config.firePixelEvents) {
            const metaEventData = (0, core_1.transformToMetaEventData)(eventData);
            firePixelEvent('InitiateCheckout', payload.eventId, metaEventData);
        }
        this.sendEvent(payload);
        this.log('View/Checkout tracked:', payload);
    }
    /**
     * Track Basket/Add event (room)
     */
    trackAddToCart(basketRow) {
        const session = this.getValidatedSession();
        if (!session)
            return;
        const { currency } = session;
        // Build event data in Roomstay format
        const eventData = {
            item: (0, helpers_1.serializeBasketRow)(basketRow),
            currency,
        };
        const payload = this.createEventPayload(core_1.ERoomstayEventName.BasketAdd, eventData);
        // Fire Meta Pixel with transformed data
        if (this.config.firePixelEvents) {
            const metaEventData = (0, core_1.transformToMetaEventData)(eventData);
            firePixelEvent('AddToCart', payload.eventId, metaEventData);
        }
        this.sendEvent(payload);
        this.log('Basket/Add tracked:', payload);
    }
    /**
     * Track Basket/AddAddon event (addon)
     */
    trackAddonAddToCart(addonRow) {
        const session = this.getValidatedSession();
        if (!session)
            return;
        const { currency } = session;
        // Build event data in Roomstay format
        const eventData = {
            addon: (0, helpers_1.serializeBasketAddonRow)(addonRow),
            currency,
        };
        const payload = this.createEventPayload(core_1.ERoomstayEventName.BasketAddAddon, eventData);
        // Fire Meta Pixel with transformed data
        if (this.config.firePixelEvents) {
            const metaEventData = (0, core_1.transformToMetaEventData)(eventData);
            firePixelEvent('AddToCart', payload.eventId, metaEventData);
        }
        this.sendEvent(payload);
        this.log('Basket/AddAddon tracked:', payload);
    }
    /**
     * Track Booking/Committed event
     */
    trackPurchase(reservation, basketRows, addonRows) {
        const session = this.getValidatedSession();
        if (!session)
            return;
        const { currency } = session;
        const items = (0, helpers_1.serializeBasketRows)(basketRows);
        const addons = (addonRows === null || addonRows === void 0 ? void 0 : addonRows.length) ? (0, helpers_1.serializeBasketAddonRows)(addonRows) : undefined;
        // Build event data in Roomstay format with embedded guest data
        const eventData = {
            items,
            addons,
            currency,
            totalValue: (0, helpers_1.calculateTotalValue)(items, addons),
            roomstayId: reservation.roomstayId,
            itineraryNumber: reservation.itineraryNumber,
            reservationNumbers: reservation.reservations.map((r) => r.reservationNumber),
            guest: Object.assign(Object.assign({}, (0, helpers_1.extractGuestData)(reservation)), { externalId: session.externalId }),
        };
        const payload = this.createEventPayload(core_1.ERoomstayEventName.BookingCommitted, eventData);
        // Fire Meta Pixel with transformed event data and user data
        if (this.config.firePixelEvents) {
            const metaEventData = (0, core_1.transformToMetaEventData)(eventData);
            const metaUserData = (0, core_1.transformToMetaUserData)(eventData.guest);
            firePixelEvent('Purchase', payload.eventId, metaEventData, metaUserData, this.config.metaPixelId);
        }
        this.sendEvent(payload);
        this.log('Booking/Committed tracked:', payload);
    }
    /**
     * Set external user ID (e.g. member portal ID)
     */
    setExternalId(externalId) {
        if (this.session) {
            this.log('Setting external ID:', externalId);
            this.session.externalId = externalId;
            this.saveSession();
        }
    }
    /**
     * Ensure session is initialized and has required fields for tracking.
     * Returns the session if tracking can proceed, null otherwise.
     */
    getValidatedSession() {
        if (!this.initialized) {
            this.initialize(this.config);
        }
        if (!this.session) {
            console.warn('[AnalyticsEvents] Cannot track: session not initialized');
            return null;
        }
        return this.session;
    }
    createEventPayload(eventName, eventData) {
        var _a, _b;
        const session = this.session;
        return {
            eventId: generateUUID(),
            sessionId: (_a = session === null || session === void 0 ? void 0 : session.sessionId) !== null && _a !== void 0 ? _a : '',
            eventName,
            eventTime: Math.floor(Date.now() / 1000),
            hotelFrontendId: (_b = session === null || session === void 0 ? void 0 : session.hotelFrontendId) !== null && _b !== void 0 ? _b : '',
            fbc: (session === null || session === void 0 ? void 0 : session.fbc) || undefined,
            fbp: (session === null || session === void 0 ? void 0 : session.fbp) || undefined,
            sourceUrl: typeof window !== 'undefined' ? window.location.href : undefined,
            eventData,
        };
    }
    sendEvent(eventData) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                // Wait for session to be created on server before sending events
                if (!this.sessionReady) {
                    console.warn('[AnalyticsEvents] Skipping event - sessionReady not initialized');
                    return;
                }
                const sessionCreated = yield this.sessionReady;
                if (!sessionCreated) {
                    console.warn('[AnalyticsEvents] Skipping event - session not created on server');
                    return;
                }
                const response = yield AnalyticsEventsAPI_1.analyticsEventsAPI.sendEvent(eventData);
                if (!response.success) {
                    console.warn('[AnalyticsEvents] Backend error:', response.message);
                }
            }
            catch (error) {
                console.error('[AnalyticsEvents] Failed to send event:', error);
            }
        });
    }
    loadSession() {
        if (typeof localStorage === 'undefined')
            return null;
        const hotelId = this.config.hotelId;
        if (!hotelId)
            return null;
        try {
            const stored = localStorage.getItem(getStorageKey(hotelId));
            if (stored) {
                const session = JSON.parse(stored);
                if (isValidSession(session) && Date.now() - session.createdAt < SESSION_MAX_AGE) {
                    return session;
                }
            }
        }
        catch (e) {
            this.log('Failed to load session:', e);
        }
        return null;
    }
    saveSession() {
        if (typeof localStorage === 'undefined' || !this.session)
            return;
        try {
            localStorage.setItem(getStorageKey(this.session.hotelFrontendId), JSON.stringify(this.session));
        }
        catch (e) {
            this.log('Failed to save session:', e);
        }
    }
    log(...args) {
        if (this.config.debug) {
            console.log('[AnalyticsEvents]', ...args);
        }
    }
}
exports.default = new AnalyticsEvents();
//# sourceMappingURL=AnalyticsEvents.js.map