import fetch from 'node-fetch';
import { TimeManager } from '../utils/time.utils.js';
import { RedisService } from './redis.service.js';
import _ from 'lodash';

const METRIC_IDS = {
  PLACED_ORDER: 'Y8cqcF',
  SHIPPED_ORDER: 'VExpdL',
  ACCOUNT_CREATED: 'TeeypV',
  CANCELED_ORDER: 'YjVMNg',
  NEW_BLOG_POST: 'YcxeDr',
  PAYMENT_REFUNDED: 'R7XUYh'
};

export class EventsService {
  constructor(apiKey, apiRevision) {
    this.apiKey = apiKey;
    this.apiRevision = apiRevision;
    this.baseUrl = 'https://a.klaviyo.com/api';
    this.timeManager = new TimeManager();
    this.redisService = new RedisService();
  }

  async getEvents(params = {}) {
    try {
      // Add request debouncing
      const requestKey = JSON.stringify(params);
      if (this._pendingRequests && this._pendingRequests[requestKey]) {
        return this._pendingRequests[requestKey];
      }

      // Try to get from cache first
      const cacheKey = this.redisService._getCacheKey('events', params);
      let cachedData = null;
      try {
        cachedData = await this.redisService.get(`${cacheKey}:raw`);
        if (cachedData) {
          cachedData.data = this._transformEvents(cachedData.data);
          return cachedData;
        }
      } catch (cacheError) {
        console.warn('[EventsService] Cache error:', cacheError);
      }

      this._pendingRequests = this._pendingRequests || {};
      this._pendingRequests[requestKey] = (async () => {
        let allEvents = [];
        let nextCursor = params.pageCursor;
        let pageCount = 0;
        
        const filter = params.filter || this._buildFilter(params);
        
        do {
          const queryParams = new URLSearchParams();
          if (filter) {
            queryParams.append('filter', filter);
          }
          queryParams.append('sort', params.sort || '-datetime');

          if (nextCursor) {
            queryParams.append('page[cursor]', nextCursor);
          }

          const url = `${this.baseUrl}/events?${queryParams.toString()}`;

          try {
            const response = await fetch(url, {
              method: 'GET',
              headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json',
                'Authorization': `Klaviyo-API-Key ${this.apiKey}`,
                'revision': this.apiRevision
              }
            });

            if (!response.ok) {
              const errorData = await response.json();
              console.error('[EventsService] API Error:', errorData);
              throw new Error(`Klaviyo API error: ${response.status} ${response.statusText}`);
            }

            const responseData = await response.json();
            allEvents = allEvents.concat(responseData.data || []);
            pageCount++;
            
            nextCursor = responseData.links?.next ? 
              new URL(responseData.links.next).searchParams.get('page[cursor]') : null;

            if (nextCursor) {
              await new Promise(resolve => setTimeout(resolve, 50));
            }
          } catch (fetchError) {
            console.error('[EventsService] Fetch error:', fetchError);
            throw fetchError;
          }
          
        } while (nextCursor);

        const transformedEvents = this._transformEvents(allEvents);

        const result = {
          data: transformedEvents,
          meta: {
            total_count: transformedEvents.length,
            page_count: pageCount
          }
        };

        try {
          const ttl = this.redisService._getTTL(params.timeRange);
          await this.redisService.set(`${cacheKey}:raw`, result, ttl);
        } catch (cacheError) {
          console.warn('[EventsService] Cache set error:', cacheError);
        }

        delete this._pendingRequests[requestKey];
        return result;
      })();

      return await this._pendingRequests[requestKey];
    } catch (error) {
      console.error('[EventsService] Error fetching events:', error);
      throw error;
    }
  }

  _buildFilter(params) {
    const filters = [];
    
    if (params.metricId) {
      filters.push(`equals(metric_id,"${params.metricId}")`);
    }

    if (params.startDate && params.endDate) {
      const startUtc = this.timeManager.formatForAPI(params.startDate);
      const endUtc = this.timeManager.formatForAPI(params.endDate);
      
      filters.push(`greater-or-equal(datetime,${startUtc})`);
      filters.push(`less-than(datetime,${endUtc})`);
    }

    if (params.profileId) {
      filters.push(`equals(profile_id,"${params.profileId}")`);
    }

    if (params.customFilters) {
      filters.push(...params.customFilters);
    }

    return filters.length > 0 ? (filters.length > 1 ? `and(${filters.join(',')})` : filters[0]) : null;
  }

  async getEventsByTimeRange(timeRange, options = {}) {
    const range = this.timeManager.getDateRange(timeRange);
    if (!range) {
      throw new Error('Invalid time range specified');
    }

    const params = {
      timeRange,
      startDate: range.start.toISO(),
      endDate: range.end.toISO(),
      metricId: options.metricId
    };

    // Try to get from cache first
    const cacheKey = this.redisService._getCacheKey('events', params);
    let cachedData = null;
    try {
      cachedData = await this.redisService.get(`${cacheKey}:raw`);
      if (cachedData) {
        // Transform cached events
        cachedData.data = this._transformEvents(cachedData.data);
        return cachedData;
      }
    } catch (cacheError) {
      console.warn('[EventsService] Cache error:', cacheError);
      // Continue with API request if cache fails
    }

    return this.getEvents(params);
  }

  async calculatePeriodStats(params = {}) {
    try {
      // Add request debouncing
      const requestKey = JSON.stringify(params);
      if (this._pendingStatRequests && this._pendingStatRequests[requestKey]) {
        return this._pendingStatRequests[requestKey];
      }

      // Get period dates
      let periodStart, periodEnd, prevPeriodStart, prevPeriodEnd;
      if (params.startDate && params.endDate) {
        periodStart = this.timeManager.toDateTime(params.startDate);
        periodEnd = this.timeManager.toDateTime(params.endDate);
        const duration = periodEnd.diff(periodStart);
        prevPeriodStart = periodStart.minus(duration);
        prevPeriodEnd = periodStart.minus({ milliseconds: 1 });
      } else if (params.timeRange) {
        const range = this.timeManager.getDateRange(params.timeRange);
        periodStart = range.start;
        periodEnd = range.end;
        const duration = periodEnd.diff(periodStart);
        prevPeriodStart = periodStart.minus(duration);
        prevPeriodEnd = periodStart.minus({ milliseconds: 1 });
      }

      // Load both current and previous period data
      const [orderData, shippedData, refundData, canceledData, prevPeriodData] = await Promise.all([
        this.getEvents({
          ...params,
          metricId: METRIC_IDS.PLACED_ORDER
        }),
        this.getEvents({
          ...params,
          metricId: METRIC_IDS.SHIPPED_ORDER
        }),
        this.getEvents({
          ...params,
          metricId: METRIC_IDS.PAYMENT_REFUNDED
        }),
        this.getEvents({
          ...params,
          metricId: METRIC_IDS.CANCELED_ORDER
        }),
        this.getEvents({
          // Only pass through non-date related params for previous period
          ..._.omit(params, ['timeRange', 'startDate', 'endDate']),
          metricId: METRIC_IDS.PLACED_ORDER,
          startDate: prevPeriodStart.toISO(),
          endDate: prevPeriodEnd.toISO()
        })
      ]);

      // Add debug logging
      console.log('[EventsService] Previous period request:', {
        params: _.omit(params, ['timeRange', 'startDate', 'endDate']),
        dates: {
          start: prevPeriodStart.toISO(),
          end: prevPeriodEnd.toISO()
        },
        responseLength: prevPeriodData?.data?.length
      });

      // Transform all data
      const transformedOrders = this._transformEvents(orderData.data);
      const transformedShipped = this._transformEvents(shippedData.data);
      const transformedRefunds = this._transformEvents(refundData.data);
      const transformedCanceled = this._transformEvents(canceledData.data);
      const transformedPrevPeriod = this._transformEvents(prevPeriodData.data);

      // Calculate previous period stats
      const prevPeriodRevenue = transformedPrevPeriod.reduce((sum, order) => {
        const props = order.event_properties || {};
        return sum + Number(props.TotalAmount || 0);
      }, 0);

      const prevPeriodOrders = transformedPrevPeriod.length;
      const prevPeriodAOV = prevPeriodOrders > 0 ? prevPeriodRevenue / prevPeriodOrders : 0;

      // Calculate stats with all data available
      const stats = {
        orderCount: 0,
        revenue: 0,
        averageOrderValue: 0,
        itemCount: 0,
        prevPeriodRevenue,
        projectedRevenue: 0,
        periodProgress: 0,
        dailyData: [],
        products: {
          list: [],
          total: 0,
          status: {
            backordered: 0,
            inStock: 0,
            outOfStock: 0,
            preorder: 0
          }
        },
        shipping: {
          shippedCount: 0,
          locations: {
            byState: [],
            byCountry: []
          },
          methods: {},
          methodPercentages: {},
          totalRevenue: 0,
          averageShipTime: 0,
          totalShipTime: 0
        },
        refunds: {
          total: 0,
          count: 0,
          reasons: {},
          items: [],
          averageAmount: 0
        },
        canceledOrders: {
          total: 0,
          count: 0,
          reasons: {},
          items: [],
          averageAmount: 0
        },
        orderTypes: {
          preOrders: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
          localPickup: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
          heldItems: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
          digital: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
          giftCard: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 }
        },
        brands: {
          total: 0,
          list: [],
          topBrands: [],
          totalRevenue: 0,
          averageOrderValue: 0
        },
        categories: {
          total: 0,
          list: [],
          topCategories: [],
          totalRevenue: 0,
          averageOrderValue: 0
        },
        hourlyOrders: Array(24).fill(0),
        peakOrderHour: null,
        bestRevenueDay: null,
        orderValueRange: {
          largest: 0,
          smallest: 0,
          largestOrderId: null,
          smallestOrderId: null,
          distribution: {
            under25: { count: 0, total: 0 },
            under50: { count: 0, total: 0 },
            under100: { count: 0, total: 0 },
            under200: { count: 0, total: 0 },
            over200: { count: 0, total: 0 }
          }
        }
      };

      // Calculate period progress
      if (periodStart && periodEnd) {
        const totalDuration = periodEnd.diff(periodStart);
        const elapsedDuration = this.timeManager.getNow().diff(periodStart);
        stats.periodProgress = Math.min(100, Math.max(0, (elapsedDuration.milliseconds / totalDuration.milliseconds) * 100));
      }

      // Process orders
      const brandMap = new Map();
      const categoryMap = new Map();
      const dailyOrderCounts = {};
      const dailyStats = new Map();

      // Track best day stats
      let bestDay = {
        date: null,
        displayDate: null,
        amount: 0,
        orderCount: 0
      };

      // Initialize daily stats for the entire date range
      if (periodStart && periodEnd) {
        let currentDate = periodStart;
        while (currentDate <= periodEnd) {
          const dateKey = currentDate.toFormat('yyyy-MM-dd');
          dailyStats.set(dateKey, {
            date: currentDate.toISO(), // ISO format for chart library
            timestamp: dateKey,
            revenue: 0,
            orders: 0,
            itemCount: 0,
            averageOrderValue: 0,
            averageItemsPerOrder: 0,
            hourlyOrders: Array(24).fill(0),
            refunds: { total: 0, count: 0, reasons: {}, items: [] },
            canceledOrders: { total: 0, count: 0, reasons: {}, items: [] },
            orderTypes: {
              preOrders: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
              localPickup: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
              heldItems: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
              digital: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
              giftCard: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 }
            },
            orderValueRange: {
              largest: 0,
              smallest: 0,
              largestOrderId: null,
              smallestOrderId: null,
              distribution: {
                under25: { count: 0, total: 0 },
                under50: { count: 0, total: 0 },
                under100: { count: 0, total: 0 },
                under200: { count: 0, total: 0 },
                over200: { count: 0, total: 0 }
              }
            }
          });
          currentDate = currentDate.plus({ days: 1 });
        }
      }

      // Track peak hour stats
      let maxHourCount = 0;
      let peakHour = 0;

      for (const order of transformedOrders) {
        const props = order.event_properties || {};
        const items = props.Items || [];
        const totalAmount = Number(props.TotalAmount || 0);
        const datetime = this.timeManager.toDateTime(order.attributes?.datetime);
        const orderId = props.OrderId;

        // Update order counts and revenue
        stats.orderCount++;
        stats.revenue += totalAmount;
        stats.itemCount += items.length;

        // Calculate running AOV
        stats.averageOrderValue = stats.revenue / stats.orderCount;

        // Track daily stats
        if (datetime) {
          const dateKey = datetime.toFormat('yyyy-MM-dd');
          const hourOfDay = datetime.hour;
          
          // Initialize stats for this date if it doesn't exist
          if (!dailyStats.has(dateKey)) {
            dailyStats.set(dateKey, {
              date: datetime.toISO(), // ISO format for chart library
              timestamp: dateKey,
              revenue: 0,
              orders: 0,
              itemCount: 0,
              averageOrderValue: 0,
              averageItemsPerOrder: 0,
              hourlyOrders: Array(24).fill(0),
              refunds: { total: 0, count: 0, reasons: {}, items: [] },
              canceledOrders: { total: 0, count: 0, reasons: {}, items: [] },
              orderTypes: {
                preOrders: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
                localPickup: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
                heldItems: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
                digital: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
                giftCard: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 }
              },
              orderValueRange: {
                largest: 0,
                smallest: 0,
                largestOrderId: null,
                smallestOrderId: null,
                distribution: {
                  under25: { count: 0, total: 0 },
                  under50: { count: 0, total: 0 },
                  under100: { count: 0, total: 0 },
                  under200: { count: 0, total: 0 },
                  over200: { count: 0, total: 0 }
                }
              }
            });
          }

          const dayStats = dailyStats.get(dateKey);

          // Update daily stats
          dayStats.revenue += totalAmount;
          dayStats.orders++;
          dayStats.itemCount += items.length;
          dayStats.hourlyOrders[hourOfDay]++;
          dayStats.averageOrderValue = dayStats.revenue / dayStats.orders;
          dayStats.averageItemsPerOrder = dayStats.itemCount / dayStats.orders;

          // Track best day
          if (dayStats.revenue > bestDay.amount) {
            bestDay = {
              date: dateKey,
              displayDate: datetime.toFormat('LLL d, yyyy'),
              amount: dayStats.revenue,
              orderCount: dayStats.orders
            };
          }

          // Track daily order value range
          if (totalAmount > dayStats.orderValueRange.largest) {
            dayStats.orderValueRange.largest = totalAmount;
            dayStats.orderValueRange.largestOrderId = orderId;
          }
          if (totalAmount > 0) {
            if (dayStats.orderValueRange.smallest === 0 || totalAmount < dayStats.orderValueRange.smallest) {
              dayStats.orderValueRange.smallest = totalAmount;
              dayStats.orderValueRange.smallestOrderId = orderId;
            }
          }

          // Track order types
          if (props.HasPreorder) {
            dayStats.orderTypes.preOrders.count++;
            dayStats.orderTypes.preOrders.value += totalAmount;
            dayStats.orderTypes.preOrders.percentage = (dayStats.orderTypes.preOrders.count / dayStats.orders) * 100;
            dayStats.orderTypes.preOrders.averageValue = dayStats.orderTypes.preOrders.value / dayStats.orderTypes.preOrders.count;
          }
          if (props.LocalPickup) {
            dayStats.orderTypes.localPickup.count++;
            dayStats.orderTypes.localPickup.value += totalAmount;
            dayStats.orderTypes.localPickup.percentage = (dayStats.orderTypes.localPickup.count / dayStats.orders) * 100;
            dayStats.orderTypes.localPickup.averageValue = dayStats.orderTypes.localPickup.value / dayStats.orderTypes.localPickup.count;
          }
          if (props.IsOnHold) {
            dayStats.orderTypes.heldItems.count++;
            dayStats.orderTypes.heldItems.value += totalAmount;
            dayStats.orderTypes.heldItems.percentage = (dayStats.orderTypes.heldItems.count / dayStats.orders) * 100;
            dayStats.orderTypes.heldItems.averageValue = dayStats.orderTypes.heldItems.value / dayStats.orderTypes.heldItems.count;
          }
          if (props.HasDigiItem) {
            dayStats.orderTypes.digital.count++;
            dayStats.orderTypes.digital.value += totalAmount;
            dayStats.orderTypes.digital.percentage = (dayStats.orderTypes.digital.count / dayStats.orders) * 100;
            dayStats.orderTypes.digital.averageValue = dayStats.orderTypes.digital.value / dayStats.orderTypes.digital.count;
          }
          if (props.HasDigitalGC) {
            dayStats.orderTypes.giftCard.count++;
            dayStats.orderTypes.giftCard.value += totalAmount;
            dayStats.orderTypes.giftCard.percentage = (dayStats.orderTypes.giftCard.count / dayStats.orders) * 100;
            dayStats.orderTypes.giftCard.averageValue = dayStats.orderTypes.giftCard.value / dayStats.orderTypes.giftCard.count;
          }

          // Update hourly stats for peak hour calculation
          if (dayStats.hourlyOrders[hourOfDay] > maxHourCount) {
            maxHourCount = dayStats.hourlyOrders[hourOfDay];
            peakHour = hourOfDay;
          }

          dailyStats.set(dateKey, dayStats);
        }

        // Track order value range
        if (totalAmount > stats.orderValueRange.largest) {
          stats.orderValueRange.largest = totalAmount;
          stats.orderValueRange.largestOrderId = orderId;
        }
        if (totalAmount > 0) {
          if (stats.orderValueRange.smallest === 0 || totalAmount < stats.orderValueRange.smallest) {
            stats.orderValueRange.smallest = totalAmount;
            stats.orderValueRange.smallestOrderId = orderId;
          }

          // Track order value distribution
          if (totalAmount < 25) {
            stats.orderValueRange.distribution.under25.count++;
            stats.orderValueRange.distribution.under25.total += totalAmount;
          } else if (totalAmount < 50) {
            stats.orderValueRange.distribution.under50.count++;
            stats.orderValueRange.distribution.under50.total += totalAmount;
          } else if (totalAmount < 100) {
            stats.orderValueRange.distribution.under100.count++;
            stats.orderValueRange.distribution.under100.total += totalAmount;
          } else if (totalAmount < 200) {
            stats.orderValueRange.distribution.under200.count++;
            stats.orderValueRange.distribution.under200.total += totalAmount;
          } else {
            stats.orderValueRange.distribution.over200.count++;
            stats.orderValueRange.distribution.over200.total += totalAmount;
          }
        }

        // Track order types with values
        if (props.HasPreorder) {
          stats.orderTypes.preOrders.count++;
          stats.orderTypes.preOrders.value += totalAmount;
          stats.orderTypes.preOrders.items.push({ orderId, amount: totalAmount, items });
          stats.orderTypes.preOrders.percentage = (stats.orderTypes.preOrders.count / stats.orderCount) * 100;
          stats.orderTypes.preOrders.averageValue = stats.orderTypes.preOrders.value / stats.orderTypes.preOrders.count;
        }
        if (props.LocalPickup) {
          stats.orderTypes.localPickup.count++;
          stats.orderTypes.localPickup.value += totalAmount;
          stats.orderTypes.localPickup.items.push({ orderId, amount: totalAmount, items });
          stats.orderTypes.localPickup.percentage = (stats.orderTypes.localPickup.count / stats.orderCount) * 100;
          stats.orderTypes.localPickup.averageValue = stats.orderTypes.localPickup.value / stats.orderTypes.localPickup.count;
        }
        if (props.IsOnHold) {
          stats.orderTypes.heldItems.count++;
          stats.orderTypes.heldItems.value += totalAmount;
          stats.orderTypes.heldItems.items.push({ orderId, amount: totalAmount, items });
          stats.orderTypes.heldItems.percentage = (stats.orderTypes.heldItems.count / stats.orderCount) * 100;
          stats.orderTypes.heldItems.averageValue = stats.orderTypes.heldItems.value / stats.orderTypes.heldItems.count;
        }
        if (props.HasDigiItem) {
          stats.orderTypes.digital.count++;
          stats.orderTypes.digital.value += totalAmount;
          stats.orderTypes.digital.items.push({ orderId, amount: totalAmount, items });
          stats.orderTypes.digital.percentage = (stats.orderTypes.digital.count / stats.orderCount) * 100;
          stats.orderTypes.digital.averageValue = stats.orderTypes.digital.value / stats.orderTypes.digital.count;
        }
        if (props.HasDigitalGC) {
          stats.orderTypes.giftCard.count++;
          stats.orderTypes.giftCard.value += totalAmount;
          stats.orderTypes.giftCard.items.push({ orderId, amount: totalAmount, items });
          stats.orderTypes.giftCard.percentage = (stats.orderTypes.giftCard.count / stats.orderCount) * 100;
          stats.orderTypes.giftCard.averageValue = stats.orderTypes.giftCard.value / stats.orderTypes.giftCard.count;
        }

        // Track hourly and daily stats
        if (datetime) {
          const hour = datetime.hour;
          stats.hourlyOrders[hour]++;
        }

        // Process products and related data
        for (const item of items) {
          const productId = item.ProductID;
          const quantity = Number(item.Quantity || item.QuantityOrdered || 1);
          const revenue = Number(item.RowTotal || (item.ItemPrice * quantity) || 0);
          
          // Track item status
          switch(item.ItemStatus?.toLowerCase()) {
            case 'backordered':
              stats.products.status.backordered++;
              break;
            case 'out of stock':
              stats.products.status.outOfStock++;
              break;
            case 'preorder':
              stats.products.status.preorder++;
              break;
            default:
              stats.products.status.inStock++;
          }

          // Update product stats
          const existingProduct = stats.products.list.find(p => p.id === productId);
          if (existingProduct) {
            existingProduct.totalQuantity += quantity;
            existingProduct.totalRevenue += revenue;
            existingProduct.orderCount++;
            existingProduct.orders.add(orderId);
          } else {
            stats.products.list.push({
              id: productId,
              sku: item.SKU,
              name: item.ProductName,
              brand: item.Brand,
              price: Number(item.ItemPrice || 0),
              ImgThumb: item.ImgThumb,
              totalQuantity: quantity,
              totalRevenue: revenue,
              orderCount: 1,
              orders: new Set([orderId]),
              categories: item.Categories || [],
              status: item.ItemStatus || 'In Stock'
            });
          }

          // Update brand stats
          if (item.Brand) {
            const brand = brandMap.get(item.Brand) || {
              name: item.Brand,
              quantity: 0,
              revenue: 0,
              products: new Set()
            };
            brand.quantity += quantity;
            brand.revenue += revenue;
            brand.products.add(productId);
            brandMap.set(item.Brand, brand);
          }

          // Update category stats
          if (item.Categories) {
            for (const category of item.Categories) {
              const categoryStats = categoryMap.get(category) || {
                name: category,
                quantity: 0,
                revenue: 0,
                products: new Set()
              };
              categoryStats.quantity += quantity;
              categoryStats.revenue += revenue;
              categoryStats.products.add(productId);
              categoryMap.set(category, categoryStats);
            }
          }
        }
      }

      // After processing all orders
      if (stats.orderCount > 0) {
        stats.averageOrderValue = stats.revenue / stats.orderCount;
        stats.averageItemsPerOrder = stats.itemCount / stats.orderCount;
      }

      // Calculate projected revenue for incomplete periods
      if (periodStart && periodEnd) {
        const totalDuration = periodEnd.diff(periodStart);
        const elapsedDuration = this.timeManager.getNow().diff(periodStart);
        const periodProgress = Math.min(100, Math.max(0, (elapsedDuration.milliseconds / totalDuration.milliseconds) * 100));
        
        if (periodProgress > 0 && periodProgress < 100) {
          stats.projectedRevenue = (stats.revenue / (periodProgress / 100));
        } else {
          stats.projectedRevenue = stats.revenue;
        }
        stats.periodProgress = periodProgress;
      }

      // Calculate trend data only for revenue
      if (prevPeriodRevenue > 0) {
        stats.trend = {
          revenue: ((stats.revenue - prevPeriodRevenue) / prevPeriodRevenue) * 100
        };
      }

      // Process shipped orders with better formatting
      const shippingMethodMap = new Map();
      const locationMap = new Map();
      const stateMap = new Map();
      const countryMap = new Map();
      
      for (const shipped of transformedShipped) {
        const props = shipped.event_properties || {};
        stats.shipping.shippedCount++;
        
        // Track shipping methods
        const method = props.ShipMethod || props.ShippingMethod || 'Unknown';
        const currentMethodCount = shippingMethodMap.get(method) || 0;
        shippingMethodMap.set(method, currentMethodCount + 1);

        // Track locations by state and country
        const state = props.ShippingState?.trim() || 'Unknown State';
        const country = props.ShippingCountry?.trim() || 'Unknown Country';

        // Track unique locations
        const locationKey = `${state}-${country}`;
        locationMap.set(locationKey, true);

        // Track by state
        const stateStats = stateMap.get(state) || { count: 0, country };
        stateStats.count++;
        stateMap.set(state, stateStats);

        // Track by country
        const countryStats = countryMap.get(country) || { count: 0, states: new Set() };
        countryStats.count++;
        countryStats.states.add(state);
        countryMap.set(country, countryStats);
      }

      // Format shipping methods
      stats.shipping.methods = Object.fromEntries(shippingMethodMap);
      stats.shipping.methodPercentages = {};
      stats.shipping.methodStats = [];
      shippingMethodMap.forEach((count, method) => {
        const percentage = (count / stats.shipping.shippedCount) * 100;
        stats.shipping.methodPercentages[method] = percentage;
        stats.shipping.methodStats.push({
          name: method,
          value: count,
          percentage
        });
      });
      stats.shipping.methodStats.sort((a, b) => b.value - a.value);

      // Format locations by state and country
      stats.shipping.locations = {
        total: locationMap.size,
        byState: Array.from(stateMap.entries())
          .map(([state, data]) => ({
            state,
            country: data.country,
            count: data.count,
            percentage: (data.count / stats.shipping.shippedCount) * 100
          }))
          .sort((a, b) => b.count - a.count),
        byCountry: Array.from(countryMap.entries())
          .map(([country, data]) => ({
            country,
            count: data.count,
            states: Array.from(data.states),
            percentage: (data.count / stats.shipping.shippedCount) * 100
          }))
          .sort((a, b) => b.count - a.count)
      };

      // Process refunds with more detail
      for (const refund of transformedRefunds) {
        const props = refund.event_properties || {};
        const amount = Number(props.PaymentAmount || 0);
        const reason = props.CancelReason || props.OrderMessage || 'No reason provided';
        const datetime = this.timeManager.toDateTime(refund.attributes?.datetime);
        const orderId = props.OrderId || props.FromOrder;
        
        stats.refunds.total += amount;
        stats.refunds.count++;
        stats.refunds.reasons[reason] = (stats.refunds.reasons[reason] || 0) + 1;
        stats.refunds.items.push({
          orderId,
          amount,
          reason,
          datetime: datetime?.toISO()
        });
        stats.refunds.averageAmount = stats.refunds.total / stats.refunds.count;

        // Track daily refunds
        if (datetime) {
          const dateKey = datetime.toFormat('yyyy-MM-dd');
          // Initialize stats for this date if it doesn't exist
          if (!dailyStats.has(dateKey)) {
            dailyStats.set(dateKey, {
              date: datetime.toISO(), // ISO format for chart library
              timestamp: dateKey,
              revenue: 0,
              orders: 0,
              itemCount: 0,
              averageOrderValue: 0,
              averageItemsPerOrder: 0,
              hourlyOrders: Array(24).fill(0),
              refunds: { total: 0, count: 0, reasons: {}, items: [] },
              canceledOrders: { total: 0, count: 0, reasons: {}, items: [] },
              orderTypes: {
                preOrders: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
                localPickup: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
                heldItems: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
                digital: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
                giftCard: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 }
              },
              orderValueRange: {
                largest: 0,
                smallest: 0,
                largestOrderId: null,
                smallestOrderId: null,
                distribution: {
                  under25: { count: 0, total: 0 },
                  under50: { count: 0, total: 0 },
                  under100: { count: 0, total: 0 },
                  under200: { count: 0, total: 0 },
                  over200: { count: 0, total: 0 }
                }
              }
            });
          }
          const dayStats = dailyStats.get(dateKey);
          dayStats.refunds.total += amount;
          dayStats.refunds.count++;
          dayStats.refunds.reasons[reason] = (dayStats.refunds.reasons[reason] || 0) + 1;
          dailyStats.set(dateKey, dayStats);
        }
      }

      // Process canceled orders with more detail
      for (const canceled of transformedCanceled) {
        const props = canceled.event_properties || {};
        const amount = Number(props.TotalAmount || 0);
        const reason = props.CancelReason || props.OrderMessage || 'No reason provided';
        const datetime = this.timeManager.toDateTime(canceled.attributes?.datetime);
        const orderId = props.OrderId || props.FromOrder;
        
        stats.canceledOrders = stats.canceledOrders || { total: 0, count: 0, reasons: {}, items: [], averageAmount: 0 };
        stats.canceledOrders.total += amount;
        stats.canceledOrders.count++;
        stats.canceledOrders.reasons[reason] = (stats.canceledOrders.reasons[reason] || 0) + 1;
        stats.canceledOrders.items.push({
          orderId,
          amount,
          reason,
          datetime: datetime?.toISO()
        });
        stats.canceledOrders.averageAmount = stats.canceledOrders.total / stats.canceledOrders.count;

        // Track daily cancellations
        if (datetime) {
          const dateKey = datetime.toFormat('yyyy-MM-dd');
          // Initialize stats for this date if it doesn't exist
          if (!dailyStats.has(dateKey)) {
            dailyStats.set(dateKey, {
              date: datetime.toISO(), // ISO format for chart library
              timestamp: dateKey,
              revenue: 0,
              orders: 0,
              itemCount: 0,
              averageOrderValue: 0,
              averageItemsPerOrder: 0,
              hourlyOrders: Array(24).fill(0),
              refunds: { total: 0, count: 0, reasons: {}, items: [] },
              canceledOrders: { total: 0, count: 0, reasons: {}, items: [] },
              orderTypes: {
                preOrders: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
                localPickup: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
                heldItems: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
                digital: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
                giftCard: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 }
              },
              orderValueRange: {
                largest: 0,
                smallest: 0,
                largestOrderId: null,
                smallestOrderId: null,
                distribution: {
                  under25: { count: 0, total: 0 },
                  under50: { count: 0, total: 0 },
                  under100: { count: 0, total: 0 },
                  under200: { count: 0, total: 0 },
                  over200: { count: 0, total: 0 }
                }
              }
            });
          }
          const dayStats = dailyStats.get(dateKey);
          dayStats.canceledOrders.total += amount;
          dayStats.canceledOrders.count++;
          dayStats.canceledOrders.reasons[reason] = (dayStats.canceledOrders.reasons[reason] || 0) + 1;
          dailyStats.set(dateKey, dayStats);
        }
      }

      // Set peak hour stats
      if (maxHourCount > 0) {
        stats.peakOrderHour = {
          hour: peakHour,
          count: maxHourCount,
          displayHour: this._formatHour(peakHour),
          hourData: stats.hourlyOrders.map((count, hour) => ({
            hour,
            displayHour: this._formatHour(hour),
            count,
            percentage: (count / stats.orderCount) * 100
          }))
        };
      }

      // Set best day stats
      if (bestDay.date) {
        stats.bestRevenueDay = {
          ...bestDay,
          dialogType: 'revenue', // Add dialog type for frontend
          dialogTitle: 'Revenue Details' // Add dialog title for frontend
        };
      }

      // Process products with status tracking
      stats.products.list = stats.products.list.map(product => ({
        ...product,
        averageOrderValue: product.totalRevenue / product.orderCount,
        orders: Array.from(product.orders)
      }));
      stats.products.list.sort((a, b) => b.totalRevenue - a.totalRevenue);
      stats.products.total = stats.products.list.length;

      // Format brands with more detail and period-specific data
      stats.brands.list = Array.from(brandMap.values())
        .map(brand => {
          const brandStats = {
            name: brand.name,
            count: brand.quantity,
            revenue: brand.revenue,
            productCount: brand.products.size,
            percentage: (brand.revenue / stats.revenue) * 100, // Calculate percentage based on period revenue
            averageOrderValue: brand.revenue / brand.quantity,
            products: Array.from(brand.products)
          };
          // Add display formatting for charts
          brandStats.tooltipLabel = `${brand.name}\nRevenue: $${brand.revenue.toFixed(2)}\nItems: ${brand.quantity}`;
          return brandStats;
        })
        .sort((a, b) => b.revenue - a.revenue);

      stats.brands.topBrands = stats.brands.list.slice(0, 10);
      stats.brands.totalRevenue = stats.revenue; // Use period revenue
      stats.brands.averageOrderValue = stats.revenue / stats.itemCount;
      stats.brands.total = stats.brands.list.length;

      // Format categories with more detail and period-specific data
      stats.categories.list = Array.from(categoryMap.values())
        .map(category => {
          const categoryStats = {
            name: category.name,
            count: category.quantity,
            revenue: category.revenue,
            productCount: category.products.size,
            percentage: (category.revenue / stats.revenue) * 100, // Calculate percentage based on period revenue
            averageOrderValue: category.revenue / category.quantity,
            products: Array.from(category.products)
          };
          // Add display formatting for charts
          categoryStats.tooltipLabel = `${category.name}\nRevenue: $${category.revenue.toFixed(2)}\nItems: ${category.quantity}`;
          return categoryStats;
        })
        .sort((a, b) => b.revenue - a.revenue);

      stats.categories.topCategories = stats.categories.list.slice(0, 10);
      stats.categories.totalRevenue = stats.revenue; // Use period revenue
      stats.categories.averageOrderValue = stats.revenue / stats.itemCount;
      stats.categories.total = stats.categories.list.length;

      // Remove pie chart labels and create a key
      stats.brands.key = stats.brands.list.map(brand => ({
        name: brand.name,
        color: this._getColorForBrand(brand.name)
      }));
      stats.categories.key = stats.categories.list.map(category => ({
        name: category.name,
        color: this._getColorForCategory(category.name)
      }));

      // Set peak hour stats with proper formatting
      if (maxHourCount > 0) {
        stats.peakOrderHour = {
          hour: peakHour,
          count: maxHourCount,
          displayHour: this._formatHour(peakHour),
          hourData: stats.hourlyOrders.map((count, hour) => ({
            hour,
            displayHour: this._formatHour(hour),
            count,
            percentage: (count / stats.orderCount) * 100
          }))
        };
      }

      // Set best day stats with link to revenue dialog
      if (bestDay.date) {
        stats.bestRevenueDay = {
          ...bestDay,
          dialogType: 'revenue', // Add dialog type for frontend
          dialogTitle: 'Revenue Details' // Add dialog title for frontend
        };
      }

      // Add daily stats for order types
      const orderTypeStats = {
        preOrders: { dailyData: [] },
        localPickup: { dailyData: [] },
        heldItems: { dailyData: [] }
      };

      // Process daily stats for order types
      stats.dailyData.forEach(day => {
        // Pre-orders daily data
        orderTypeStats.preOrders.dailyData.push({
          date: day.date,
          count: day.orderTypes.preOrders.count,
          value: day.orderTypes.preOrders.value,
          percentage: day.orderTypes.preOrders.percentage,
          totalOrders: day.orders
        });

        // Local pickup daily data
        orderTypeStats.localPickup.dailyData.push({
          date: day.date,
          count: day.orderTypes.localPickup.count,
          value: day.orderTypes.localPickup.value,
          percentage: day.orderTypes.localPickup.percentage,
          totalOrders: day.orders
        });

        // Held items daily data
        orderTypeStats.heldItems.dailyData.push({
          date: day.date,
          count: day.orderTypes.heldItems.count,
          value: day.orderTypes.heldItems.value,
          percentage: day.orderTypes.heldItems.percentage,
          totalOrders: day.orders
        });
      });

      // Add order type stats to main stats object
      stats.orderTypes.preOrders.dailyData = orderTypeStats.preOrders.dailyData;
      stats.orderTypes.localPickup.dailyData = orderTypeStats.localPickup.dailyData;
      stats.orderTypes.heldItems.dailyData = orderTypeStats.heldItems.dailyData;

      // Convert daily stats to array and sort
      stats.dailyData = Array.from(dailyStats.values())
        .sort((a, b) => a.date.localeCompare(b.date))
        .map(day => ({
          date: day.date,
          timestamp: day.timestamp,
          revenue: Number(day.revenue || 0),
          orders: Number(day.orders || 0),
          itemCount: Number(day.itemCount || 0),
          averageOrderValue: Number(day.averageOrderValue || 0),
          averageItemsPerOrder: Number(day.averageItemsPerOrder || 0),
          hourlyOrders: day.hourlyOrders || Array(24).fill(0),
          refunds: {
            total: Number(day.refunds?.total || 0),
            count: Number(day.refunds?.count || 0),
            reasons: day.refunds?.reasons || {}
          },
          canceledOrders: {
            total: Number(day.canceledOrders?.total || 0),
            count: Number(day.canceledOrders?.count || 0),
            reasons: day.canceledOrders?.reasons || {}
          },
          orderTypes: {
            preOrders: { 
              count: Number(day.orderTypes?.preOrders?.count || 0),
              value: Number(day.orderTypes?.preOrders?.value || 0),
              percentage: Number(day.orderTypes?.preOrders?.percentage || 0),
              averageValue: Number(day.orderTypes?.preOrders?.averageValue || 0)
            },
            localPickup: {
              count: Number(day.orderTypes?.localPickup?.count || 0),
              value: Number(day.orderTypes?.localPickup?.value || 0),
              percentage: Number(day.orderTypes?.localPickup?.percentage || 0),
              averageValue: Number(day.orderTypes?.localPickup?.averageValue || 0)
            },
            heldItems: {
              count: Number(day.orderTypes?.heldItems?.count || 0),
              value: Number(day.orderTypes?.heldItems?.value || 0),
              percentage: Number(day.orderTypes?.heldItems?.percentage || 0),
              averageValue: Number(day.orderTypes?.heldItems?.averageValue || 0)
            },
            digital: {
              count: Number(day.orderTypes?.digital?.count || 0),
              value: Number(day.orderTypes?.digital?.value || 0),
              percentage: Number(day.orderTypes?.digital?.percentage || 0),
              averageValue: Number(day.orderTypes?.digital?.averageValue || 0)
            },
            giftCard: {
              count: Number(day.orderTypes?.giftCard?.count || 0),
              value: Number(day.orderTypes?.giftCard?.value || 0),
              percentage: Number(day.orderTypes?.giftCard?.percentage || 0),
              averageValue: Number(day.orderTypes?.giftCard?.averageValue || 0)
            }
          }
        }));

      // Set totals
      stats.brands.total = stats.brands.list.length;
      stats.categories.total = stats.categories.list.length;

      return stats;
    } catch (error) {
      console.error('[EventsService] Error calculating period stats:', error);
      throw error;
    }
  }

  _getEmptyStats() {
    return {
      orderCount: 0,
      revenue: 0,
      averageOrderValue: 0,
      itemCount: 0,
      prevPeriodRevenue: 0,
      projectedRevenue: 0,
      periodProgress: 0,
      dailyData: [],
      products: {
        list: [],
        total: 0,
        status: {
          backordered: 0,
          inStock: 0,
          outOfStock: 0,
          preorder: 0
        }
      },
      shipping: {
        shippedCount: 0,
        locations: {
          total: 0,
          byState: [],
          byCountry: []
        },
        methods: {},
        methodStats: [],
        methodPercentages: {}
      },
      refunds: {
        total: 0,
        count: 0,
        reasons: {},
        items: [],
        averageAmount: 0
      },
      canceledOrders: {
        total: 0,
        count: 0,
        reasons: {},
        items: [],
        averageAmount: 0
      },
      orderTypes: {
        preOrders: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
        localPickup: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
        heldItems: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
        digital: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 },
        giftCard: { count: 0, percentage: 0, value: 0, items: [], averageValue: 0 }
      },
      brands: {
        total: 0,
        list: [],
        topBrands: [],
        totalRevenue: 0,
        averageOrderValue: 0
      },
      categories: {
        total: 0,
        list: [],
        topCategories: [],
        totalRevenue: 0,
        averageOrderValue: 0
      },
      hourlyOrders: Array(24).fill(0),
      peakOrderHour: null,
      bestRevenueDay: null,
      orderValueRange: {
        largest: 0,
        smallest: 0,
        largestOrderId: null,
        smallestOrderId: null,
        distribution: {
          under25: { count: 0, total: 0 },
          under50: { count: 0, total: 0 },
          under100: { count: 0, total: 0 },
          under200: { count: 0, total: 0 },
          over200: { count: 0, total: 0 }
        }
      }
    };
  }

  async getMultiMetricEvents(params = {}) {
    try {
      const { timeRange, startDate, endDate, metricIds } = params;
      const metrics = metricIds || Object.values(METRIC_IDS);

      // Try to get from cache first
      const cacheKey = this.redisService._getCacheKey('events', params);
      let cachedData = null;
      try {
        cachedData = await this.redisService.get(`${cacheKey}:feed`);
        if (cachedData) {
          return cachedData;
        }
      } catch (cacheError) {
        console.warn('[EventsService] Cache error:', cacheError);
      }

      // Fetch events for all specified metrics
      const eventPromises = metrics.map(metricId => 
        this.getEvents({
          startDate,
          endDate,
          timeRange,
          metricId,
          sort: '-datetime'
        })
      );

      const results = await Promise.all(eventPromises);

      // Combine and sort all events
      const allEvents = results
        .flatMap(result => result.data || [])
        .sort((a, b) => new Date(b.attributes?.datetime) - new Date(a.attributes?.datetime));

      // Transform the events
      const transformedEvents = this._transformEvents(allEvents);

      const result = {
        data: transformedEvents,
        meta: {
          total_count: transformedEvents.length
        }
      };

      // Cache the results
      try {
        const ttl = this.redisService._getTTL(timeRange);
        await this.redisService.set(`${cacheKey}:feed`, result, ttl);
      } catch (cacheError) {
        console.warn('[EventsService] Cache set error:', cacheError);
      }

      return result;
    } catch (error) {
      console.error('[EventsService] Error fetching multi-metric events:', error);
      throw error;
    }
  }

  _transformEvents(events) {
    if (!Array.isArray(events)) {
      console.warn('[EventsService] Events is not an array:', events);
      return [];
    }

    return events.map(event => {
      try {
        // Extract metric ID from the relationships field if it exists
        const metricId = event.relationships?.metric?.data?.id || 
                        event.attributes?.metric?.id || 
                        event.attributes?.metric_id;

        // Extract properties from all possible locations in the Klaviyo event structure
        const eventProps = {
          ...(event.attributes?.event_properties || {}),
          ...(event.attributes?.properties || {}),
          ...(event.attributes?.profile || {}),
          value: event.attributes?.value,
          datetime: event.attributes?.datetime
        };
                          
        // Normalize shipping data
        const shippingData = {
          name: eventProps.ShippingName || eventProps.shipping_name || eventProps.shipping?.name,
          street1: eventProps.ShippingStreet1 || eventProps.shipping_street1 || eventProps.shipping?.street1,
          street2: eventProps.ShippingStreet2 || eventProps.shipping_street2 || eventProps.shipping?.street2,
          city: eventProps.ShippingCity || eventProps.shipping_city || eventProps.shipping?.city,
          state: eventProps.ShippingState || eventProps.shipping_state || eventProps.shipping?.state,
          zip: eventProps.ShippingZip || eventProps.shipping_zip || eventProps.shipping?.zip,
          country: eventProps.ShippingCountry || eventProps.shipping_country || eventProps.shipping?.country,
          method: eventProps.ShipMethod || eventProps.shipping_method || eventProps.shipping?.method,
          tracking: eventProps.TrackingNumber || eventProps.tracking_number
        };

        const transformed = {
          id: event.id,
          type: event.type,
          metric_id: metricId,
          // Preserve the original attributes structure for compatibility
          attributes: {
            ...event.attributes,
            datetime: event.attributes?.datetime,
            value: event.attributes?.value,
            metric: {
              ...event.attributes?.metric,
              id: metricId
            }
          },
          relationships: event.relationships,
          event_properties: {
            ...eventProps,
            // Transform common properties
            EmailAddress: eventProps.EmailAddress || eventProps.email,
            FirstName: eventProps.FirstName || eventProps.first_name,
            LastName: eventProps.LastName || eventProps.last_name,
            OrderId: eventProps.OrderId || eventProps.FromOrder || eventProps.order_id,
            TotalAmount: Number(eventProps.TotalAmount || eventProps.PaymentAmount || eventProps.total_amount || eventProps.value || 0),
            Items: this._transformItems(eventProps.Items || eventProps.items || eventProps.line_items || []),
            // Add normalized shipping information
            ShippingName: shippingData.name,
            ShippingStreet1: shippingData.street1,
            ShippingStreet2: shippingData.street2,
            ShippingCity: shippingData.city,
            ShippingState: shippingData.state,
            ShippingZip: shippingData.zip,
            ShippingCountry: shippingData.country,
            ShippingMethod: shippingData.method,
            TrackingNumber: shippingData.tracking,
            ShipMethod: shippingData.method,
            // Add payment information
            PaymentMethod: eventProps.PaymentMethod || eventProps.payment_method || eventProps.payment?.method,
            PaymentName: eventProps.PaymentName || eventProps.payment_name || eventProps.payment?.name,
            PaymentAmount: Number(eventProps.PaymentAmount || eventProps.payment_amount || eventProps.payment?.amount || 0),
            // Add order flags
            OrderType: eventProps.OrderType || eventProps.order_type || 'standard',
            HasPreorder: Boolean(eventProps.HasPreorder || eventProps.has_preorder || eventProps.preorder),
            LocalPickup: Boolean(eventProps.LocalPickup || eventProps.local_pickup || eventProps.pickup),
            IsOnHold: Boolean(eventProps.IsOnHold || eventProps.is_on_hold || eventProps.on_hold),
            HasDigiItem: Boolean(eventProps.HasDigiItem || eventProps.has_digital_item || eventProps.digital_item),
            HasNotions: Boolean(eventProps.HasNotions || eventProps.has_notions || eventProps.notions),
            HasDigitalGC: Boolean(eventProps.HasDigitalGC || eventProps.has_digital_gc || eventProps.gift_card),
            StillOwes: Boolean(eventProps.StillOwes || eventProps.still_owes || eventProps.balance_due),
            // Add refund/cancel information
            CancelReason: eventProps.CancelReason || eventProps.cancel_reason || eventProps.reason,
            CancelMessage: eventProps.CancelMessage || eventProps.cancel_message || eventProps.message,
            OrderMessage: eventProps.OrderMessage || eventProps.order_message || eventProps.note
          }
        };

        return transformed;
      } catch (error) {
        console.error('[EventsService] Error transforming event:', error, event);
        // Return a minimal valid event structure
        return {
          id: event.id || 'unknown',
          type: event.type || 'unknown',
          metric_id: event.relationships?.metric?.data?.id || 'unknown',
          attributes: event.attributes || {},
          event_properties: {}
        };
      }
    }).filter(Boolean); // Remove any null/undefined events
  }

  _transformItems(items) {
    if (!Array.isArray(items)) {
      console.warn('[EventsService] Items is not an array:', items);
      return [];
    }

    return items.map(item => {
      const transformed = {
        ...item,
        ProductID: item.ProductID || item.product_id,
        ProductName: item.ProductName || item.product_name,
        SKU: item.SKU || item.sku,
        Brand: item.Brand || item.brand,
        Categories: item.Categories || item.categories || [],
        ItemPrice: Number(item.ItemPrice || item.item_price || 0),
        Quantity: Number(item.Quantity || item.QuantityOrdered || item.quantity || 1),
        QuantityOrdered: Number(item.QuantityOrdered || item.Quantity || item.quantity_ordered || 1),
        QuantitySent: Number(item.QuantitySent || item.quantity_sent || 0),
        QuantityBackordered: Number(item.QuantityBackordered || item.quantity_backordered || 0),
        RowTotal: Number(item.RowTotal || item.row_total || (item.ItemPrice * (item.Quantity || item.QuantityOrdered || 1))),
        ItemStatus: item.ItemStatus || item.item_status || 'In Stock',
        ImgThumb: item.ImgThumb || item.img_thumb
      };
      return transformed;
    });
  }

  _formatHour(hour) {
    if (hour === 0) return "12:00 AM";
    if (hour === 12) return "12:00 PM";
    if (hour > 12) return `${hour - 12}:00 PM`;
    return `${hour}:00 AM`;
  }

  async calculateDetailedStats(params = {}) {
    try {
      const { metric, daily = false } = params;
      console.log('[EventsService] Request params:', params);

      // Get period dates
      let periodStart, periodEnd, prevPeriodStart, prevPeriodEnd;
      if (params.startDate && params.endDate) {
        periodStart = this.timeManager.toDateTime(params.startDate);
        periodEnd = this.timeManager.toDateTime(params.endDate);
        const duration = periodEnd.diff(periodStart);
        prevPeriodEnd = periodStart.minus({ milliseconds: 1 });
        prevPeriodStart = prevPeriodEnd.minus(duration);
      } else if (params.timeRange) {
        // Handle both current and previous period time ranges
        const timeRange = params.timeRange;
        const isPreviousPeriod = timeRange.startsWith('previous');
        const normalizedTimeRange = isPreviousPeriod ? timeRange.replace('previous', 'last') : timeRange;
        
        console.log('[EventsService] Time range details:', {
          originalTimeRange: timeRange,
          isPreviousPeriod,
          normalizedTimeRange
        });

        // Get current period range
        const range = this.timeManager.getDateRange(normalizedTimeRange);
        if (!range) {
          throw new Error(`Invalid time range specified: ${timeRange}`);
        }

        // Get previous period range using TimeManager
        const prevRange = this.timeManager.getPreviousPeriod(normalizedTimeRange);
        if (!prevRange) {
          throw new Error(`Could not calculate previous period for: ${timeRange}`);
        }

        periodStart = range.start;
        periodEnd = range.end;
        prevPeriodStart = prevRange.start;
        prevPeriodEnd = prevRange.end;

        console.log('[EventsService] Calculated date ranges:', {
          timeRange,
          current: {
            start: periodStart.toISO(),
            end: periodEnd.toISO(),
            duration: periodEnd.diff(periodStart).as('days')
          },
          previous: {
            start: prevPeriodStart.toISO(),
            end: prevPeriodEnd.toISO(),
            duration: prevPeriodEnd.diff(prevPeriodStart).as('days')
          }
        });
      }

      // Load both current and previous period data
      console.log('[EventsService] Fetching events with params:', {
        current: {
          startDate: periodStart.toISO(),
          endDate: periodEnd.toISO(),
          metricId: METRIC_IDS.PLACED_ORDER,
          ...params
        },
        previous: {
          startDate: prevPeriodStart.toISO(),
          endDate: prevPeriodEnd.toISO(),
          metricId: METRIC_IDS.PLACED_ORDER
        }
      });

      const [currentResponse, prevResponse] = await Promise.all([
        this.getEvents({
          ...params,
          startDate: periodStart.toISO(),
          endDate: periodEnd.toISO(),
          metricId: METRIC_IDS.PLACED_ORDER,
          customFilters: params.metric === 'pre_orders' ? ['equals(event_properties.HasPreorder,true)'] :
                        params.metric === 'local_pickup' ? ['equals(event_properties.LocalPickup,true)'] :
                        params.metric === 'on_hold' ? ['equals(event_properties.IsOnHold,true)'] : undefined
        }),
        this.getEvents({
          ..._.omit(params, ['timeRange', 'startDate', 'endDate']),
          startDate: prevPeriodStart.toISO(),
          endDate: prevPeriodEnd.toISO(),
          metricId: METRIC_IDS.PLACED_ORDER,
          timeRange: undefined,
          isPreviousPeriod: true,
          cacheKey: `prev_${prevPeriodStart.toISO()}_${prevPeriodEnd.toISO()}`,
          customFilters: params.metric === 'pre_orders' ? ['equals(event_properties.HasPreorder,true)'] :
                        params.metric === 'local_pickup' ? ['equals(event_properties.LocalPickup,true)'] :
                        params.metric === 'on_hold' ? ['equals(event_properties.IsOnHold,true)'] : undefined
        })
      ]);

      // Add debug logging for request params and filters
      console.log('[EventsService] Request details with filters:', {
        current: {
          params: {
            ...params,
            startDate: periodStart.toISO(),
            endDate: periodEnd.toISO(),
            customFilters: params.metric === 'pre_orders' ? ['equals(event_properties.HasPreorder,true)'] :
                          params.metric === 'local_pickup' ? ['equals(event_properties.LocalPickup,true)'] :
                          params.metric === 'on_hold' ? ['equals(event_properties.IsOnHold,true)'] : undefined
          },
          responseLength: currentResponse?.data?.length
        },
        previous: {
          params: {
            ..._.omit(params, ['timeRange', 'startDate', 'endDate']),
            startDate: prevPeriodStart.toISO(),
            endDate: prevPeriodEnd.toISO(),
            customFilters: params.metric === 'pre_orders' ? ['equals(event_properties.HasPreorder,true)'] :
                          params.metric === 'local_pickup' ? ['equals(event_properties.LocalPickup,true)'] :
                          params.metric === 'on_hold' ? ['equals(event_properties.IsOnHold,true)'] : undefined
          },
          responseLength: prevResponse?.data?.length
        }
      });

      // Transform events
      const currentEvents = this._transformEvents(currentResponse.data || []);
      const prevEvents = this._transformEvents(prevResponse.data || []);

      console.log('[EventsService] Transformed events:', {
        current: {
          count: currentEvents.length,
          revenue: currentEvents.reduce((sum, event) => sum + (Number(event.event_properties?.TotalAmount) || 0), 0)
        },
        previous: {
          count: prevEvents.length,
          revenue: prevEvents.reduce((sum, event) => sum + (Number(event.event_properties?.TotalAmount) || 0), 0)
        }
      });

      // Initialize daily stats map with all dates in range
      const dailyStats = new Map();
      let currentDate = periodStart;
      while (currentDate <= periodEnd) {
        const dateKey = currentDate.toFormat('yyyy-MM-dd');
        dailyStats.set(dateKey, {
          date: currentDate.toISO(),
          timestamp: dateKey,
          revenue: 0,
          orders: 0,
          itemCount: 0,
          averageOrderValue: 0,
          averageItemsPerOrder: 0,
          prevRevenue: 0,
          prevOrders: 0,
          prevItemCount: 0,
          prevAvgOrderValue: 0
        });
        currentDate = currentDate.plus({ days: 1 });
      }

      // Process current period events
      for (const event of currentEvents) {
        const datetime = this.timeManager.toDateTime(event.attributes?.datetime);
        if (!datetime) continue;

        const dateKey = datetime.toFormat('yyyy-MM-dd');
        if (!dailyStats.has(dateKey)) continue;

        const dayStats = dailyStats.get(dateKey);
        const props = event.event_properties || {};
        const totalAmount = Number(props.TotalAmount || 0);
        const items = props.Items || [];

        dayStats.revenue += totalAmount;
        dayStats.orders++;
        dayStats.itemCount += items.length;
        dayStats.averageOrderValue = dayStats.revenue / dayStats.orders;
        dayStats.averageItemsPerOrder = dayStats.itemCount / dayStats.orders;
      }

      // Process previous period events
      const prevDailyStats = new Map();
      let prevDate = prevPeriodStart;
      while (prevDate <= prevPeriodEnd) {
        const dateKey = prevDate.toFormat('yyyy-MM-dd');
        prevDailyStats.set(dateKey, {
          date: prevDate.toISO(),
          timestamp: dateKey,
          revenue: 0,
          orders: 0,
          itemCount: 0
        });
        prevDate = prevDate.plus({ days: 1 });
      }

      // Aggregate previous period data
      for (const event of prevEvents) {
        const datetime = this.timeManager.toDateTime(event.attributes?.datetime);
        if (!datetime) continue;

        const dateKey = datetime.toFormat('yyyy-MM-dd');
        if (!prevDailyStats.has(dateKey)) continue;

        const dayStats = prevDailyStats.get(dateKey);
        const props = event.event_properties || {};
        const totalAmount = Number(props.TotalAmount || 0);
        const items = props.Items || [];

        dayStats.revenue += totalAmount;
        dayStats.orders++;
        dayStats.itemCount += items.length;
        prevDailyStats.set(dateKey, dayStats);
      }

      // Map previous period data to current period days
      const prevPeriodDays = Array.from(prevDailyStats.values()).sort((a, b) => a.date.localeCompare(b.date));
      const currentPeriodDays = Array.from(dailyStats.values()).sort((a, b) => a.date.localeCompare(b.date));

      // Add debug logging for data before mapping
      console.log('[EventsService] Data before mapping:', {
        currentPeriod: currentPeriodDays.slice(0, 3),
        previousPeriod: prevPeriodDays.slice(0, 3),
        currentLength: currentPeriodDays.length,
        prevLength: prevPeriodDays.length
      });

      // Map the data using array indices
      for (let i = 0; i < currentPeriodDays.length && i < prevPeriodDays.length; i++) {
        const currentDayStats = currentPeriodDays[i];
        const prevDayStats = prevPeriodDays[i];
        
        if (prevDayStats && currentDayStats) {
          const dayStats = dailyStats.get(currentDayStats.timestamp);
          if (dayStats) {
            dayStats.prevRevenue = prevDayStats.revenue;
            dayStats.prevOrders = prevDayStats.orders;
            dayStats.prevItemCount = prevDayStats.itemCount;
            dayStats.prevAvgOrderValue = prevDayStats.orders > 0 ? prevDayStats.revenue / prevDayStats.orders : 0;
            dailyStats.set(currentDayStats.timestamp, dayStats);
          }
        }
      }

      // Add debug logging for mapped data
      console.log('[EventsService] Sample of mapped data:', {
        firstDay: dailyStats.get(currentPeriodDays[0]?.timestamp),
        lastDay: dailyStats.get(currentPeriodDays[currentPeriodDays.length - 1]?.timestamp),
        totalDays: dailyStats.size
      });

      // Convert to array and sort by date
      const stats = Array.from(dailyStats.values())
        .sort((a, b) => a.date.localeCompare(b.date))
        .map(day => ({
          ...day,
          revenue: Number(day.revenue || 0),
          orders: Number(day.orders || 0),
          itemCount: Number(day.itemCount || 0),
          averageOrderValue: Number(day.averageOrderValue || 0),
          averageItemsPerOrder: Number(day.averageItemsPerOrder || 0),
          prevRevenue: Number(day.prevRevenue || 0),
          prevOrders: Number(day.prevOrders || 0),
          prevItemCount: Number(day.prevItemCount || 0),
          prevAvgOrderValue: Number(day.prevAvgOrderValue || 0)
        }));

      return stats;
    } catch (error) {
      console.error('[EventsService] Error calculating detailed stats:', error);
      throw error;
    }
  }

  _getColorForBrand(brandName) {
    // Generate a consistent color based on the brand name
    let hash = 0;
    for (let i = 0; i < brandName.length; i++) {
      hash = brandName.charCodeAt(i) + ((hash << 5) - hash);
    }
    
    // Use HSL to ensure colors are visually distinct and pleasing
    const hue = Math.abs(hash % 360);
    return `hsl(${hue}, 70%, 50%)`;
  }

  _getColorForCategory(categoryName) {
    // Generate a consistent color based on the category name
    let hash = 0;
    for (let i = 0; i < categoryName.length; i++) {
      hash = categoryName.charCodeAt(i) + ((hash << 5) - hash);
    }
    
    // Use HSL with different saturation/lightness than brands
    const hue = Math.abs(hash % 360);
    return `hsl(${hue}, 60%, 60%)`;
  }

  async getBatchMetrics(params = {}) {
    try {
      const { timeRange, startDate, endDate, metrics = [] } = params;
      
      // Create a map of all metric requests
      const metricPromises = metrics.map(metric => {
        switch(metric) {
          case 'orders':
            return this.getEvents({
              ...params,
              metricId: METRIC_IDS.PLACED_ORDER
            });
          case 'revenue':
            return this.getEvents({
              ...params,
              metricId: METRIC_IDS.PLACED_ORDER,
              property: 'TotalAmount'
            });
          case 'refunds':
            return this.getEvents({
              ...params,
              metricId: METRIC_IDS.PAYMENT_REFUNDED
            });
          case 'cancellations':
            return this.getEvents({
              ...params,
              metricId: METRIC_IDS.CANCELED_ORDER
            });
          case 'shipping':
            return this.getEvents({
              ...params,
              metricId: METRIC_IDS.SHIPPED_ORDER
            });
          default:
            return Promise.resolve(null);
        }
      });

      // Execute all promises in parallel
      const results = await Promise.all(metricPromises);
      
      // Transform results into a keyed object
      const batchResults = {};
      metrics.forEach((metric, index) => {
        batchResults[metric] = results[index];
      });

      return batchResults;
    } catch (error) {
      console.error('[EventsService] Error in batch metrics:', error);
      throw error;
    }
  }
} 
