import db from '../config/database';
import { logger } from '../utils/logger';
import { AuditService } from './AuditService';

interface HistoricalData {
  adId: string;
  platform: string;
  dailyMetrics: Array<{
    date: Date;
    impressions: number;
    clicks: number;
    conversions: number;
    spend: number;
    revenue: number;
  }>;
  seasonality: {
    dayOfWeek: number[];
    monthly: number[];
    holiday: number[];
  };
}

interface PredictionResult {
  adId: string;
  platform: string;
  predictionPeriod: { start: Date; end: Date };
  metrics: {
    impressions: PredictedMetric;
    clicks: PredictedMetric;
    conversions: PredictedMetric;
    spend: PredictedMetric;
    revenue: PredictedMetric;
    roas: PredictedMetric;
  };
  confidence: number;
  factors: PredictionFactor[];
  recommendations: string[];
  traceId: string;
}

interface PredictedMetric {
  predicted: number;
  lowerBound: number;
  upperBound: number;
  trend: 'INCREASING' | 'DECREASING' | 'STABLE';
  changeFromLastPeriod: number;
}

interface PredictionFactor {
  name: string;
  impact: 'POSITIVE' | 'NEGATIVE' | 'NEUTRAL';
  weight: number;
  description: string;
}

interface ScenarioAnalysis {
  scenario: 'OPTIMISTIC' | 'REALISTIC' | 'PESSIMISTIC';
  metrics: {
    impressions: number;
    clicks: number;
    conversions: number;
    spend: number;
    revenue: number;
    roas: number;
  };
  probability: number;
}

export class AdPredictionService {
  private static readonly TABLE_PREDICTIONS = 'cf_ad_predictions';
  private static readonly TABLE_PREDICTION_HISTORY = 'cf_ad_prediction_history';
  
  private static readonly SEASONALITY_WEIGHTS = {
    DAY_OF_WEEK: 0.15,
    MONTHLY: 0.25,
    HOLIDAY: 0.20,
    TREND: 0.40,
  };
  
  private static readonly CONFIDENCE_THRESHOLDS = {
    HIGH: 0.8,
    MEDIUM: 0.6,
    LOW: 0.4,
  };
  
  static async predictPerformance(
    adId: string,
    historicalData: HistoricalData,
    predictionDays: number,
    options: {
      tenantId: string;
      shopId: string;
      taskId: string;
      traceId: string;
      businessType: 'TOC' | 'TOB';
    }
  ): Promise<PredictionResult> {
    const { tenantId, shopId, taskId, traceId, businessType } = options;
    
    logger.info(`[AdPredictionService] Starting performance prediction: ${JSON.stringify({
      adId,
      tenantId,
      shopId,
      predictionDays,
      dataPoints: historicalData.dailyMetrics.length,
      traceId,
      businessType,
    })}`);
    
    const trendAnalysis = this.analyzeTrend(historicalData.dailyMetrics);
    const seasonalityFactors = this.calculateSeasonalityFactors(historicalData);
    const baseMetrics = this.calculateBaseMetrics(historicalData.dailyMetrics);
    
    const predictedMetrics = this.generatePredictions(
      baseMetrics,
      trendAnalysis,
      seasonalityFactors,
      predictionDays
    );
    
    const confidence = this.calculateConfidence(historicalData, trendAnalysis);
    const factors = this.identifyFactors(trendAnalysis, seasonalityFactors);
    const recommendations = this.generateRecommendations(predictedMetrics, trendAnalysis);
    
    const result: PredictionResult = {
      adId,
      platform: historicalData.platform,
      predictionPeriod: {
        start: new Date(),
        end: new Date(Date.now() + predictionDays * 24 * 60 * 60 * 1000),
      },
      metrics: predictedMetrics,
      confidence,
      factors,
      recommendations,
      traceId,
    };
    
    await this.savePrediction(result, tenantId, shopId, taskId, traceId, businessType);
    
    logger.info(`[AdPredictionService] Prediction completed: ${JSON.stringify({
      adId,
      confidence,
      predictedRoas: predictedMetrics.roas.predicted,
      traceId,
    })}`);
    
    return result;
  }
  
  private static analyzeTrend(dailyMetrics: HistoricalData['dailyMetrics']): {
    direction: 'INCREASING' | 'DECREASING' | 'STABLE';
    strength: number;
    slope: number;
  } {
    if (dailyMetrics.length < 7) {
      return { direction: 'STABLE', strength: 0, slope: 0 };
    }
    
    const recentMetrics = dailyMetrics.slice(-14);
    const n = recentMetrics.length;
    
    let sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
    recentMetrics.forEach((m, i) => {
      const x = i;
      const y = m.conversions;
      sumX += x;
      sumY += y;
      sumXY += x * y;
      sumX2 += x * x;
    });
    
    const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
    
    const meanY = sumY / n;
    const strength = Math.abs(slope / meanY);
    
    let direction: 'INCREASING' | 'DECREASING' | 'STABLE';
    if (slope > 0.05 * meanY) {
      direction = 'INCREASING';
    } else if (slope < -0.05 * meanY) {
      direction = 'DECREASING';
    } else {
      direction = 'STABLE';
    }
    
    return { direction, strength: Math.min(1, strength), slope };
  }
  
  private static calculateSeasonalityFactors(data: HistoricalData): {
    dayOfWeek: number[];
    monthly: number[];
    holidayMultiplier: number;
  } {
    const dayOfWeekFactors = data.seasonality.dayOfWeek.length > 0
      ? data.seasonality.dayOfWeek
      : [1, 1, 1, 1, 1, 1.2, 1.3];
    
    const monthlyFactors = data.seasonality.monthly.length > 0
      ? data.seasonality.monthly
      : Array(12).fill(1);
    
    const holidayMultiplier = data.seasonality.holiday.length > 0
      ? data.seasonality.holiday.reduce((a, b) => a + b, 0) / data.seasonality.holiday.length
      : 1.2;
    
    return { dayOfWeek: dayOfWeekFactors, monthly: monthlyFactors, holidayMultiplier };
  }
  
  private static calculateBaseMetrics(dailyMetrics: HistoricalData['dailyMetrics']): {
    avgImpressions: number;
    avgClicks: number;
    avgConversions: number;
    avgSpend: number;
    avgRevenue: number;
    avgRoas: number;
    variance: number;
  } {
    const recent = dailyMetrics.slice(-30);
    
    const sum = recent.reduce((acc, m) => ({
      impressions: acc.impressions + m.impressions,
      clicks: acc.clicks + m.clicks,
      conversions: acc.conversions + m.conversions,
      spend: acc.spend + m.spend,
      revenue: acc.revenue + m.revenue,
    }), { impressions: 0, clicks: 0, conversions: 0, spend: 0, revenue: 0 });
    
    const count = recent.length || 1;
    
    const avgImpressions = sum.impressions / count;
    const avgClicks = sum.clicks / count;
    const avgConversions = sum.conversions / count;
    const avgSpend = sum.spend / count;
    const avgRevenue = sum.revenue / count;
    const avgRoas = avgSpend > 0 ? avgRevenue / avgSpend : 0;
    
    const variance = recent.reduce((acc, m) => {
      const diff = m.conversions - avgConversions;
      return acc + diff * diff;
    }, 0) / count;
    
    return { avgImpressions, avgClicks, avgConversions, avgSpend, avgRevenue, avgRoas, variance };
  }
  
  private static generatePredictions(
    baseMetrics: ReturnType<typeof AdPredictionService['calculateBaseMetrics']>,
    trendAnalysis: ReturnType<typeof AdPredictionService['analyzeTrend']>,
    seasonalityFactors: ReturnType<typeof AdPredictionService['calculateSeasonalityFactors']>,
    predictionDays: number
  ): PredictionResult['metrics'] {
    const trendMultiplier = trendAnalysis.direction === 'INCREASING'
      ? 1 + trendAnalysis.strength * 0.5
      : trendAnalysis.direction === 'DECREASING'
        ? 1 - trendAnalysis.strength * 0.5
        : 1;
    
    const dayOfWeekFactor = seasonalityFactors.dayOfWeek[new Date().getDay()] || 1;
    const monthlyFactor = seasonalityFactors.monthly[new Date().getMonth()] || 1;
    
    const combinedFactor = trendMultiplier * dayOfWeekFactor * monthlyFactor;
    
    const varianceMultiplier = Math.sqrt(baseMetrics.variance) / (baseMetrics.avgConversions || 1);
    const uncertaintyRange = Math.max(0.1, varianceMultiplier * 0.5);
    
    const predictMetric = (base: number): PredictedMetric => {
      const predicted = base * combinedFactor * predictionDays;
      const lowerBound = predicted * (1 - uncertaintyRange);
      const upperBound = predicted * (1 + uncertaintyRange);
      
      return {
        predicted: Math.round(predicted),
        lowerBound: Math.round(lowerBound),
        upperBound: Math.round(upperBound),
        trend: trendAnalysis.direction,
        changeFromLastPeriod: Math.round((combinedFactor - 1) * 100),
      };
    };
    
    return {
      impressions: predictMetric(baseMetrics.avgImpressions),
      clicks: predictMetric(baseMetrics.avgClicks),
      conversions: predictMetric(baseMetrics.avgConversions),
      spend: predictMetric(baseMetrics.avgSpend),
      revenue: predictMetric(baseMetrics.avgRevenue),
      roas: {
        predicted: Math.round(baseMetrics.avgRoas * combinedFactor * 100) / 100,
        lowerBound: Math.round(baseMetrics.avgRoas * (1 - uncertaintyRange) * 100) / 100,
        upperBound: Math.round(baseMetrics.avgRoas * (1 + uncertaintyRange) * 100) / 100,
        trend: trendAnalysis.direction,
        changeFromLastPeriod: Math.round((combinedFactor - 1) * 100),
      },
    };
  }
  
  private static calculateConfidence(
    data: HistoricalData,
    trendAnalysis: ReturnType<typeof AdPredictionService['analyzeTrend']>
  ): number {
    let confidence = 0.5;
    
    if (data.dailyMetrics.length >= 30) {
      confidence += 0.2;
    } else if (data.dailyMetrics.length >= 14) {
      confidence += 0.1;
    }
    
    if (trendAnalysis.strength < 0.3) {
      confidence += 0.15;
    } else if (trendAnalysis.strength < 0.5) {
      confidence += 0.05;
    }
    
    if (data.seasonality.dayOfWeek.length > 0) {
      confidence += 0.1;
    }
    if (data.seasonality.monthly.length > 0) {
      confidence += 0.05;
    }
    
    return Math.min(0.95, Math.max(0.3, confidence));
  }
  
  private static identifyFactors(
    trendAnalysis: ReturnType<typeof AdPredictionService['analyzeTrend']>,
    seasonalityFactors: ReturnType<typeof AdPredictionService['calculateSeasonalityFactors']>
  ): PredictionFactor[] {
    const factors: PredictionFactor[] = [];
    
    if (trendAnalysis.direction === 'INCREASING') {
      factors.push({
        name: 'Positive Trend',
        impact: 'POSITIVE',
        weight: this.SEASONALITY_WEIGHTS.TREND,
        description: `Performance trend is increasing with ${(trendAnalysis.strength * 100).toFixed(0)}% strength`,
      });
    } else if (trendAnalysis.direction === 'DECREASING') {
      factors.push({
        name: 'Negative Trend',
        impact: 'NEGATIVE',
        weight: this.SEASONALITY_WEIGHTS.TREND,
        description: `Performance trend is decreasing with ${(trendAnalysis.strength * 100).toFixed(0)}% strength`,
      });
    }
    
    const maxDayFactor = Math.max(...seasonalityFactors.dayOfWeek);
    if (maxDayFactor > 1.1) {
      factors.push({
        name: 'Day-of-Week Seasonality',
        impact: 'POSITIVE',
        weight: this.SEASONALITY_WEIGHTS.DAY_OF_WEEK,
        description: `Peak day factor: ${maxDayFactor.toFixed(2)}x`,
      });
    }
    
    if (seasonalityFactors.holidayMultiplier > 1.1) {
      factors.push({
        name: 'Holiday Effect',
        impact: 'POSITIVE',
        weight: this.SEASONALITY_WEIGHTS.HOLIDAY,
        description: `Holiday multiplier: ${seasonalityFactors.holidayMultiplier.toFixed(2)}x`,
      });
    }
    
    return factors;
  }
  
  private static generateRecommendations(
    metrics: PredictionResult['metrics'],
    trendAnalysis: ReturnType<typeof AdPredictionService['analyzeTrend']>
  ): string[] {
    const recommendations: string[] = [];
    
    if (metrics.roas.predicted < 2) {
      recommendations.push('Predicted ROAS is below 2x. Consider pausing or reducing budget.');
    } else if (metrics.roas.predicted > 4) {
      recommendations.push('Excellent predicted ROAS. Consider increasing budget to scale.');
    }
    
    if (trendAnalysis.direction === 'DECREASING') {
      recommendations.push('Performance is trending downward. Review targeting and creative.');
    }
    
    if (metrics.conversions.changeFromLastPeriod < -10) {
      recommendations.push('Conversions expected to decrease. Investigate potential causes.');
    }
    
    if (recommendations.length === 0) {
      recommendations.push('Performance is stable. Continue monitoring and optimizing.');
    }
    
    return recommendations;
  }
  
  private static async savePrediction(
    result: PredictionResult,
    tenantId: string,
    shopId: string,
    taskId: string,
    traceId: string,
    businessType: 'TOC' | 'TOB'
  ): Promise<void> {
    try {
      await db(this.TABLE_PREDICTIONS).insert({
        id: `PRED-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
        tenant_id: tenantId,
        shop_id: shopId,
        ad_id: result.adId,
        platform: result.platform,
        prediction_start: result.predictionPeriod.start,
        prediction_end: result.predictionPeriod.end,
        predicted_impressions: result.metrics.impressions.predicted,
        predicted_clicks: result.metrics.clicks.predicted,
        predicted_conversions: result.metrics.conversions.predicted,
        predicted_spend: result.metrics.spend.predicted,
        predicted_revenue: result.metrics.revenue.predicted,
        predicted_roas: result.metrics.roas.predicted,
        confidence: result.confidence,
        factors: JSON.stringify(result.factors),
        recommendations: JSON.stringify(result.recommendations),
        trace_id: traceId,
        task_id: taskId,
        business_type: businessType,
        created_at: new Date(),
      });
      
      await AuditService.log({
        action: 'AD_PREDICTION_GENERATED',
        resourceType: 'ad_prediction',
        resourceId: result.adId,
        tenantId,
        shopId,
        taskId,
        traceId,
        userId: 'SYSTEM',
        module: 'ad_prediction',
        result: 'success',
        source: 'console',
        metadata: {
          confidence: result.confidence,
          predictedRoas: result.metrics.roas.predicted,
        },
      });
    } catch (error: any) {
      logger.error(`[AdPredictionService] Failed to save prediction: ${JSON.stringify({
        adId: result.adId,
        error: error.message,
        traceId,
      })}`);
    }
  }
  
  static async runScenarioAnalysis(
    adId: string,
    historicalData: HistoricalData,
    options: {
      tenantId: string;
      shopId: string;
      taskId: string;
      traceId: string;
      businessType: 'TOC' | 'TOB';
    }
  ): Promise<ScenarioAnalysis[]> {
    const { tenantId, shopId, taskId, traceId, businessType } = options;
    
    logger.info(`[AdPredictionService] Running scenario analysis: ${JSON.stringify({
      adId,
      tenantId,
      shopId,
      traceId,
    })}`);
    
    const baseMetrics = this.calculateBaseMetrics(historicalData.dailyMetrics);
    const trendAnalysis = this.analyzeTrend(historicalData.dailyMetrics);
    
    const scenarios: ScenarioAnalysis[] = [
      {
        scenario: 'OPTIMISTIC',
        metrics: {
          impressions: Math.round(baseMetrics.avgImpressions * 1.3 * 7),
          clicks: Math.round(baseMetrics.avgClicks * 1.3 * 7),
          conversions: Math.round(baseMetrics.avgConversions * 1.3 * 7),
          spend: Math.round(baseMetrics.avgSpend * 1.2 * 7),
          revenue: Math.round(baseMetrics.avgRevenue * 1.4 * 7),
          roas: Math.round(baseMetrics.avgRoas * 1.2 * 100) / 100,
        },
        probability: 0.25,
      },
      {
        scenario: 'REALISTIC',
        metrics: {
          impressions: Math.round(baseMetrics.avgImpressions * 7),
          clicks: Math.round(baseMetrics.avgClicks * 7),
          conversions: Math.round(baseMetrics.avgConversions * 7),
          spend: Math.round(baseMetrics.avgSpend * 7),
          revenue: Math.round(baseMetrics.avgRevenue * 7),
          roas: Math.round(baseMetrics.avgRoas * 100) / 100,
        },
        probability: 0.5,
      },
      {
        scenario: 'PESSIMISTIC',
        metrics: {
          impressions: Math.round(baseMetrics.avgImpressions * 0.7 * 7),
          clicks: Math.round(baseMetrics.avgClicks * 0.7 * 7),
          conversions: Math.round(baseMetrics.avgConversions * 0.6 * 7),
          spend: Math.round(baseMetrics.avgSpend * 0.8 * 7),
          revenue: Math.round(baseMetrics.avgRevenue * 0.5 * 7),
          roas: Math.round(baseMetrics.avgRoas * 0.7 * 100) / 100,
        },
        probability: 0.25,
      },
    ];
    
    await AuditService.log({
      action: 'SCENARIO_ANALYSIS_COMPLETED',
      resourceType: 'ad_prediction',
      resourceId: adId,
      tenantId,
      shopId,
      taskId,
      traceId,
      userId: 'SYSTEM',
      module: 'ad_prediction',
      result: 'success',
      source: 'console',
      metadata: { scenarios: scenarios.map(s => s.scenario) },
    });
    
    return scenarios;
  }
}
