import { ChartDataPoint, ChartSeries } from '../components/advanced-chart'

interface ChartTheme {
  colors: string[]
  backgroundColor: string
  textColor: string
  gridColor: string
  tooltipBackground: string
  tooltipBorder: string
}

export const CHART_THEMES: Record<string, ChartTheme> = {
  default: {
    colors: ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', '#06b6d4', '#f97316', '#84cc16', '#ec4899', '#6366f1'],
    backgroundColor: '#ffffff',
    textColor: '#374151',
    gridColor: '#e5e7eb',
    tooltipBackground: '#1f2937',
    tooltipBorder: '#374151',
  },
  dark: {
    colors: ['#60a5fa', '#f87171', '#34d399', '#fbbf24', '#a78bfa', '#22d3ee', '#fb923c', '#a3e635', '#f472b6', '#818cf8'],
    backgroundColor: '#1f2937',
    textColor: '#f9fafb',
    gridColor: '#374151',
    tooltipBackground: '#374151',
    tooltipBorder: '#4b5563',
  },
  minimal: {
    colors: ['#000000', '#666666', '#999999', '#cccccc'],
    backgroundColor: '#ffffff',
    textColor: '#000000',
    gridColor: '#f3f4f6',
    tooltipBackground: '#000000',
    tooltipBorder: '#000000',
  },
}

export function generateChartData(
  count: number,
  series: string[],
  options?: {
    startDate?: Date
    interval?: 'hour' | 'day' | 'week' | 'month'
    trend?: 'up' | 'down' | 'random'
    baseValue?: number
    variance?: number
  }
): ChartDataPoint[] {
  const {
    startDate = new Date(),
    interval = 'day',
    trend = 'random',
    baseValue = 100,
    variance = 20,
  } = options || {}

  const data: ChartDataPoint[] = []
  const current = new Date(startDate)

  for (let i = 0; i < count; i++) {
    const point: ChartDataPoint = {
      name: formatDateForInterval(current, interval),
      timestamp: current.getTime(),
    }

    series.forEach((seriesName, index) => {
      let value = baseValue

      if (trend === 'up') {
        value += (i * 5) + (Math.random() - 0.5) * variance
      } else if (trend === 'down') {
        value -= (i * 5) + (Math.random() - 0.5) * variance
      } else {
        value += (Math.random() - 0.5) * variance * 2
      }

      // Add some series-specific variation
      value += index * 10 + (Math.random() - 0.5) * 10

      point[seriesName] = Math.max(0, Math.round(value))
    })

    data.push(point)

    // Increment date based on interval
    switch (interval) {
      case 'hour':
        current.setHours(current.getHours() + 1)
        break
      case 'day':
        current.setDate(current.getDate() + 1)
        break
      case 'week':
        current.setDate(current.getDate() + 7)
        break
      case 'month':
        current.setMonth(current.getMonth() + 1)
        break
    }
  }

  return data
}

function formatDateForInterval(date: Date, interval: 'hour' | 'day' | 'week' | 'month'): string {
  switch (interval) {
    case 'hour':
      return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
    case 'day':
      return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
    case 'week':
      return `Week ${getWeekNumber(date)}`
    case 'month':
      return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' })
    default:
      return date.toLocaleDateString()
  }
}

function getWeekNumber(date: Date): number {
  const firstDayOfYear = new Date(date.getFullYear(), 0, 1)
  const pastDaysOfYear = (date.getTime() - firstDayOfYear.getTime()) / 86400000
  return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7)
}

export function calculateMovingAverage(data: ChartDataPoint[], key: string, window: number): ChartDataPoint[] {
  if (window <= 0 || window > data.length) return data

  return data.map((point, index) => {
    if (index < window - 1) return point

    const windowData = data.slice(index - window + 1, index + 1)
    const sum = windowData.reduce((acc, item) => acc + (Number(item[key]) || 0), 0)
    const average = sum / window

    return {
      ...point,
      [`${key}_ma${window}`]: Math.round(average * 100) / 100,
    }
  })
}

export function detectOutliers(data: ChartDataPoint[], key: string, threshold: number = 2): ChartDataPoint[] {
  const values = data.map(point => Number(point[key]) || 0)
  const mean = values.reduce((sum, val) => sum + val, 0) / values.length
  const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / values.length
  const stdDev = Math.sqrt(variance)

  return data.map(point => {
    const value = Number(point[key]) || 0
    const zScore = Math.abs((value - mean) / stdDev)
    
    return {
      ...point,
      [`${key}_outlier`]: zScore > threshold,
      [`${key}_zscore`]: Math.round(zScore * 100) / 100,
    }
  })
}

export function interpolateData(data: ChartDataPoint[], key: string, method: 'linear' | 'polynomial' = 'linear'): ChartDataPoint[] {
  const result = [...data]
  
  for (let i = 0; i < result.length; i++) {
    const point = result[i]
    
    if (point[key] == null || point[key] === '') {
      // Find nearest non-null values
      let prevIndex = i - 1
      let nextIndex = i + 1
      
      while (prevIndex >= 0 && (result[prevIndex][key] == null || result[prevIndex][key] === '')) {
        prevIndex--
      }
      
      while (nextIndex < result.length && (result[nextIndex][key] == null || result[nextIndex][key] === '')) {
        nextIndex++
      }
      
      if (prevIndex >= 0 && nextIndex < result.length) {
        const prevValue = Number(result[prevIndex][key])
        const nextValue = Number(result[nextIndex][key])
        
        if (method === 'linear') {
          const ratio = (i - prevIndex) / (nextIndex - prevIndex)
          point[key] = prevValue + (nextValue - prevValue) * ratio
        }
      } else if (prevIndex >= 0) {
        point[key] = result[prevIndex][key]
      } else if (nextIndex < result.length) {
        point[key] = result[nextIndex][key]
      }
    }
  }
  
  return result
}

export function aggregateDataByPeriod(
  data: ChartDataPoint[],
  period: 'hour' | 'day' | 'week' | 'month',
  aggregations: Record<string, 'sum' | 'avg' | 'min' | 'max' | 'count'>
): ChartDataPoint[] {
  const groups: Record<string, ChartDataPoint[]> = {}
  
  data.forEach(point => {
    const timestamp = point.timestamp ? new Date(point.timestamp as number) : new Date()
    const key = formatDateForInterval(timestamp, period)
    
    if (!groups[key]) {
      groups[key] = []
    }
    groups[key].push(point)
  })
  
  return Object.entries(groups).map(([key, groupData]) => {
    const result: ChartDataPoint = { name: key }
    
    Object.entries(aggregations).forEach(([field, operation]) => {
      const values = groupData.map(point => Number(point[field]) || 0)
      
      switch (operation) {
        case 'sum':
          result[field] = values.reduce((sum, val) => sum + val, 0)
          break
        case 'avg':
          result[field] = values.reduce((sum, val) => sum + val, 0) / values.length
          break
        case 'min':
          result[field] = Math.min(...values)
          break
        case 'max':
          result[field] = Math.max(...values)
          break
        case 'count':
          result[field] = values.length
          break
      }
    })
    
    return result
  })
}

export function exportChartAsImage(
  chartElement: HTMLElement,
  filename: string = 'chart',
  format: 'png' | 'jpeg' | 'svg' = 'png'
): void {
  // This would typically use html2canvas or similar library
  // For now, we'll provide a basic implementation
  console.log(`Exporting chart as ${format} with filename: ${filename}`)
  
  // Implementation would go here
  // This is a placeholder for the actual export functionality
}

export function exportChartData(
  data: ChartDataPoint[],
  filename: string = 'chart-data',
  format: 'json' | 'csv' = 'json'
): void {
  let content: string
  let mimeType: string
  
  if (format === 'json') {
    content = JSON.stringify(data, null, 2)
    mimeType = 'application/json'
  } else {
    // CSV export
    const headers = Object.keys(data[0] || {})
    const csvRows = [
      headers.join(','),
      ...data.map(row => 
        headers.map(header => {
          const value = row[header]
          return typeof value === 'string' && value.includes(',') 
            ? `"${value}"` 
            : value
        }).join(',')
      )
    ]
    content = csvRows.join('\n')
    mimeType = 'text/csv'
  }
  
  const blob = new Blob([content], { type: mimeType })
  const url = window.URL.createObjectURL(blob)
  const link = document.createElement('a')
  link.href = url
  link.download = `${filename}.${format}`
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)
  window.URL.revokeObjectURL(url)
}

// Gradient color generator
export function generateGradientColors(baseColor: string, count: number = 5): string[] {
  const colors: string[] = []
  const rgb = hexToRgb(baseColor)
  
  if (!rgb) return [baseColor]
  
  for (let i = 0; i < count; i++) {
    const factor = i / (count - 1)
    const r = Math.round(rgb.r + (255 - rgb.r) * factor * 0.5)
    const g = Math.round(rgb.g + (255 - rgb.g) * factor * 0.5)
    const b = Math.round(rgb.b + (255 - rgb.b) * factor * 0.5)
    colors.push(`rgb(${r}, ${g}, ${b})`)
  }
  
  return colors
}

function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
  return result ? {
    r: parseInt(result[1], 16),
    g: parseInt(result[2], 16),
    b: parseInt(result[3], 16)
  } : null
}

// Performance optimization helpers
export function throttleChartData(data: ChartDataPoint[], maxPoints: number = 1000): ChartDataPoint[] {
  if (data.length <= maxPoints) return data
  
  const step = Math.ceil(data.length / maxPoints)
  return data.filter((_, index) => index % step === 0)
}

// Format large numbers
export function formatChartValue(value: number): string {
  if (value >= 1e9) return `${(value / 1e9).toFixed(1)}B`
  if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`
  if (value >= 1e3) return `${(value / 1e3).toFixed(1)}K`
  return value.toString()
}

// Calculate chart statistics
export function calculateChartStats(data: ChartDataPoint[], keys: string[]): Record<string, { min: number; max: number; avg: number; total: number }> {
  const stats: Record<string, { min: number; max: number; avg: number; total: number }> = {}
  
  keys.forEach(key => {
    const values = data.map(point => Number(point[key]) || 0)
    stats[key] = {
      min: Math.min(...values),
      max: Math.max(...values),
      avg: values.reduce((sum, val) => sum + val, 0) / values.length,
      total: values.reduce((sum, val) => sum + val, 0)
    }
  })
  
  return stats
}
