import { supabase } from './supabase';
import type { Database } from './database.types';

// New types for trending/popularity analytics
export interface EventAnalytic {
  eventId: string;
  metricType: 'page_view' | 'ticket_view' | 'checkout_start' | 'checkout_complete';
  metricValue?: number;
  sessionId?: string;
  userId?: string;
  ipAddress?: string;
  userAgent?: string;
  referrer?: string;
  locationData?: {
    latitude?: number;
    longitude?: number;
    city?: string;
    state?: string;
  };
  metadata?: Record<string, unknown>;
}

export interface TrendingEvent {
  eventId: string;
  title: string;
  venue: string;
  venueId: string;
  category: string;
  startTime: string;
  popularityScore: number;
  viewCount: number;
  ticketsSold: number;
  isFeature: boolean;
  imageUrl?: string;
  slug: string;
  distanceMiles?: number;
}

export interface PopularityMetrics {
  viewScore: number;
  ticketScore: number;
  recencyScore: number;
  engagementScore: number;
  finalScore: number;
}

// Types for analytics data
export interface SalesMetrics {
  totalRevenue: number;
  netRevenue: number;
  platformFees: number;
  ticketsSold: number;
  averageTicketPrice: number;
  conversionRate: number;
  refundRate: number;
}

export interface SalesByTimeframe {
  date: string;
  revenue: number;
  ticketsSold: number;
  averagePrice: number;
}

export interface TicketTypePerformance {
  ticketTypeId: string;
  name: string;
  price: number;
  quantitySold: number;
  quantityAvailable: number;
  revenue: number;
  sellThroughRate: number;
}

export interface RevenueBreakdown {
  grossRevenue: number;
  platformFees: number;
  netRevenue: number;
  stripeFees: number;
  organizerPayout: number;
}

export interface SalesAnalyticsData {
  metrics: SalesMetrics;
  revenueBreakdown: RevenueBreakdown;
  salesByDay: SalesByTimeframe[];
  salesByHour: SalesByTimeframe[];
  ticketTypePerformance: TicketTypePerformance[];
  topSellingTickets: TicketTypePerformance[];
  recentSales: Record<string, unknown>[];
}

// Analytics calculation functions
export class EventAnalytics {
  private eventId: string;

  constructor(eventId: string) {
    this.eventId = eventId;
  }

  // Get comprehensive analytics data for an event
  async getAnalyticsData(): Promise<SalesAnalyticsData> {
    const [
      metrics,
      revenueBreakdown,
      salesByDay,
      salesByHour,
      ticketTypePerformance,
      recentSales
    ] = await Promise.all([
      this.getSalesMetrics(),
      this.getRevenueBreakdown(),
      this.getSalesByTimeframe('day'),
      this.getSalesByTimeframe('hour'),
      this.getTicketTypePerformance(),
      this.getRecentSales()
    ]);

    return {
      metrics,
      revenueBreakdown,
      salesByDay,
      salesByHour,
      ticketTypePerformance,
      topSellingTickets: ticketTypePerformance.sort((a, b) => b.quantitySold - a.quantitySold).slice(0, 5),
      recentSales
    };
  }

  // Calculate key sales metrics
  async getSalesMetrics(): Promise<SalesMetrics> {
    try {
      // Get ticket sales data
      const { data: tickets, error: ticketsError } = await supabase
        .from('tickets')
        .select(`
          id,
          price,
          platform_fee_charged,
          created_at,
          ticket_types!inner(
            event_id
          )
        `)
        .eq('ticket_types.event_id', this.eventId);

      if (ticketsError) throw ticketsError;

      // Get ticket types for total capacity
      const { data: ticketTypes, error: typesError } = await supabase
        .from('ticket_types')
        .select('quantity_available')
        .eq('event_id', this.eventId);

      if (typesError) throw typesError;

      const ticketsSold = tickets?.length || 0;
      const totalCapacity = ticketTypes?.reduce((sum, type) => sum + (type.quantity_available || 0), 0) || 0;
      const totalRevenue = tickets?.reduce((sum, ticket) => sum + (ticket.price || 0), 0) || 0;
      const platformFees = tickets?.reduce((sum, ticket) => sum + (ticket.platform_fee_charged || 0), 0) || 0;
      const netRevenue = totalRevenue - platformFees;
      const averageTicketPrice = ticketsSold > 0 ? totalRevenue / ticketsSold : 0;
      const conversionRate = totalCapacity > 0 ? (ticketsSold / totalCapacity) * 100 : 0;

      return {
        totalRevenue,
        netRevenue,
        platformFees,
        ticketsSold,
        averageTicketPrice,
        conversionRate,
        refundRate: 0 // TODO: Implement refunds tracking
      };
    } catch (error) {

      return {
        totalRevenue: 0,
        netRevenue: 0,
        platformFees: 0,
        ticketsSold: 0,
        averageTicketPrice: 0,
        conversionRate: 0,
        refundRate: 0
      };
    }
  }

  // Get detailed revenue breakdown
  async getRevenueBreakdown(): Promise<RevenueBreakdown> {
    try {
      const { data: tickets, error } = await supabase
        .from('tickets')
        .select(`
          price,
          platform_fee_charged,
          stripe_fee_charged,
          ticket_types!inner(
            event_id
          )
        `)
        .eq('ticket_types.event_id', this.eventId);

      if (error) throw error;

      const grossRevenue = tickets?.reduce((sum, ticket) => sum + (ticket.price || 0), 0) || 0;
      const platformFees = tickets?.reduce((sum, ticket) => sum + (ticket.platform_fee_charged || 0), 0) || 0;
      const stripeFees = tickets?.reduce((sum, ticket) => sum + (ticket.stripe_fee_charged || 0), 0) || 0;
      const netRevenue = grossRevenue - platformFees;
      const organizerPayout = grossRevenue - platformFees - stripeFees;

      return {
        grossRevenue,
        platformFees,
        netRevenue,
        stripeFees,
        organizerPayout
      };
    } catch (error) {

      return {
        grossRevenue: 0,
        platformFees: 0,
        netRevenue: 0,
        stripeFees: 0,
        organizerPayout: 0
      };
    }
  }

  // Get sales data grouped by timeframe (day or hour)
  async getSalesByTimeframe(timeframe: 'day' | 'hour'): Promise<SalesByTimeframe[]> {
    try {
      const dateFormat = timeframe === 'day' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH24:00:00';
      
      const { data, error } = await supabase
        .from('tickets')
        .select(`
          price,
          created_at,
          ticket_types!inner(
            event_id
          )
        `)
        .eq('ticket_types.event_id', this.eventId);

      if (error) throw error;

      // Group sales by timeframe
      const salesMap = new Map<string, { revenue: number; count: number }>();
      
      data?.forEach(ticket => {
        const date = new Date(ticket.created_at);
        let key: string;
        
        if (timeframe === 'day') {
          key = date.toISOString().split('T')[0];
        } else {
          key = `${date.toISOString().split('T')[0]} ${date.getHours().toString().padStart(2, '0')}:00:00`;
        }
        
        const existing = salesMap.get(key) || { revenue: 0, count: 0 };
        salesMap.set(key, {
          revenue: existing.revenue + (ticket.price || 0),
          count: existing.count + 1
        });
      });

      // Convert to array and sort by date
      return Array.from(salesMap.entries())
        .map(([date, data]) => ({
          date,
          revenue: data.revenue,
          ticketsSold: data.count,
          averagePrice: data.count > 0 ? data.revenue / data.count : 0
        }))
        .sort((a, b) => a.date.localeCompare(b.date));
    } catch (error) {

      return [];
    }
  }

  // Get performance metrics for each ticket type
  async getTicketTypePerformance(): Promise<TicketTypePerformance[]> {
    try {
      // Get ticket types with sales data
      const { data: ticketTypes, error: typesError } = await supabase
        .from('ticket_types')
        .select(`
          id,
          name,
          price,
          quantity_available,
          tickets(id, price)
        `)
        .eq('event_id', this.eventId);

      if (typesError) throw typesError;

      return ticketTypes?.map(type => {
        const quantitySold = type.tickets?.length || 0;
        const revenue = type.tickets?.reduce((sum: number, ticket: any) => sum + (ticket.price || 0), 0) || 0;
        const sellThroughRate = type.quantity_available > 0 ? (quantitySold / type.quantity_available) * 100 : 0;

        return {
          ticketTypeId: type.id,
          name: type.name,
          price: type.price || 0,
          quantitySold,
          quantityAvailable: type.quantity_available || 0,
          revenue,
          sellThroughRate
        };
      }) || [];
    } catch (error) {

      return [];
    }
  }

  // Get recent sales transactions
  async getRecentSales(limit: number = 20): Promise<any[]> {
    try {
      const { data: tickets, error } = await supabase
        .from('tickets')
        .select(`
          id,
          price,
          purchaser_name,
          purchaser_email,
          created_at,
          ticket_types!inner(
            event_id,
            name
          )
        `)
        .eq('ticket_types.event_id', this.eventId)
        .order('created_at', { ascending: false })
        .limit(limit);

      if (error) throw error;

      return tickets || [];
    } catch (error) {

      return [];
    }
  }

  // Get sales velocity (sales per hour/day trends)
  async getSalesVelocity(): Promise<{ current: number; trend: 'up' | 'down' | 'stable' }> {
    try {
      const now = new Date();
      const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
      const twoDaysAgo = new Date(now.getTime() - 48 * 60 * 60 * 1000);

      const { data: recentSales, error: recentError } = await supabase
        .from('tickets')
        .select(`
          id,
          created_at,
          ticket_types!inner(event_id)
        `)
        .eq('ticket_types.event_id', this.eventId)
        .gte('created_at', oneDayAgo.toISOString());

      const { data: previousSales, error: previousError } = await supabase
        .from('tickets')
        .select(`
          id,
          created_at,
          ticket_types!inner(event_id)
        `)
        .eq('ticket_types.event_id', this.eventId)
        .gte('created_at', twoDaysAgo.toISOString())
        .lt('created_at', oneDayAgo.toISOString());

      if (recentError || previousError) throw recentError || previousError;

      const currentVelocity = recentSales?.length || 0;
      const previousVelocity = previousSales?.length || 0;

      let trend: 'up' | 'down' | 'stable' = 'stable';
      if (currentVelocity > previousVelocity * 1.1) trend = 'up';
      else if (currentVelocity < previousVelocity * 0.9) trend = 'down';

      return { current: currentVelocity, trend };
    } catch (error) {

      return { current: 0, trend: 'stable' };
    }
  }

  // Format currency values
  static formatCurrency(amount: number): string {
    return new Intl.NumberFormat('en-US', {
      style: 'currency',
      currency: 'USD'
    }).format(amount);
  }

  // Format percentage values
  static formatPercentage(value: number): string {
    return `${value.toFixed(1)}%`;
  }

  // Format large numbers
  static formatNumber(value: number): string {
    if (value >= 1000000) {
      return `${(value / 1000000).toFixed(1)}M`;
    } else if (value >= 1000) {
      return `${(value / 1000).toFixed(1)}K`;
    }
    return value.toString();
  }
}

// Export data to CSV
export function exportAnalyticsToCSV(data: SalesAnalyticsData, eventTitle: string): void {
  const csvContent = [
    // Summary metrics
    ['Sales Analytics Report', eventTitle],
    ['Generated', new Date().toISOString()],
    [''],
    ['SUMMARY METRICS'],
    ['Total Revenue', EventAnalytics.formatCurrency(data.metrics.totalRevenue)],
    ['Net Revenue', EventAnalytics.formatCurrency(data.metrics.netRevenue)],
    ['Platform Fees', EventAnalytics.formatCurrency(data.metrics.platformFees)],
    ['Tickets Sold', data.metrics.ticketsSold.toString()],
    ['Average Ticket Price', EventAnalytics.formatCurrency(data.metrics.averageTicketPrice)],
    ['Conversion Rate', EventAnalytics.formatPercentage(data.metrics.conversionRate)],
    [''],
    ['TICKET TYPE PERFORMANCE'],
    ['Ticket Type', 'Price', 'Sold', 'Available', 'Revenue', 'Sell-through Rate'],
    ...data.ticketTypePerformance.map(type => [
      type.name,
      EventAnalytics.formatCurrency(type.price),
      type.quantitySold.toString(),
      type.quantityAvailable.toString(),
      EventAnalytics.formatCurrency(type.revenue),
      EventAnalytics.formatPercentage(type.sellThroughRate)
    ]),
    [''],
    ['DAILY SALES'],
    ['Date', 'Revenue', 'Tickets Sold', 'Average Price'],
    ...data.salesByDay.map(day => [
      day.date,
      EventAnalytics.formatCurrency(day.revenue),
      day.ticketsSold.toString(),
      EventAnalytics.formatCurrency(day.averagePrice)
    ])
  ];

  const csv = csvContent.map(row => row.join(',')).join('\n');
  const blob = new Blob([csv], { type: 'text/csv' });
  const url = window.URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;
  link.download = `${eventTitle.replace(/[^a-z0-9]/gi, '_').toLowerCase()}_analytics_${new Date().toISOString().split('T')[0]}.csv`;
  link.click();
  window.URL.revokeObjectURL(url);
}

// New trending/popularity analytics service
export class TrendingAnalyticsService {
  private static instance: TrendingAnalyticsService;
  private batchedEvents: EventAnalytic[] = [];
  private batchTimer: NodeJS.Timeout | null = null;
  private readonly BATCH_SIZE = 10;
  private readonly BATCH_TIMEOUT = 5000; // 5 seconds

  static getInstance(): TrendingAnalyticsService {
    if (!TrendingAnalyticsService.instance) {
      TrendingAnalyticsService.instance = new TrendingAnalyticsService();
    }
    return TrendingAnalyticsService.instance;
  }

  async trackEvent(analytic: EventAnalytic): Promise<void> {
    this.batchedEvents.push(analytic);

    if (this.batchedEvents.length >= this.BATCH_SIZE) {
      await this.flushBatch();
    } else if (!this.batchTimer) {
      this.batchTimer = setTimeout(() => {
        this.flushBatch();
      }, this.BATCH_TIMEOUT);
    }
  }

  private async flushBatch(): Promise<void> {
    if (this.batchedEvents.length === 0) return;

    const events = [...this.batchedEvents];
    this.batchedEvents = [];

    if (this.batchTimer) {
      clearTimeout(this.batchTimer);
      this.batchTimer = null;
    }

    try {
      const { error } = await supabase
        .from('event_analytics')
        .insert(events.map(event => ({
          event_id: event.eventId,
          metric_type: event.metricType,
          metric_value: event.metricValue || 1,
          session_id: event.sessionId,
          user_id: event.userId,
          ip_address: event.ipAddress,
          user_agent: event.userAgent,
          referrer: event.referrer,
          location_data: event.locationData,
          metadata: event.metadata || {}
        })));

      if (error) {

      }
    } catch (error) {

    }
  }

  async updateEventPopularityScore(eventId: string): Promise<number> {
    try {
      const { data, error } = await supabase
        .rpc('calculate_event_popularity_score', { event_id_param: eventId });

      if (error) {

        return 0;
      }

      return data || 0;
    } catch (error) {

      return 0;
    }
  }

  async getTrendingEvents(
    latitude?: number,
    longitude?: number,
    radiusMiles: number = 50,
    limit: number = 20
  ): Promise<TrendingEvent[]> {
    try {
      const query = supabase
        .from('events')
        .select(`
          id,
          title,
          venue,
          venue_id,
          category,
          start_time,
          popularity_score,
          view_count,
          is_featured,
          image_url,
          slug,
          venues!inner (
            id,
            name,
            latitude,
            longitude
          )
        `)
        .eq('is_published', true)
        .eq('is_public', true)
        .gt('start_time', new Date().toISOString())
        .order('popularity_score', { ascending: false })
        .limit(limit);

      const { data: events, error } = await query;

      if (error) {

        return [];
      }

      if (!events) return [];

      // Get ticket sales for each event
      const eventIds = events.map(event => event.id);
      const { data: ticketData } = await supabase
        .from('tickets')
        .select('event_id')
        .in('event_id', eventIds);

      const ticketCounts = ticketData?.reduce((acc, ticket) => {
        acc[ticket.event_id] = (acc[ticket.event_id] || 0) + 1;
        return acc;
      }, {} as Record<string, number>) || {};

      // Calculate distances if user location is provided
      const trendingEvents: TrendingEvent[] = events.map(event => {
        const venue = event.venues as any;
        let distanceMiles: number | undefined;

        if (latitude && longitude && venue?.latitude && venue?.longitude) {
          distanceMiles = this.calculateDistance(
            latitude,
            longitude,
            venue.latitude,
            venue.longitude
          );
        }

        return {
          eventId: event.id,
          title: event.title,
          venue: event.venue,
          venueId: event.venue_id,
          category: event.category || 'General',
          startTime: event.start_time,
          popularityScore: event.popularity_score || 0,
          viewCount: event.view_count || 0,
          ticketsSold: ticketCounts[event.id] || 0,
          isFeature: event.is_featured || false,
          imageUrl: event.image_url,
          slug: event.slug,
          distanceMiles
        };
      });

      // Filter by location if provided
      if (latitude && longitude) {
        return trendingEvents.filter(event => 
          event.distanceMiles === undefined || event.distanceMiles <= radiusMiles
        );
      }

      return trendingEvents;
    } catch (error) {

      return [];
    }
  }

  async getHotEventsInArea(
    latitude: number,
    longitude: number,
    radiusMiles: number = 25,
    limit: number = 10
  ): Promise<TrendingEvent[]> {
    try {
      const { data, error } = await supabase
        .rpc('get_events_within_radius', {
          user_lat: latitude,
          user_lng: longitude,
          radius_miles: radiusMiles,
          limit_count: limit
        });

      if (error) {

        return [];
      }

      if (!data) return [];

      // Get complete event data for each result
      const eventIds = data.map(event => event.event_id);
      const { data: eventDetails } = await supabase
        .from('events')
        .select('id, image_url, slug')
        .in('id', eventIds);

      const eventDetailsMap = eventDetails?.reduce((acc, event) => {
        acc[event.id] = event;
        return acc;
      }, {} as Record<string, any>) || {};

      // Get ticket sales for each event
      const { data: ticketData } = await supabase
        .from('tickets')
        .select('event_id')
        .in('event_id', eventIds);

      const ticketCounts = ticketData?.reduce((acc, ticket) => {
        acc[ticket.event_id] = (acc[ticket.event_id] || 0) + 1;
        return acc;
      }, {} as Record<string, number>) || {};

      return data.map(event => {
        const details = eventDetailsMap[event.event_id];
        return {
          eventId: event.event_id,
          title: event.title,
          venue: event.venue,
          venueId: event.venue_id,
          category: event.category || 'General',
          startTime: event.start_time,
          popularityScore: event.popularity_score || 0,
          viewCount: 0,
          ticketsSold: ticketCounts[event.event_id] || 0,
          isFeature: event.is_featured || false,
          imageUrl: details?.image_url,
          slug: details?.slug || '',
          distanceMiles: event.distance_miles
        };
      });
    } catch (error) {

      return [];
    }
  }

  private calculateDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
    const R = 3959; // Earth's radius in miles
    const dLat = this.toRad(lat2 - lat1);
    const dLon = this.toRad(lon2 - lon1);
    const a = 
      Math.sin(dLat/2) * Math.sin(dLat/2) +
      Math.cos(this.toRad(lat1)) * Math.cos(this.toRad(lat2)) * 
      Math.sin(dLon/2) * Math.sin(dLon/2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    return R * c;
  }

  private toRad(value: number): number {
    return value * Math.PI / 180;
  }

  async batchUpdatePopularityScores(): Promise<void> {
    try {
      const { data: events, error } = await supabase
        .from('events')
        .select('id')
        .eq('is_published', true)
        .gt('start_time', new Date().toISOString());

      if (error || !events) {

        return;
      }

      // Process in batches to avoid overwhelming the database
      const batchSize = 10;
      for (let i = 0; i < events.length; i += batchSize) {
        const batch = events.slice(i, i + batchSize);
        await Promise.all(
          batch.map(event => this.updateEventPopularityScore(event.id))
        );
        
        // Add a small delay between batches
        await new Promise(resolve => setTimeout(resolve, 100));
      }
    } catch (error) {

    }
  }
}

export const trendingAnalyticsService = TrendingAnalyticsService.getInstance();