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

interface AdData {
  adId: string;
  platform: string;
  impressions: number;
  clicks: number;
  conversions: number;
  spend: number;
  revenue: number;
  ctr: number;
  cpc: number;
  cpa: number;
  roas: number;
  dateRange: { start: Date; end: Date };
}

interface MarketData {
  platform: string;
  category: string;
  avgCtr: number;
  avgCpc: number;
  avgCpa: number;
  competitionLevel: 'LOW' | 'MEDIUM' | 'HIGH';
  trendDirection: 'UP' | 'DOWN' | 'STABLE';
  seasonality: number;
}

interface OptimizationSuggestion {
  type: 'BUDGET' | 'BIDDING' | 'TARGETING' | 'CREATIVE' | 'SCHEDULING';
  priority: 'HIGH' | 'MEDIUM' | 'LOW';
  description: string;
  expectedImpact: {
    metric: string;
    improvement: number;
  };
  action: string;
}

interface OptimizationResult {
  adId: string;
  currentPerformance: AdData;
  suggestions: OptimizationSuggestion[];
  overallScore: number;
  traceId: string;
}

export class AdOptimizationService {
  private static readonly TABLE_AD_PERFORMANCE = 'cf_ad_performance';
  private static readonly TABLE_OPTIMIZATION_LOG = 'cf_ad_optimization_log';
  
  private static readonly PERFORMANCE_THRESHOLDS = {
    CTR_GOOD: 0.02,
    CTR_EXCELLENT: 0.05,
    CPC_GOOD: 1.0,
    CPC_EXCELLENT: 0.5,
    ROAS_GOOD: 2.0,
    ROAS_EXCELLENT: 4.0,
    CPA_GOOD: 20,
    CPA_EXCELLENT: 10,
  };
  
  static async analyzeAndOptimize(
    adId: string,
    adData: AdData,
    marketData: MarketData,
    options: {
      tenantId: string;
      shopId: string;
      taskId: string;
      traceId: string;
      businessType: 'TOC' | 'TOB';
    }
  ): Promise<OptimizationResult> {
    const { tenantId, shopId, taskId, traceId, businessType } = options;
    
    logger.info(`[AdOptimizationService] Starting ad optimization: ${JSON.stringify({
      adId,
      tenantId,
      shopId,
      taskId,
      traceId,
      businessType,
    })}`);
    
    const suggestions: OptimizationSuggestion[] = [];
    
    suggestions.push(...this.analyzeCtr(adData, marketData));
    suggestions.push(...this.analyzeCpc(adData, marketData));
    suggestions.push(...this.analyzeRoas(adData, marketData));
    suggestions.push(...this.analyzeCpa(adData, marketData));
    suggestions.push(...this.analyzeBudget(adData, marketData));
    suggestions.push(...this.analyzeCompetition(adData, marketData));
    
    const overallScore = this.calculateOverallScore(adData, marketData);
    
    const result: OptimizationResult = {
      adId,
      currentPerformance: adData,
      suggestions: suggestions.sort((a, b) => {
        const priorityOrder = { HIGH: 0, MEDIUM: 1, LOW: 2 };
        return priorityOrder[a.priority] - priorityOrder[b.priority];
      }),
      overallScore,
      traceId,
    };
    
    await this.saveOptimizationLog(result, tenantId, shopId, taskId, traceId, businessType);
    
    logger.info(`[AdOptimizationService] Optimization completed: ${JSON.stringify({
      adId,
      suggestionCount: suggestions.length,
      overallScore,
      traceId,
    })}`);
    
    return result;
  }
  
  private static analyzeCtr(adData: AdData, marketData: MarketData): OptimizationSuggestion[] {
    const suggestions: OptimizationSuggestion[] = [];
    const ctrRatio = adData.ctr / marketData.avgCtr;
    
    if (adData.ctr < this.PERFORMANCE_THRESHOLDS.CTR_GOOD) {
      suggestions.push({
        type: 'CREATIVE',
        priority: 'HIGH',
        description: `CTR (${(adData.ctr * 100).toFixed(2)}%) is below good threshold (${(this.PERFORMANCE_THRESHOLDS.CTR_GOOD * 100)}%)`,
        expectedImpact: {
          metric: 'CTR',
          improvement: 50,
        },
        action: 'Review and update ad creative, headline, and call-to-action. Consider A/B testing new variations.',
      });
    } else if (ctrRatio < 0.8) {
      suggestions.push({
        type: 'TARGETING',
        priority: 'MEDIUM',
        description: `CTR is ${(ctrRatio * 100).toFixed(0)}% of market average`,
        expectedImpact: {
          metric: 'CTR',
          improvement: 25,
        },
        action: 'Refine audience targeting to reach more relevant users. Consider negative keywords.',
      });
    }
    
    return suggestions;
  }
  
  private static analyzeCpc(adData: AdData, marketData: MarketData): OptimizationSuggestion[] {
    const suggestions: OptimizationSuggestion[] = [];
    const cpcRatio = adData.cpc / marketData.avgCpc;
    
    if (adData.cpc > this.PERFORMANCE_THRESHOLDS.CPC_GOOD * 2) {
      suggestions.push({
        type: 'BIDDING',
        priority: 'HIGH',
        description: `CPC ($${adData.cpc.toFixed(2)}) is significantly above good threshold ($${this.PERFORMANCE_THRESHOLDS.CPC_GOOD})`,
        expectedImpact: {
          metric: 'CPC',
          improvement: 30,
        },
        action: 'Consider lowering bid amounts or switching to target CPA bidding strategy.',
      });
    } else if (cpcRatio > 1.5) {
      suggestions.push({
        type: 'BIDDING',
        priority: 'MEDIUM',
        description: `CPC is ${(cpcRatio * 100).toFixed(0)}% of market average`,
        expectedImpact: {
          metric: 'CPC',
          improvement: 20,
        },
        action: 'Optimize bid strategy based on conversion data. Consider dayparting adjustments.',
      });
    }
    
    return suggestions;
  }
  
  private static analyzeRoas(adData: AdData, marketData: MarketData): OptimizationSuggestion[] {
    const suggestions: OptimizationSuggestion[] = [];
    
    if (adData.roas < this.PERFORMANCE_THRESHOLDS.ROAS_GOOD) {
      suggestions.push({
        type: 'BUDGET',
        priority: 'HIGH',
        description: `ROAS (${adData.roas.toFixed(2)}x) is below good threshold (${this.PERFORMANCE_THRESHOLDS.ROAS_GOOD}x)`,
        expectedImpact: {
          metric: 'ROAS',
          improvement: 40,
        },
        action: 'Reduce budget allocation to this ad. Focus on high-performing keywords and audiences.',
      });
    } else if (adData.roas >= this.PERFORMANCE_THRESHOLDS.ROAS_EXCELLENT) {
      suggestions.push({
        type: 'BUDGET',
        priority: 'MEDIUM',
        description: `ROAS (${adData.roas.toFixed(2)}x) is excellent. Consider scaling.`,
        expectedImpact: {
          metric: 'Revenue',
          improvement: 25,
        },
        action: 'Increase budget to capture more market share. Monitor performance closely during scaling.',
      });
    }
    
    return suggestions;
  }
  
  private static analyzeCpa(adData: AdData, marketData: MarketData): OptimizationSuggestion[] {
    const suggestions: OptimizationSuggestion[] = [];
    const cpaRatio = adData.cpa / marketData.avgCpa;
    
    if (adData.cpa > this.PERFORMANCE_THRESHOLDS.CPA_GOOD * 2) {
      suggestions.push({
        type: 'TARGETING',
        priority: 'HIGH',
        description: `CPA ($${adData.cpa.toFixed(2)}) is significantly above good threshold ($${this.PERFORMANCE_THRESHOLDS.CPA_GOOD})`,
        expectedImpact: {
          metric: 'CPA',
          improvement: 35,
        },
        action: 'Review landing page experience and conversion funnel. Narrow targeting to high-intent audiences.',
      });
    } else if (cpaRatio > 1.3) {
      suggestions.push({
        type: 'TARGETING',
        priority: 'MEDIUM',
        description: `CPA is ${(cpaRatio * 100).toFixed(0)}% of market average`,
        expectedImpact: {
          metric: 'CPA',
          improvement: 20,
        },
        action: 'Optimize for conversions rather than clicks. Review keyword relevance and match types.',
      });
    }
    
    return suggestions;
  }
  
  private static analyzeBudget(adData: AdData, marketData: MarketData): OptimizationSuggestion[] {
    const suggestions: OptimizationSuggestion[] = [];
    
    if (marketData.competitionLevel === 'HIGH' && adData.spend < 100) {
      suggestions.push({
        type: 'BUDGET',
        priority: 'HIGH',
        description: 'Low budget in high competition market may limit visibility',
        expectedImpact: {
          metric: 'Impressions',
          improvement: 50,
        },
        action: 'Consider increasing daily budget to compete effectively in this market.',
      });
    }
    
    if (marketData.trendDirection === 'UP' && adData.roas > this.PERFORMANCE_THRESHOLDS.ROAS_GOOD) {
      suggestions.push({
        type: 'BUDGET',
        priority: 'MEDIUM',
        description: 'Market trend is positive with good ROAS. Opportunity to scale.',
        expectedImpact: {
          metric: 'Conversions',
          improvement: 30,
        },
        action: 'Increase budget to capture growing market demand.',
      });
    }
    
    return suggestions;
  }
  
  private static analyzeCompetition(adData: AdData, marketData: MarketData): OptimizationSuggestion[] {
    const suggestions: OptimizationSuggestion[] = [];
    
    if (marketData.competitionLevel === 'HIGH') {
      suggestions.push({
        type: 'SCHEDULING',
        priority: 'LOW',
        description: 'High competition market - consider off-peak scheduling',
        expectedImpact: {
          metric: 'CPC',
          improvement: 15,
        },
        action: 'Test running ads during less competitive hours to reduce costs.',
      });
    }
    
    if (marketData.seasonality > 0.7) {
      suggestions.push({
        type: 'BUDGET',
        priority: 'MEDIUM',
        description: 'High seasonality detected - adjust budget accordingly',
        expectedImpact: {
          metric: 'ROAS',
          improvement: 20,
        },
        action: 'Increase budget during peak seasons, reduce during off-peak periods.',
      });
    }
    
    return suggestions;
  }
  
  private static calculateOverallScore(adData: AdData, marketData: MarketData): number {
    let score = 50;
    
    const ctrScore = Math.min(25, (adData.ctr / this.PERFORMANCE_THRESHOLDS.CTR_EXCELLENT) * 25);
    const cpcScore = Math.max(0, 25 - (adData.cpc / this.PERFORMANCE_THRESHOLDS.CPC_EXCELLENT) * 25);
    const roasScore = Math.min(25, (adData.roas / this.PERFORMANCE_THRESHOLDS.ROAS_EXCELLENT) * 25);
    const cpaScore = Math.max(0, 25 - (adData.cpa / this.PERFORMANCE_THRESHOLDS.CPA_EXCELLENT) * 25);
    
    score = ctrScore + cpcScore + roasScore + cpaScore;
    
    return Math.min(100, Math.max(0, Math.round(score)));
  }
  
  private static async saveOptimizationLog(
    result: OptimizationResult,
    tenantId: string,
    shopId: string,
    taskId: string,
    traceId: string,
    businessType: 'TOC' | 'TOB'
  ): Promise<void> {
    try {
      await db(this.TABLE_OPTIMIZATION_LOG).insert({
        id: `OPT-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
        tenant_id: tenantId,
        shop_id: shopId,
        ad_id: result.adId,
        overall_score: result.overallScore,
        suggestions: JSON.stringify(result.suggestions),
        trace_id: traceId,
        task_id: taskId,
        business_type: businessType,
        created_at: new Date(),
      });
      
      await AuditService.log({
        action: 'AD_OPTIMIZATION',
        resourceType: 'ad_optimization',
        resourceId: result.adId,
        tenantId,
        shopId,
        taskId,
        traceId,
        userId: 'SYSTEM',
        module: 'ad_optimization',
        result: 'success',
        source: 'console',
        metadata: {
          overallScore: result.overallScore,
          suggestionCount: result.suggestions.length,
        },
      });
    } catch (error: any) {
      logger.error(`[AdOptimizationService] Failed to save optimization log: ${JSON.stringify({
        adId: result.adId,
        error: error.message,
        traceId,
      })}`);
    }
  }
  
  static async batchOptimize(
    ads: Array<{ adId: string; adData: AdData; marketData: MarketData }>,
    options: {
      tenantId: string;
      shopId: string;
      taskId: string;
      traceId: string;
      businessType: 'TOC' | 'TOB';
    }
  ): Promise<OptimizationResult[]> {
    logger.info(`[AdOptimizationService] Starting batch optimization: ${JSON.stringify({
      count: ads.length,
      traceId: options.traceId,
    })}`);
    
    const results: OptimizationResult[] = [];
    
    for (const ad of ads) {
      try {
        const result = await this.analyzeAndOptimize(
          ad.adId,
          ad.adData,
          ad.marketData,
          options
        );
        results.push(result);
      } catch (error: any) {
        logger.error(`[AdOptimizationService] Batch optimization failed for ad: ${JSON.stringify({
          adId: ad.adId,
          error: error.message,
          traceId: options.traceId,
        })}`);
      }
    }
    
    return results;
  }
}
