Análisis de Experiencia de Usuario FullStory

Ejemplos de FullStory para grabación de sesiones, mapas de calor, embudos de conversión y análisis de comportamiento de usuario

💻 FullStory Hello World javascript

🟢 simple

Configuración básica de FullStory con grabación de sesión e identificación de usuario

⏱️ 10 min 🏷️ frontend, monitoring, analytics, ux
Prerequisites: JavaScript basics, HTML/CSS, Browser APIs
// FullStory Hello World - Basic User Experience Analytics
// Simple session recording and user tracking setup

// Initialize FullStory with your organization ID
window['_fs_debug'] = false
window['_fs_host'] = 'fullstory.com'
window['_fs_org'] = 'YOUR-ORG-ID'
window['_fs_namespace'] = 'FS'
;(function(m,n,e,t,l,o,g,y){
    if (e in m) {if(m.console && m.console.log) { m.console.log('FullStory namespace conflict. Please set window["_fs_namespace"].');} return;}
    g=m[e]=function(a,b,s){g.q?g.q.push([a,b,s]):g._api(a,b,s);};g.q=[];
    o=n.createElement(t);o.async=1;o.src='https://'+_fs_host+'/s/fs.js';
    y=n.getElementsByTagName(t)[0];y.parentNode.insertBefore(o,y);
    g.identify=function(i,v,s){g(l,{uid:i},s);if(v)g(l,v,s)};g.setUserVars=function(v,s){g(l,v,s)};g.event=function(i,v,s){g('event',{n:i,p:v},s)};
    g.shutdown=function(){g("rec",!1)};g.restart=function(){g("rec",!0)};
    g.consent=function(a){g("consent",!a)};g.identifyAccount=function(i,v){o='account';v=v||{};v.acctId=i;g(o,v)};
    g.clearUserCookie=function(){};
})(window,document,window['_fs_namespace'],'script','user');

// Identify the current user
FS.identify('user-123', {
  displayName: 'John Doe',
  email: '[email protected]',
  plan: 'premium',
  signUpDate: '2023-01-15',
  lastLogin: new Date().toISOString()
});

// Track custom events
FS.event('User signed up', {
  method: 'email',
  referralSource: 'google',
  userAgent: navigator.userAgent
});

// Track page views
FS.event('Page Viewed', {
  page: window.location.pathname,
  title: document.title,
  referrer: document.referrer
});

console.log('FullStory initialized and recording!');

// Track form submissions
document.addEventListener('submit', function(event) {
  const form = event.target;
  if (form.tagName === 'FORM') {
    FS.event('Form Submitted', {
      formId: form.id,
      formName: form.name,
      action: form.action
    });
  }
});

// Track clicks on important elements
document.addEventListener('click', function(event) {
  const element = event.target;
  if (element.matches('button, a, input[type="button"], input[type="submit"]')) {
    FS.event('Button Clicked', {
      text: element.textContent || element.value,
      className: element.className,
      id: element.id
    });
  }
});

💻 Embudo de Conversión E-commerce typescript

🟡 intermediate ⭐⭐⭐

Seguir el viaje del usuario a través del proceso de checkout con análisis de conversión

⏱️ 30 min 🏷️ frontend, analytics, e-commerce, conversion
Prerequisites: JavaScript/TypeScript, E-commerce concepts, Event tracking, FullStory API
// FullStory E-commerce Conversion Funnel Analysis
// Track user journey through checkout process and identify drop-off points

interface ConversionEvent {
  step: string
  userId: string
  sessionId: string
  timestamp: number
  properties: Record<string, any>
}

interface FunnelStep {
  name: string
  stepOrder: number
  eventTrigger: string
  properties: string[]
}

class EcommerceFunnelTracker {
  private funnelSteps: FunnelStep[] = [
    {
      name: 'Product View',
      stepOrder: 1,
      eventTrigger: 'Product Viewed',
      properties: ['productId', 'productName', 'price', 'category']
    },
    {
      name: 'Add to Cart',
      stepOrder: 2,
      eventTrigger: 'Product Added to Cart',
      properties: ['productId', 'quantity', 'price']
    },
    {
      name: 'View Cart',
      stepOrder: 3,
      eventTrigger: 'Cart Viewed',
      properties: ['cartValue', 'itemCount']
    },
    {
      name: 'Checkout Started',
      stepOrder: 4,
      eventTrigger: 'Checkout Started',
      properties: ['cartValue', 'itemCount', 'paymentMethod']
    },
    {
      name: 'Payment Info',
      stepOrder: 5,
      eventTrigger: 'Payment Info Entered',
      properties: ['paymentMethod', 'billingAddress']
    },
    {
      name: 'Order Completed',
      stepOrder: 6,
      eventTrigger: 'Order Completed',
      properties: ['orderId', 'totalAmount', 'items']
    }
  ]

  private currentStep: number = 0
  private userJourney: ConversionEvent[] = []

  constructor() {
    this.initializeTracking()
  }

  private initializeTracking() {
    // Track product views
    this.trackProductViews()

    // Track cart interactions
    this.trackCartInteractions()

    // Track checkout process
    this.trackCheckoutProcess()

    // Track order completion
    this.trackOrderCompletion()
  }

  private trackProductViews() {
    // Track when product pages are viewed
    const trackProductView = (productId: string, productName: string, price: number, category: string) => {
      this.recordFunnelEvent('Product Viewed', {
        productId,
        productName,
        price,
        category,
        timestamp: Date.now()
      })
    }

    // Example: Call this when product page loads
    // trackProductView('prod-123', 'Wireless Headphones', 99.99, 'Electronics')
  }

  private trackCartInteractions() {
    // Track add to cart events
    window.addEventListener('click', (event) => {
      const target = event.target as HTMLElement
      if (target.matches('[data-add-to-cart]')) {
        const productId = target.getAttribute('data-product-id')
        const productName = target.getAttribute('data-product-name')
        const price = parseFloat(target.getAttribute('data-price') || '0')
        const quantity = parseInt(target.getAttribute('data-quantity') || '1')

        this.recordFunnelEvent('Product Added to Cart', {
          productId,
          productName,
          price,
          quantity,
          timestamp: Date.now()
        })
      }
    })

    // Track cart view
    const trackCartView = () => {
      const cartItems = this.getCartItems()
      const cartValue = cartItems.reduce((total, item) => total + (item.price * item.quantity), 0)

      this.recordFunnelEvent('Cart Viewed', {
        cartValue,
        itemCount: cartItems.length,
        items: cartItems,
        timestamp: Date.now()
      })
    }

    // Call trackCartView() when cart page is visited
  }

  private trackCheckoutProcess() {
    // Track checkout start
    const trackCheckoutStart = () => {
      const cartItems = this.getCartItems()
      const cartValue = cartItems.reduce((total, item) => total + (item.price * item.quantity), 0)

      this.recordFunnelEvent('Checkout Started', {
        cartValue,
        itemCount: cartItems.length,
        items: cartItems,
        timestamp: Date.now()
      })
    }

    // Track payment method selection
    const trackPaymentMethod = (paymentMethod: string) => {
      this.recordFunnelEvent('Payment Method Selected', {
        paymentMethod,
        timestamp: Date.now()
      })
    }

    // Track shipping info entered
    const trackShippingInfo = (shippingData: any) => {
      this.recordFunnelEvent('Shipping Info Entered', {
        shippingMethod: shippingData.method,
        shippingAddress: shippingData.address,
        timestamp: Date.now()
      })
    }

    // Track payment info entered
    const trackPaymentInfo = (paymentMethod: string) => {
      this.recordFunnelEvent('Payment Info Entered', {
        paymentMethod,
        timestamp: Date.now()
      })
    }
  }

  private trackOrderCompletion() {
    const trackOrderCompletion = (orderData: {
      orderId: string
      totalAmount: number
      items: any[]
      paymentMethod: string
      shippingMethod: string
    }) => {
      this.recordFunnelEvent('Order Completed', {
        orderId: orderData.orderId,
        totalAmount: orderData.totalAmount,
        items: orderData.items,
        paymentMethod: orderData.paymentMethod,
        shippingMethod: orderData.shippingMethod,
        timestamp: Date.now()
      })

      // Track conversion value
      this.recordConversionValue(orderData.totalAmount)
    }
  }

  private recordFunnelEvent(eventName: string, properties: Record<string, any>) {
    // Send to FullStory
    FS.event(eventName, properties)

    // Record in internal journey
    const event: ConversionEvent = {
      step: eventName,
      userId: this.getCurrentUserId(),
      sessionId: this.getCurrentSessionId(),
      timestamp: properties.timestamp,
      properties
    }

    this.userJourney.push(event)
    this.updateCurrentStep(eventName)

    console.log('Funnel event recorded:', event)
  }

  private updateCurrentStep(eventName: string) {
    const stepIndex = this.funnelSteps.findIndex(step => step.eventTrigger === eventName)
    if (stepIndex !== -1 && stepIndex > this.currentStep) {
      this.currentStep = stepIndex
    }
  }

  private recordConversionValue(value: number) {
    FS.event('Conversion Value Recorded', {
      value,
      currency: 'USD',
      timestamp: Date.now()
    })
  }

  private getCartItems() {
    // Implement cart retrieval logic
    // This would typically get cart data from your state management or API
    return [
      {
        productId: 'prod-123',
        productName: 'Wireless Headphones',
        price: 99.99,
        quantity: 1
      }
    ]
  }

  private getCurrentUserId(): string {
    // Get current user ID from your authentication system
    return 'user-123'
  }

  private getCurrentSessionId(): string {
    // FullStory provides session ID
    return (window as any).FS?.getCurrentSession?.()?.session?.id || 'unknown'
  }

  // Generate funnel analysis
  public generateFunnelReport() {
    const funnelData = this.analyzeFunnelPerformance()

    FS.event('Funnel Analysis Generated', {
      totalSteps: this.funnelSteps.length,
      completionRate: funnelData.completionRate,
      dropOffPoints: funnelData.dropOffPoints,
      averageTime: funnelData.averageTime,
      timestamp: Date.now()
    })

    return funnelData
  }

  private analyzeFunnelPerformance() {
    const stepCounts: Record<string, number> = {}
    const stepTimes: Record<string, number[]> = {}

    // Count events per step and calculate times
    this.funnelSteps.forEach(step => {
      const stepEvents = this.userJourney.filter(event => event.step === step.eventTrigger)
      stepCounts[step.name] = stepEvents.length

      // Calculate time between steps
      if (step.stepOrder > 1) {
        const previousStep = this.funnelSteps.find(s => s.stepOrder === step.stepOrder - 1)
        if (previousStep) {
          const timeDiffs: number[] = []

          stepEvents.forEach(currentEvent => {
            const previousEvent = this.userJourney.find(
              event => event.step === previousStep.eventTrigger &&
                       event.userId === currentEvent.userId &&
                       event.timestamp < currentEvent.timestamp
            )

            if (previousEvent) {
              timeDiffs.push(currentEvent.timestamp - previousEvent.timestamp)
            }
          })

          stepTimes[step.name] = timeDiffs
        }
      }
    })

    // Calculate drop-off points
    const dropOffPoints: string[] = []
    for (let i = 0; i < this.funnelSteps.length - 1; i++) {
      const currentStepCount = stepCounts[this.funnelSteps[i].name] || 0
      const nextStepCount = stepCounts[this.funnelSteps[i + 1].name] || 0

      if (nextStepCount < currentStepCount) {
        const dropOffRate = ((currentStepCount - nextStepCount) / currentStepCount) * 100
        dropOffPoints.push(`${this.funnelSteps[i].name} → ${this.funnelSteps[i + 1].name}: ${dropOffRate.toFixed(1)}%`)
      }
    }

    // Calculate overall completion rate
    const firstStepCount = stepCounts[this.funnelSteps[0].name] || 1
    const lastStepCount = stepCounts[this.funnelSteps[this.funnelSteps.length - 1].name] || 0
    const completionRate = (lastStepCount / firstStepCount) * 100

    // Calculate average time per step
    const averageTimes: Record<string, number> = {}
    Object.entries(stepTimes).forEach(([stepName, times]) => {
      if (times.length > 0) {
        averageTimes[stepName] = times.reduce((sum, time) => sum + time, 0) / times.length
      }
    })

    return {
      stepCounts,
      dropOffPoints,
      completionRate,
      averageTime: averageTimes,
      totalUsers: this.userJourney.filter((event, index, arr) =>
        arr.findIndex(e => e.userId === event.userId) === index
      ).length
    }
  }

  // Track user abandonment
  public trackAbandonment() {
    const abandonEvent = () => {
      const currentStepName = this.funnelSteps[this.currentStep]?.name || 'Unknown'

      FS.event('Funnel Abandoned', {
        currentStep: currentStepName,
        stepOrder: this.currentStep + 1,
        totalSteps: this.funnelSteps.length,
        journeyLength: this.userJourney.length,
        timestamp: Date.now()
      })
    }

    // Track abandonment on page unload
    window.addEventListener('beforeunload', abandonEvent)
  }
}

// Initialize the funnel tracker
const funnelTracker = new EcommerceFunnelTracker()
funnelTracker.trackAbandonment()

// Example usage
// funnelTracker.trackProductView('prod-123', 'Wireless Headphones', 99.99, 'Electronics')
// funnelTracker.generateFunnelReport()

export { EcommerceFunnelTracker, funnelTracker }

💻 Integración React con Hooks typescript

🟡 intermediate ⭐⭐⭐

Integración React moderna con hooks para seguimiento FullStory y análisis de comportamiento de usuario

⏱️ 35 min 🏷️ react, frontend, hooks, analytics, user-experience
Prerequisites: React hooks, TypeScript, FullStory API, Event tracking
// FullStory React Integration with Hooks
// Modern React hooks for comprehensive user experience tracking

import React, { useEffect, useCallback, useState, useRef } from 'react'

// Type definitions
interface UserTraits {
  displayName?: string
  email?: string
  plan?: string
  signUpDate?: string
  [key: string]: any
}

interface EventProperties {
  [key: string]: any
}

// Custom hook for FullStory user identification
export const useFullStoryUser = (userId: string, traits: UserTraits = {}) => {
  useEffect(() => {
    if (typeof window !== 'undefined' && window.FS) {
      window.FS.identify(userId, traits)
    }
  }, [userId, traits])
}

// Custom hook for FullStory event tracking
export const useFullStoryEvent = () => {
  const trackEvent = useCallback((eventName: string, properties: EventProperties = {}) => {
    if (typeof window !== 'undefined' && window.FS) {
      window.FS.event(eventName, {
        ...properties,
        timestamp: Date.now(),
        url: window.location.href,
        userAgent: navigator.userAgent
      })
    }
  }, [])

  return { trackEvent }
}

// Custom hook for tracking page views
export const useFullStoryPageView = (pageName?: string) => {
  const { trackEvent } = useFullStoryEvent()

  useEffect(() => {
    const pageNameToUse = pageName || window.location.pathname
    trackEvent('Page Viewed', {
      page: pageNameToUse,
      title: document.title,
      referrer: document.referrer
    })
  }, [trackEvent, pageName])
}

// Custom hook for tracking form interactions
export const useFullStoryFormTracking = (formId: string) => {
  const { trackEvent } = useFullStoryEvent()
  const [formData, setFormData] = useState<Record<string, any>>({})

  const trackFieldFocus = useCallback((fieldName: string, fieldType: string) => {
    trackEvent('Form Field Focused', {
      formId,
      fieldName,
      fieldType,
      timestamp: Date.now()
    })
  }, [formId, trackEvent])

  const trackFieldBlur = useCallback((fieldName: string, value: string) => {
    setFormData(prev => ({ ...prev, [fieldName]: value }))
    trackEvent('Form Field Blurred', {
      formId,
      fieldName,
      hasValue: value.length > 0,
      timestamp: Date.now()
    })
  }, [formId, trackEvent])

  const trackFormSubmit = useCallback((data: Record<string, any>) => {
    trackEvent('Form Submitted', {
      formId,
      fieldCount: Object.keys(data).length,
      hasValidationErrors: Object.values(data).some(val => !val),
      timestamp: Date.now()
    })
  }, [formId, trackEvent])

  const trackValidationError = useCallback((fieldName: string, errorMessage: string) => {
    trackEvent('Form Validation Error', {
      formId,
      fieldName,
      errorMessage,
      timestamp: Date.now()
    })
  }, [formId, trackEvent])

  return {
    formData,
    trackFieldFocus,
    trackFieldBlur,
    trackFormSubmit,
    trackValidationError
  }
}

// Custom hook for tracking user engagement
export const useFullStoryEngagement = () => {
  const { trackEvent } = useFullStoryEvent()
  const [sessionStartTime] = useState(Date.now())
  const [lastActivityTime, setLastActivityTime] = useState(Date.now())
  const [totalActiveTime, setTotalActiveTime] = useState(0)

  const trackClick = useCallback((element: HTMLElement, x: number, y: number) => {
    trackEvent('Element Clicked', {
      tagName: element.tagName,
      className: element.className,
      id: element.id,
      textContent: element.textContent?.slice(0, 50),
      x,
      y,
      timestamp: Date.now()
    })
  }, [trackEvent])

  const trackScroll = useCallback((scrollPercentage: number, direction: 'up' | 'down') => {
    trackEvent('Page Scrolled', {
      scrollPercentage,
      direction,
      timestamp: Date.now()
    })
  }, [trackEvent])

  const trackEngagementEnd = useCallback(() => {
    const sessionDuration = Date.now() - sessionStartTime
    trackEvent('Session Ended', {
      totalDuration: sessionDuration,
      activeTime: totalActiveTime,
      lastActivity: Date.now() - lastActivityTime,
      timestamp: Date.now()
    })
  }, [sessionStartTime, totalActiveTime, lastActivityTime, trackEvent])

  // Update activity tracking
  useEffect(() => {
    const updateActivity = () => {
      const now = Date.now()
      const inactiveTime = now - lastActivityTime

      if (inactiveTime < 30000) { // Consider active if less than 30 seconds inactive
        setTotalActiveTime(prev => prev + Math.min(1000, inactiveTime))
      }

      setLastActivityTime(now)
    }

    // Track user interactions
    const events = ['click', 'scroll', 'keydown', 'mousemove']
    events.forEach(event => {
      document.addEventListener(event, updateActivity, { passive: true })
    })

    // Track session end
    window.addEventListener('beforeunload', trackEngagementEnd)

    return () => {
      events.forEach(event => {
        document.removeEventListener(event, updateActivity)
      })
      window.removeEventListener('beforeunload', trackEngagementEnd)
    }
  }, [lastActivityTime, trackEngagementEnd])

  return {
    trackClick,
    trackScroll,
    trackEngagementEnd
  }
}

// Custom hook for A/B testing integration
export const useFullStoryABTesting = (experimentName: string, variant?: string) => {
  const { trackEvent } = useFullStoryEvent()

  useEffect(() => {
    if (variant) {
      trackEvent('A/B Test Started', {
        experimentName,
        variant,
        timestamp: Date.now()
      })

      // Set user properties for segmentation
      if (window.FS) {
        window.FS.setUserVars({
          [`ab_${experimentName}`]: variant
        })
      }
    }
  }, [experimentName, variant, trackEvent])

  const trackConversion = useCallback((conversionType: string, value?: number) => {
    trackEvent('A/B Test Conversion', {
      experimentName,
      variant,
      conversionType,
      value,
      timestamp: Date.now()
    })
  }, [experimentName, variant, trackEvent])

  return { trackConversion }
}

// Higher-order component for automatic tracking
export const withFullStoryTracking = <P extends object>(
  WrappedComponent: React.ComponentType<P>,
  options: {
    trackPageView?: boolean
    trackClicks?: boolean
    componentName?: string
  } = {}
) => {
  const HOC = (props: P) => {
    const { trackEvent } = useFullStoryEvent()
    const { trackClick } = useFullStoryEngagement()
    const elementRef = useRef<HTMLDivElement>(null)

    // Track page view
    useFullStoryPageView(options.componentName)

    // Track clicks on component
    useEffect(() => {
      if (options.trackClicks && elementRef.current) {
        const handleClick = (event: MouseEvent) => {
          const target = event.target as HTMLElement
          trackClick(target, event.clientX, event.clientY)
        }

        const element = elementRef.current
        element.addEventListener('click', handleClick)

        return () => {
          element.removeEventListener('click', handleClick)
        }
      }
    }, [trackClick])

    return (
      <div ref={elementRef}>
        <WrappedComponent {...props} />
      </div>
    )
  }

  HOC.displayName = `withFullStoryTracking(${WrappedComponent.displayName || WrappedComponent.name})`
  return HOC
}

// Example usage components

// User identification component
const UserProfile: React.FC<{ userId: string; name: string; email: string }> = ({ userId, name, email }) => {
  useFullStoryUser(userId, {
    displayName: name,
    email,
    plan: 'premium',
    lastLogin: new Date().toISOString()
  })

  return <div>Welcome, {name}!</div>
}

// Contact form with tracking
const ContactForm: React.FC = () => {
  const { formData, trackFieldFocus, trackFieldBlur, trackFormSubmit, trackValidationError } = useFullStoryFormTracking('contact-form')
  const { trackEvent } = useFullStoryEvent()

  const handleSubmit = (event: React.FormEvent) => {
    event.preventDefault()

    // Validate form
    if (!formData.name || !formData.email) {
      trackValidationError('name', !formData.name ? 'Name is required' : '')
      trackValidationError('email', !formData.email ? 'Email is required' : '')
      return
    }

    trackFormSubmit(formData)
    trackEvent('Contact Form Submitted', {
      name: formData.name,
      email: formData.email,
      message: formData.message?.length > 0
    })

    // Handle form submission...
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Name"
        onFocus={() => trackFieldFocus('name', 'text')}
        onBlur={(e) => trackFieldBlur('name', e.target.value)}
      />
      <input
        type="email"
        placeholder="Email"
        onFocus={() => trackFieldFocus('email', 'email')}
        onBlur={(e) => trackFieldBlur('email', e.target.value)}
      />
      <textarea
        placeholder="Message"
        onFocus={() => trackFieldFocus('message', 'textarea')}
        onBlur={(e) => trackFieldBlur('message', e.target.value)}
      />
      <button type="submit">Send</button>
    </form>
  )
}

// A/B testing example
const CheckoutButton: React.FC<{ variant: 'A' | 'B' }> = ({ variant }) => {
  const { trackConversion } = useFullStoryABTesting('checkout_button_test', variant)

  const handleClick = () => {
    trackConversion('checkout_started', 1)
    // Handle checkout logic...
  }

  return (
    <button
      onClick={handleClick}
      style={{
        backgroundColor: variant === 'A' ? '#007bff' : '#28a745',
        color: 'white',
        padding: '12px 24px',
        border: 'none',
        borderRadius: '4px',
        cursor: 'pointer'
      }}
    >
      {variant === 'A' ? 'Buy Now' : 'Proceed to Checkout'}
    </button>
  )
}

// Wrap a component with tracking
const TrackedComponent = withFullStoryTracking(
  () => <div>This component is automatically tracked</div>,
  {
    trackPageView: true,
    trackClicks: true,
    componentName: 'TrackedComponent'
  }
)

export { UserProfile, ContactForm, CheckoutButton, TrackedComponent }

💻 Segmentación Avanzada de Usuarios typescript

🔴 complex ⭐⭐⭐⭐⭐

Crear segmentos de usuario basados en patrones de comportamiento, demografía y participación

⏱️ 60 min 🏷️ frontend, analytics, segmentation, user-behavior
Prerequisites: TypeScript, FullStory API, Data analysis, Behavioral analytics
// FullStory Advanced User Segmentation
// Dynamic user segmentation based on behavior patterns and characteristics

interface UserSegment {
  name: string
  id: string
  criteria: SegmentCriteria
  users: string[]
  properties: SegmentProperties
}

interface SegmentCriteria {
  demographics?: DemographicCriteria
  behavior?: BehaviorCriteria
  engagement?: EngagementCriteria
  technical?: TechnicalCriteria
  custom?: CustomCriteria
}

interface DemographicCriteria {
  country?: string[]
  language?: string[]
  ageRange?: { min: number; max: number }
  plan?: string[]
  signUpDateRange?: { start: Date; end: Date }
}

interface BehaviorCriteria {
  pageViews?: {
    pages: string[]
    count?: { min: number; max?: number }
    timeframe?: number // days
  }
  events?: {
    types: string[]
    count?: { min: number; max?: number }
    timeframe?: number
  }
  conversions?: {
    types: string[]
    totalValue?: { min: number; max?: number }
    timeframe?: number
  }
}

interface EngagementCriteria {
  sessionDuration?: { min: number; max?: number }
  sessionsPerWeek?: { min: number; max?: number }
  lastActivity?: { days: number }
  scrollDepth?: { percentage: number }
  clicksPerSession?: { min: number; max?: number }
}

interface TechnicalCriteria {
  browser?: string[]
  device?: string[]
  os?: string[]
  screenResolution?: { min: { width: number; height: number }; max?: { width: number; height: number } }
  connectionSpeed?: string[]
}

interface CustomCriteria {
  [key: string]: any
}

interface SegmentProperties {
  size: number
  conversionRate: number
  averageOrderValue: number
  retentionRate: number
  churnRisk: 'low' | 'medium' | 'high'
  lifetimeValue: number
  createdAt: Date
  updatedAt: Date
}

class UserSegmentationEngine {
  private segments: Map<string, UserSegment> = new Map()
  private userProfiles: Map<string, UserProfile> = new Map()
  private eventHistory: Map<string, UserEvent[]> = new Map()

  constructor() {
    this.initializeDefaultSegments()
    this.startRealTimeSegmentation()
  }

  private initializeDefaultSegments() {
    // Power users segment
    this.createSegment({
      name: 'Power Users',
      id: 'power-users',
      criteria: {
        engagement: {
          sessionDuration: { min: 600000 }, // 10 minutes
          sessionsPerWeek: { min: 5 },
          lastActivity: { days: 7 }
        },
        behavior: {
          pageViews: {
            count: { min: 50 },
            timeframe: 30
          },
          events: {
            types: ['conversion', 'feature_used'],
            count: { min: 10 },
            timeframe: 30
          }
        }
      },
      properties: {
        size: 0,
        conversionRate: 0,
        averageOrderValue: 0,
        retentionRate: 0,
        churnRisk: 'low',
        lifetimeValue: 0,
        createdAt: new Date(),
        updatedAt: new Date()
      }
    })

    // At-risk users segment
    this.createSegment({
      name: 'At Risk Users',
      id: 'at-risk-users',
      criteria: {
        engagement: {
          sessionDuration: { max: 120000 }, // 2 minutes
          sessionsPerWeek: { max: 2 },
          lastActivity: { days: 14 }
        },
        behavior: {
          events: {
            types: ['error', 'frustration'],
            count: { min: 3 },
            timeframe: 14
          }
        }
      },
      properties: {
        size: 0,
        conversionRate: 0,
        averageOrderValue: 0,
        retentionRate: 0,
        churnRisk: 'high',
        lifetimeValue: 0,
        createdAt: new Date(),
        updatedAt: new Date()
      }
    })

    // Mobile-first users segment
    this.createSegment({
      name: 'Mobile-First Users',
      id: 'mobile-users',
      criteria: {
        technical: {
          device: ['mobile', 'tablet']
        },
        behavior: {
          pageViews: {
            count: { min: 20 },
            timeframe: 30
          }
        }
      },
      properties: {
        size: 0,
        conversionRate: 0,
        averageOrderValue: 0,
        retentionRate: 0,
        churnRisk: 'medium',
        lifetimeValue: 0,
        createdAt: new Date(),
        updatedAt: new Date()
      }
    })
  }

  createSegment(segmentData: Omit<UserSegment, 'users'>): UserSegment {
    const segment: UserSegment = {
      ...segmentData,
      users: []
    }

    this.segments.set(segment.id, segment)
    this.updateSegmentInFullStory(segment)

    FS.event('User Segment Created', {
      segmentId: segment.id,
      segmentName: segment.name,
      criteria: segment.criteria,
      timestamp: Date.now()
    })

    return segment
  }

  private updateSegmentInFullStory(segment: UserSegment) {
    // Update FullStory with segment information
    segment.users.forEach(userId => {
      const userProfile = this.userProfiles.get(userId)
      if (userProfile) {
        FS.setUserVars({
          userId,
          segments: userProfile.segments.map(segId => this.segments.get(segId)?.name).filter(Boolean)
        })
      }
    })
  }

  addUserToSegment(userId: string, segmentId: string): boolean {
    const segment = this.segments.get(segmentId)
    if (!segment) return false

    if (!segment.users.includes(userId)) {
      segment.users.push(userId)

      const userProfile = this.userProfiles.get(userId) || {
        id: userId,
        segments: [],
        properties: {},
        events: []
      }

      if (!userProfile.segments.includes(segmentId)) {
        userProfile.segments.push(segmentId)
        this.userProfiles.set(userId, userProfile)
      }

      this.updateSegmentProperties(segment)
      this.updateSegmentInFullStory(segment)

      FS.event('User Added to Segment', {
        userId,
        segmentId,
        segmentName: segment.name,
        totalUsers: segment.users.length,
        timestamp: Date.now()
      })

      return true
    }

    return false
  }

  removeUserFromSegment(userId: string, segmentId: string): boolean {
    const segment = this.segments.get(segmentId)
    if (!segment) return false

    const userIndex = segment.users.indexOf(userId)
    if (userIndex !== -1) {
      segment.users.splice(userIndex, 1)

      const userProfile = this.userProfiles.get(userId)
      if (userProfile) {
        const segmentIndex = userProfile.segments.indexOf(segmentId)
        if (segmentIndex !== -1) {
          userProfile.segments.splice(segmentIndex, 1)
        }
      }

      this.updateSegmentProperties(segment)
      this.updateSegmentInFullStory(segment)

      FS.event('User Removed from Segment', {
        userId,
        segmentId,
        segmentName: segment.name,
        totalUsers: segment.users.length,
        timestamp: Date.now()
      })

      return true
    }

    return false
  }

  private updateSegmentProperties(segment: UserSegment) {
    segment.properties.size = segment.users.length
    segment.properties.updatedAt = new Date()

    // Calculate conversion rate
    let conversions = 0
    let totalOrderValue = 0
    let activeUsers = 0

    segment.users.forEach(userId => {
      const userEvents = this.eventHistory.get(userId) || []

      // Count conversions
      const userConversions = userEvents.filter(event =>
        event.type.includes('conversion') || event.type.includes('purchase')
      ).length

      if (userConversions > 0) conversions++

      // Calculate order value
      userEvents.forEach(event => {
        if (event.properties.value) {
          totalOrderValue += event.properties.value
        }
      })

      // Check if user is active (last 30 days)
      const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000)
      if (userEvents.some(event => event.timestamp > thirtyDaysAgo)) {
        activeUsers++
      }
    })

    segment.properties.conversionRate = (conversions / segment.users.length) * 100
    segment.properties.averageOrderValue = totalOrderValue / Math.max(1, conversions)
    segment.properties.retentionRate = (activeUsers / segment.users.length) * 100

    // Calculate lifetime value (simplified)
    segment.properties.lifetimeValue = totalOrderValue / Math.max(1, segment.users.length)

    // Update churn risk based on retention
    if (segment.properties.retentionRate > 80) {
      segment.properties.churnRisk = 'low'
    } else if (segment.properties.retentionRate > 50) {
      segment.properties.churnRisk = 'medium'
    } else {
      segment.properties.churnRisk = 'high'
    }
  }

  private startRealTimeSegmentation() {
    // Listen for FullStory events
    if (typeof window !== 'undefined' && window.FS) {
      // Hook into FullStory event system
      const originalEvent = window.FS.event

      window.FS.event = (eventName: string, eventProperties: any) => {
        // Call original event
        originalEvent(eventName, eventProperties)

        // Process event for segmentation
        this.processEventForSegmentation(eventName, eventProperties)
      }
    }
  }

  private processEventForSegmentation(eventName: string, properties: any) {
    // Get user ID from event properties or session
    const userId = properties.userId || this.getCurrentUserId()

    if (!userId) return

    // Record event
    const event: UserEvent = {
      type: eventName,
      timestamp: Date.now(),
      properties
    }

    const userEvents = this.eventHistory.get(userId) || []
    userEvents.push(event)
    this.eventHistory.set(userId, userEvents)

    // Check if user should be added to any segments
    this.evaluateUserForSegments(userId)
  }

  private evaluateUserForSegments(userId: string) {
    const userEvents = this.eventHistory.get(userId) || []
    const userProfile = this.userProfiles.get(userId) || {
      id: userId,
      segments: [],
      properties: {},
      events: userEvents
    }

    // Evaluate against all segments
    this.segments.forEach((segment, segmentId) => {
      const qualifies = this.evaluateUserAgainstSegment(userId, segment.criteria)

      const currentlyInSegment = segment.users.includes(userId)

      if (qualifies && !currentlyInSegment) {
        this.addUserToSegment(userId, segmentId)
      } else if (!qualifies && currentlyInSegment) {
        this.removeUserFromSegment(userId, segmentId)
      }
    })
  }

  private evaluateUserAgainstSegment(userId: string, criteria: SegmentCriteria): boolean {
    const userEvents = this.eventHistory.get(userId) || []

    // Evaluate demographic criteria
    if (criteria.demographics) {
      // Implement demographic evaluation logic
      // This would require access to user demographic data
    }

    // Evaluate behavior criteria
    if (criteria.behavior) {
      if (criteria.behavior.pageViews) {
        const pageViewEvents = userEvents.filter(event => event.type === 'Page Viewed')
        const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000)
        const recentPageViews = pageViewEvents.filter(event => event.timestamp > thirtyDaysAgo)

        if (criteria.behavior.pageViews.count) {
          const { min, max } = criteria.behavior.pageViews.count
          if (recentPageViews.length < min) return false
          if (max && recentPageViews.length > max) return false
        }
      }

      if (criteria.behavior.events) {
        const matchingEvents = userEvents.filter(event =>
          criteria.behavior!.events!.types.includes(event.type)
        )

        if (criteria.behavior.events.count) {
          const { min, max } = criteria.behavior.events.count
          if (matchingEvents.length < min) return false
          if (max && matchingEvents.length > max) return false
        }
      }
    }

    // Evaluate engagement criteria
    if (criteria.engagement) {
      // Implement engagement evaluation logic
      // This would require session duration tracking, frequency analysis, etc.
    }

    return true
  }

  private getCurrentUserId(): string | null {
    // Get current user ID from your authentication system
    // This is a placeholder implementation
    return localStorage.getItem('currentUserId')
  }

  // Get segment analytics
  getSegmentAnalytics(segmentId: string): SegmentProperties | null {
    const segment = this.segments.get(segmentId)
    return segment ? segment.properties : null
  }

  // Get all segments
  getAllSegments(): UserSegment[] {
    return Array.from(this.segments.values())
  }

  // Export segment data
  exportSegmentData(segmentId: string): any {
    const segment = this.segments.get(segmentId)
    if (!segment) return null

    return {
      segment: {
        id: segment.id,
        name: segment.name,
        criteria: segment.criteria,
        properties: segment.properties
      },
      users: segment.users.map(userId => ({
        id: userId,
        profile: this.userProfiles.get(userId),
        events: this.eventHistory.get(userId) || []
      }))
    }
  }

  // Create segment from FullStory search
  async createSegmentFromSearch(searchQuery: string, segmentName: string): Promise<UserSegment> {
    // This would use FullStory's search API to find matching users
    // For now, we'll simulate with a basic implementation

    const segment = this.createSegment({
      name: segmentName,
      id: segmentName.toLowerCase().replace(/\s+/g, '-'),
      criteria: {
        custom: {
          fullstorySearch: searchQuery
        }
      },
      properties: {
        size: 0,
        conversionRate: 0,
        averageOrderValue: 0,
        retentionRate: 0,
        churnRisk: 'medium',
        lifetimeValue: 0,
        createdAt: new Date(),
        updatedAt: new Date()
      }
    })

    FS.event('Segment Created from Search', {
      segmentId: segment.id,
      searchQuery,
      timestamp: Date.now()
    })

    return segment
  }
}

// Type definitions
interface UserProfile {
  id: string
  segments: string[]
  properties: Record<string, any>
  events: UserEvent[]
}

interface UserEvent {
  type: string
  timestamp: number
  properties: Record<string, any>
}

// Initialize the segmentation engine
const segmentationEngine = new UserSegmentationEngine()

// Example usage
/*
// Create a custom segment
const highValueCustomers = segmentationEngine.createSegment({
  name: 'High Value Customers',
  id: 'high-value-customers',
  criteria: {
    behavior: {
      conversions: {
        types: ['purchase', 'upgrade'],
        totalValue: { min: 1000 },
        timeframe: 90
      }
    },
    engagement: {
      sessionsPerWeek: { min: 3 },
      lastActivity: { days: 7 }
    }
  },
  properties: {
    size: 0,
    conversionRate: 0,
    averageOrderValue: 0,
    retentionRate: 0,
    churnRisk: 'low',
    lifetimeValue: 0,
    createdAt: new Date(),
    updatedAt: new Date()
  }
})

// Get segment analytics
const analytics = segmentationEngine.getSegmentAnalytics('high-value-customers')
console.log('High value customers:', analytics)
*/

export { UserSegmentationEngine, segmentationEngine }