"use client"; import type { AnalyticsEvent, AnalyticsProvider, UserData } from "../core/types"; declare global { interface Window { rybbit?: { event: (eventName: string, eventData?: Record) => void; pageview: () => void; }; } } type QueuedEvent = { eventName: string; properties?: Record; }; export class RybbitProvider implements AnalyticsProvider { name = "Rybbit"; private isClient: boolean; private eventQueue: QueuedEvent[] = []; private flushInterval: ReturnType | null = null; private initialized = false; constructor() { this.isClient = typeof window !== "undefined"; if (this.isClient) { console.log("[RybbitProvider] Constructor called"); // Start checking for rybbit availability this.startFlushInterval(); // Also try to flush immediately in case script is already loaded setTimeout(() => this.tryFlushQueue(), 100); } } private startFlushInterval() { // Check every 500ms for up to 15 seconds let attempts = 0; const maxAttempts = 30; this.flushInterval = setInterval(() => { attempts++; const available = this.isAvailable(); if (available && !this.initialized) { console.log("[RybbitProvider] Script became available, flushing queue"); this.initialized = true; } this.tryFlushQueue(); if (available || attempts >= maxAttempts) { this.stopFlushInterval(); if (attempts >= maxAttempts && !available) { console.warn("[RybbitProvider] Max attempts reached, script not loaded. Queue size:", this.eventQueue.length); } } }, 500); } private stopFlushInterval() { if (this.flushInterval) { clearInterval(this.flushInterval); this.flushInterval = null; } } private tryFlushQueue() { if (!this.isAvailable() || this.eventQueue.length === 0) { return; } console.log(`[RybbitProvider] Flushing ${this.eventQueue.length} queued events`); // Flush all queued events while (this.eventQueue.length > 0) { const event = this.eventQueue.shift(); if (event) { this.sendEvent(event.eventName, event.properties); } } } isAvailable(): boolean { return this.isClient && typeof window.rybbit?.event === "function"; } private sendEvent(eventName: string, properties?: Record): void { try { window.rybbit!.event(eventName, properties); console.log(`[Rybbit] Event sent: ${eventName}`); } catch (e) { console.warn(`[Rybbit] Tracking error for ${eventName}:`, e); } } private trackEvent(eventName: string, properties?: Record): void { if (!this.isClient) return; if (this.isAvailable()) { this.sendEvent(eventName, properties); } else { // Queue the event for later this.eventQueue.push({ eventName, properties }); console.log(`[Rybbit] Queued event: ${eventName}, queue size: ${this.eventQueue.length}`); } } track(event: AnalyticsEvent): void { switch (event.type) { case "product_viewed": this.trackEvent("product_view", { product_id: event.product.id, product_name: event.product.name, price: event.product.price, currency: event.product.currency, category: event.product.category, variant: event.product.variant, }); break; case "add_to_cart": this.trackEvent("add_to_cart", { product_id: event.product.id, product_name: event.product.name, price: event.product.price, currency: event.product.currency, quantity: event.product.quantity, variant: event.product.variant, }); break; case "remove_from_cart": this.trackEvent("remove_from_cart", { product_id: event.product.id, product_name: event.product.name, quantity: event.product.quantity, }); break; case "cart_view": this.trackEvent("cart_view", { cart_total: event.cart.total, currency: event.cart.currency, item_count: event.cart.item_count, }); break; case "checkout_started": this.trackEvent("checkout_started", { cart_total: event.cart.total, currency: event.cart.currency, item_count: event.cart.item_count, items: event.cart.items, }); break; case "checkout_step": this.trackEvent("checkout_step", { step: event.step, ...event.data, }); break; case "order_completed": this.trackEvent("order_completed", { order_id: event.order.order_id, order_number: event.order.order_number, total: event.order.total, currency: event.order.currency, item_count: event.order.item_count, shipping_cost: event.order.shipping_cost, coupon_code: event.order.coupon_code, customer_email: event.order.customer_email, payment_method: event.order.payment_method, revenue: event.order.total, }); break; case "search": this.trackEvent("search", { query: event.query, results_count: event.results_count, }); break; case "external_link_click": this.trackEvent("external_link_click", { url: event.url, label: event.label, }); break; case "wishlist_add": this.trackEvent("wishlist_add", { product_id: event.product.id, product_name: event.product.name, }); break; case "user_login": this.trackEvent("user_login", { method: event.method, }); break; case "user_register": this.trackEvent("user_register", { method: event.method, }); break; case "newsletter_signup": this.trackEvent("newsletter_signup", { email: event.email, source: event.source, }); break; } } identify(_user: UserData): void { // Rybbit doesn't have explicit identify - it's handled automatically via cookies } revenue?(_amount: number, _currency: string, _properties?: Record): Promise { // Revenue is tracked via order_completed event return Promise.resolve(); } }