/**
 * Predictive Analytics Service
 * Provides predictive insights for resource usage, costs, and capacity planning
 */

import { Context } from '../types/context'
import { getDb } from '../db'

export interface Prediction {
  id: string
  resourceId: string
  metricType: string
  predictionType: 'USAGE' | 'COST' | 'CAPACITY' | 'FAILURE'
  currentValue: number
  predictedValue: number
  confidence: number // 0-100
  timeframe: '1H' | '6H' | '24H' | '7D' | '30D'
  timestamp: Date
  trend: 'INCREASING' | 'DECREASING' | 'STABLE'
  recommendation?: string
}

export interface PredictiveConfig {
  resourceId: string
  metricType: string
  timeframe: Prediction['timeframe']
  predictionType: Prediction['predictionType']
}

/**
 * Predict future resource usage using time series forecasting
 */
export async function predictUsage(
  context: Context,
  config: PredictiveConfig
): Promise<Prediction> {
  const db = getDb()

  // Get historical data based on timeframe
  const timeframes = {
    '1H': 24 * 60, // 24 hours of data for 1 hour prediction
    '6H': 7 * 24 * 60, // 7 days for 6 hour prediction
    '24H': 30 * 24 * 60, // 30 days for 24 hour prediction
    '7D': 90 * 24 * 60, // 90 days for 7 day prediction
    '30D': 365 * 24 * 60, // 365 days for 30 day prediction
  }

  const minutesBack = timeframes[config.timeframe]
  const endTime = new Date()
  const startTime = new Date(endTime.getTime() - minutesBack * 60 * 1000)

  // Query historical metrics
  const metricsQuery = `
    SELECT timestamp, value
    FROM metrics
    WHERE resource_id = $1
      AND metric_type = $2
      AND timestamp >= $3
      AND timestamp <= $4
    ORDER BY timestamp ASC
  `

  const result = await db.query(metricsQuery, [
    config.resourceId,
    config.metricType,
    startTime,
    endTime,
  ])

  if (result.rows.length < 10) {
    throw new Error('Insufficient historical data for prediction')
  }

  const values = result.rows.map((row) => parseFloat(row.value))
  const timestamps = result.rows.map((row) => new Date(row.timestamp))

  // Simple linear regression for trend
  const n = values.length
  const sumX = timestamps.reduce((sum, ts, i) => sum + i, 0)
  const sumY = values.reduce((sum, val) => sum + val, 0)
  const sumXY = values.reduce((sum, val, i) => sum + i * val, 0)
  const sumX2 = values.reduce((sum, _, i) => sum + i * i, 0)

  const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX)
  const intercept = (sumY - slope * sumX) / n

  // Calculate current value (most recent)
  const currentValue = values[values.length - 1]

  // Predict future value
  const futureSteps = {
    '1H': 1,
    '6H': 6,
    '24H': 24,
    '7D': 7 * 24,
    '30D': 30 * 24,
  }[config.timeframe]

  const predictedValue = slope * (n + futureSteps) + intercept

  // Calculate confidence based on data quality and variance
  const mean = sumY / n
  const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / n
  const stdDev = Math.sqrt(variance)
  const coefficientOfVariation = stdDev / mean

  // Confidence decreases with higher variance
  const baseConfidence = 100
  const confidence = Math.max(50, baseConfidence - (coefficientOfVariation * 100))

  // Determine trend
  const recentValues = values.slice(-10)
  const recentTrend = recentValues[recentValues.length - 1] - recentValues[0]
  const trend: Prediction['trend'] =
    recentTrend > 0.1 ? 'INCREASING' : recentTrend < -0.1 ? 'DECREASING' : 'STABLE'

  const prediction: Prediction = {
    id: `prediction-${config.resourceId}-${config.metricType}-${Date.now()}`,
    resourceId: config.resourceId,
    metricType: config.metricType,
    predictionType: config.predictionType,
    currentValue,
    predictedValue: Math.max(0, predictedValue), // Ensure non-negative
    confidence: Math.round(confidence),
    timeframe: config.timeframe,
    timestamp: new Date(),
    trend,
    recommendation: generateRecommendation(config, predictedValue, currentValue, trend),
  }

  // Store prediction
  await storePrediction(context, prediction)

  return prediction
}

/**
 * Predict resource costs
 */
export async function predictCost(
  context: Context,
  resourceId: string,
  timeframe: Prediction['timeframe']
): Promise<Prediction> {
  // Get cost metrics
  const costPrediction = await predictUsage(context, {
    resourceId,
    metricType: 'COST',
    timeframe,
    predictionType: 'COST',
  })

  return costPrediction
}

/**
 * Predict capacity needs
 */
export async function predictCapacity(
  context: Context,
  resourceId: string,
  metricType: 'CPU_USAGE' | 'MEMORY_USAGE' | 'STORAGE_IOPS',
  timeframe: Prediction['timeframe']
): Promise<Prediction> {
  const prediction = await predictUsage(context, {
    resourceId,
    metricType,
    timeframe,
    predictionType: 'CAPACITY',
  })

  // Add capacity-specific recommendations
  if (prediction.predictedValue > 80) {
    prediction.recommendation = `High ${metricType} predicted (${prediction.predictedValue.toFixed(2)}%). Consider scaling up resources.`
  } else if (prediction.predictedValue < 20) {
    prediction.recommendation = `Low ${metricType} predicted (${prediction.predictedValue.toFixed(2)}%). Consider scaling down to optimize costs.`
  }

  return prediction
}

/**
 * Predict potential failures
 */
export async function predictFailure(
  context: Context,
  resourceId: string,
  timeframe: Prediction['timeframe'] = '24H'
): Promise<Prediction | null> {
  const db = getDb()

  // Analyze error rates and health metrics
  const errorRatePrediction = await predictUsage(context, {
    resourceId,
    metricType: 'ERROR_RATE',
    timeframe,
    predictionType: 'FAILURE',
  })

  const healthScorePrediction = await predictUsage(context, {
    resourceId,
    metricType: 'HEALTH_SCORE',
    timeframe,
    predictionType: 'FAILURE',
  })

  // Combine predictions to assess failure risk
  const failureRisk =
    (errorRatePrediction.predictedValue / 100) * 0.6 +
    ((100 - healthScorePrediction.predictedValue) / 100) * 0.4

  if (failureRisk < 0.3) {
    // Low risk, no prediction needed
    return null
  }

  const confidence = Math.min(
    errorRatePrediction.confidence,
    healthScorePrediction.confidence
  )

  return {
    id: `failure-prediction-${resourceId}-${Date.now()}`,
    resourceId,
    metricType: 'HEALTH_SCORE',
    predictionType: 'FAILURE',
    currentValue: healthScorePrediction.currentValue,
    predictedValue: failureRisk * 100,
    confidence: Math.round(confidence * 0.8), // Lower confidence for failure predictions
    timeframe,
    timestamp: new Date(),
    trend: failureRisk > 0.5 ? 'INCREASING' : 'STABLE',
    recommendation: `Failure risk: ${(failureRisk * 100).toFixed(2)}%. Review resource health and error logs. Consider proactive maintenance.`,
  }
}

/**
 * Generate recommendation based on prediction
 */
function generateRecommendation(
  config: PredictiveConfig,
  predictedValue: number,
  currentValue: number,
  trend: Prediction['trend']
): string {
  if (config.predictionType === 'COST') {
    const costIncrease = ((predictedValue - currentValue) / currentValue) * 100
    if (costIncrease > 20) {
      return `Cost predicted to increase by ${costIncrease.toFixed(2)}%. Review resource usage and consider optimization.`
    }
    return `Cost trend: ${trend.toLowerCase()}. Monitor for optimization opportunities.`
  }

  if (config.predictionType === 'CAPACITY') {
    if (predictedValue > 90) {
      return `Capacity predicted to exceed 90%. Immediate scaling recommended.`
    }
    if (predictedValue > 80) {
      return `Capacity predicted to exceed 80%. Plan for scaling within ${config.timeframe}.`
    }
  }

  if (config.predictionType === 'USAGE') {
    const change = ((predictedValue - currentValue) / currentValue) * 100
    if (Math.abs(change) > 30) {
      return `Usage predicted to ${change > 0 ? 'increase' : 'decrease'} by ${Math.abs(change).toFixed(2)}%. Adjust resources accordingly.`
    }
  }

  return `Monitor ${config.metricType} trend: ${trend.toLowerCase()}.`
}

/**
 * Store prediction in database
 */
async function storePrediction(context: Context, prediction: Prediction): Promise<void> {
  const db = getDb()

  await db.query(
    `INSERT INTO predictions (
      id, resource_id, metric_type, prediction_type, current_value,
      predicted_value, confidence, timeframe, timestamp, trend, recommendation
    ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
    ON CONFLICT (id) DO UPDATE SET
      predicted_value = EXCLUDED.predicted_value,
      confidence = EXCLUDED.confidence,
      trend = EXCLUDED.trend,
      updated_at = NOW()`,
    [
      prediction.id,
      prediction.resourceId,
      prediction.metricType,
      prediction.predictionType,
      prediction.currentValue,
      prediction.predictedValue,
      prediction.confidence,
      prediction.timeframe,
      prediction.timestamp,
      prediction.trend,
      prediction.recommendation,
    ]
  )
}

/**
 * Get predictions for a resource
 */
export async function getPredictions(
  context: Context,
  resourceId?: string,
  limit: number = 50
): Promise<Prediction[]> {
  const db = getDb()

  let query = `
    SELECT * FROM predictions
    WHERE 1=1
  `
  const params: any[] = []
  let paramCount = 1

  if (resourceId) {
    query += ` AND resource_id = $${paramCount}`
    params.push(resourceId)
    paramCount++
  }

  query += ` ORDER BY timestamp DESC LIMIT $${paramCount}`
  params.push(limit)

  const result = await db.query(query, params)

  return result.rows.map((row) => ({
    id: row.id,
    resourceId: row.resource_id,
    metricType: row.metric_type,
    predictionType: row.prediction_type,
    currentValue: parseFloat(row.current_value),
    predictedValue: parseFloat(row.predicted_value),
    confidence: parseInt(row.confidence),
    timeframe: row.timeframe,
    timestamp: new Date(row.timestamp),
    trend: row.trend,
    recommendation: row.recommendation,
  }))
}

