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

interface CompetitionData {
  adId: string;
  platform: string;
  keyword: string;
  currentBid: number;
  avgCompetitorBid: number;
  minCompetitorBid: number;
  maxCompetitorBid: number;
  competitionLevel: 'LOW' | 'MEDIUM' | 'HIGH';
  auctionInsights: {
    impressionShare: number;
    topImpressionShare: number;
    outrankedShare: number;
  };
}

interface BiddingRecommendation {
  keyword: string;
  currentBid: number;
  recommendedBid: number;
  bidChange: number;
  bidChangePercent: number;
  strategy: 'INCREASE' | 'DECREASE' | 'MAINTAIN';
  reasoning: string;
  expectedImpact: {
    impressions: number;
    clicks: number;
    cost: number;
  };
  confidence: 'HIGH' | 'MEDIUM' | 'LOW';
}

interface BiddingStrategyResult {
  adId: string;
  platform: string;
  totalBudget: number;
  recommendations: BiddingRecommendation[];
  overallStrategy: 'AGGRESSIVE' | 'MODERATE' | 'CONSERVATIVE';
  estimatedBudgetChange: number;
  traceId: string;
}

interface BidHistory {
  bidId: string;
  adId: string;
  keyword: string;
  oldBid: number;
  newBid: number;
  performance: {
    impressions: number;
    clicks: number;
    conversions: number;
    cost: number;
  };
  timestamp: Date;
}

export class BiddingStrategyService {
  private static readonly TABLE_BID_HISTORY = 'cf_bid_history';
  private static readonly TABLE_BIDDING_RECOMMENDATIONS = 'cf_bidding_recommendations';
  
  private static readonly BID_ADJUSTMENT_RULES = {
    LOW_IMPRESSION_SHARE: {
      threshold: 0.3,
      bidIncrease: 0.15,
    },
    HIGH_IMPRESSION_SHARE: {
      threshold: 0.8,
      bidDecrease: 0.10,
    },
    LOW_TOP_SHARE: {
      threshold: 0.2,
      bidIncrease: 0.20,
    },
    HIGH_OUTRANKED_SHARE: {
      threshold: 0.5,
      bidIncrease: 0.25,
    },
  };
  
  private static readonly MAX_BID_INCREASE = 0.50;
  private static readonly MAX_BID_DECREASE = 0.30;
  
  static async generateBiddingStrategy(
    adId: string,
    competitionData: CompetitionData[],
    options: {
      tenantId: string;
      shopId: string;
      totalBudget: number;
      targetRoas?: number;
      targetCpa?: number;
      taskId: string;
      traceId: string;
      businessType: 'TOC' | 'TOB';
    }
  ): Promise<BiddingStrategyResult> {
    const { tenantId, shopId, totalBudget, targetRoas, targetCpa, taskId, traceId, businessType } = options;
    
    logger.info(`[BiddingStrategyService] Generating bidding strategy: ${JSON.stringify({
      adId,
      tenantId,
      shopId,
      keywordCount: competitionData.length,
      totalBudget,
      targetRoas,
      targetCpa,
      traceId,
      businessType,
    })}`);
    
    const recommendations: BiddingRecommendation[] = [];
    let totalCurrentBid = 0;
    let totalRecommendedBid = 0;
    
    for (const data of competitionData) {
      const recommendation = this.calculateBidRecommendation(data, targetRoas, targetCpa);
      recommendations.push(recommendation);
      totalCurrentBid += data.currentBid;
      totalRecommendedBid += recommendation.recommendedBid;
    }
    
    const overallStrategy = this.determineOverallStrategy(recommendations, totalBudget);
    
    const result: BiddingStrategyResult = {
      adId,
      platform: competitionData[0]?.platform || 'UNKNOWN',
      totalBudget,
      recommendations,
      overallStrategy,
      estimatedBudgetChange: totalRecommendedBid - totalCurrentBid,
      traceId,
    };
    
    await this.saveBiddingRecommendations(result, tenantId, shopId, taskId, traceId, businessType);
    
    logger.info(`[BiddingStrategyService] Bidding strategy generated: ${JSON.stringify({
      adId,
      recommendationCount: recommendations.length,
      overallStrategy,
      estimatedBudgetChange: result.estimatedBudgetChange,
      traceId,
    })}`);
    
    return result;
  }
  
  private static calculateBidRecommendation(
    data: CompetitionData,
    targetRoas?: number,
    targetCpa?: number
  ): BiddingRecommendation {
    let recommendedBid = data.currentBid;
    let strategy: 'INCREASE' | 'DECREASE' | 'MAINTAIN' = 'MAINTAIN';
    let reasoning = '';
    let confidence: 'HIGH' | 'MEDIUM' | 'LOW' = 'MEDIUM';
    
    const { impressionShare, topImpressionShare, outrankedShare } = data.auctionInsights;
    
    if (impressionShare < this.BID_ADJUSTMENT_RULES.LOW_IMPRESSION_SHARE.threshold) {
      const increase = this.BID_ADJUSTMENT_RULES.LOW_IMPRESSION_SHARE.bidIncrease;
      recommendedBid = data.currentBid * (1 + increase);
      strategy = 'INCREASE';
      reasoning = `Low impression share (${(impressionShare * 100).toFixed(1)}%). Increasing bid to improve visibility.`;
      confidence = 'HIGH';
    } else if (topImpressionShare < this.BID_ADJUSTMENT_RULES.LOW_TOP_SHARE.threshold) {
      const increase = this.BID_ADJUSTMENT_RULES.LOW_TOP_SHARE.bidIncrease;
      recommendedBid = data.currentBid * (1 + increase);
      strategy = 'INCREASE';
      reasoning = `Low top impression share (${(topImpressionShare * 100).toFixed(1)}%). Increasing bid for top positions.`;
      confidence = 'HIGH';
    } else if (outrankedShare > this.BID_ADJUSTMENT_RULES.HIGH_OUTRANKED_SHARE.threshold) {
      const increase = this.BID_ADJUSTMENT_RULES.HIGH_OUTRANKED_SHARE.bidIncrease;
      recommendedBid = data.currentBid * (1 + increase);
      strategy = 'INCREASE';
      reasoning = `High outranked share (${(outrankedShare * 100).toFixed(1)}%). Competitors are outbidding.`;
      confidence = 'MEDIUM';
    } else if (impressionShare > this.BID_ADJUSTMENT_RULES.HIGH_IMPRESSION_SHARE.threshold) {
      const decrease = this.BID_ADJUSTMENT_RULES.HIGH_IMPRESSION_SHARE.bidDecrease;
      recommendedBid = data.currentBid * (1 - decrease);
      strategy = 'DECREASE';
      reasoning = `High impression share (${(impressionShare * 100).toFixed(1)}%). Opportunity to reduce costs.`;
      confidence = 'MEDIUM';
    } else {
      reasoning = `Current bid is optimal for current market conditions.`;
      confidence = 'LOW';
    }
    
    if (data.competitionLevel === 'HIGH') {
      recommendedBid = Math.max(recommendedBid, data.avgCompetitorBid * 1.1);
      if (strategy === 'MAINTAIN') {
        strategy = 'INCREASE';
        reasoning = 'High competition market requires competitive bidding.';
      }
    }
    
    if (targetCpa && data.currentBid > targetCpa * 0.5) {
      recommendedBid = Math.min(recommendedBid, targetCpa * 0.4);
      if (strategy === 'INCREASE') {
        strategy = 'DECREASE';
        reasoning = `Bid exceeds target CPA ($${targetCpa}). Reducing to meet target.`;
      }
    }
    
    const maxBid = data.currentBid * (1 + this.MAX_BID_INCREASE);
    const minBid = data.currentBid * (1 - this.MAX_BID_DECREASE);
    recommendedBid = Math.max(minBid, Math.min(maxBid, recommendedBid));
    
    const bidChange = recommendedBid - data.currentBid;
    const bidChangePercent = (bidChange / data.currentBid) * 100;
    
    const expectedImpressions = Math.round(data.auctionInsights.impressionShare * 10000 * (1 + bidChangePercent / 100));
    const expectedClicks = Math.round(expectedImpressions * 0.02);
    const expectedCost = expectedClicks * recommendedBid;
    
    return {
      keyword: data.keyword,
      currentBid: data.currentBid,
      recommendedBid: Math.round(recommendedBid * 100) / 100,
      bidChange: Math.round(bidChange * 100) / 100,
      bidChangePercent: Math.round(bidChangePercent * 100) / 100,
      strategy,
      reasoning,
      expectedImpact: {
        impressions: expectedImpressions,
        clicks: expectedClicks,
        cost: Math.round(expectedCost * 100) / 100,
      },
      confidence,
    };
  }
  
  private static determineOverallStrategy(
    recommendations: BiddingRecommendation[],
    totalBudget: number
  ): 'AGGRESSIVE' | 'MODERATE' | 'CONSERVATIVE' {
    const increaseCount = recommendations.filter(r => r.strategy === 'INCREASE').length;
    const decreaseCount = recommendations.filter(r => r.strategy === 'DECREASE').length;
    const totalBudgetChange = recommendations.reduce((sum, r) => sum + r.bidChange, 0);
    
    const increaseRatio = increaseCount / recommendations.length;
    
    if (increaseRatio > 0.6 && totalBudgetChange > totalBudget * 0.1) {
      return 'AGGRESSIVE';
    } else if (decreaseCount > increaseCount) {
      return 'CONSERVATIVE';
    }
    
    return 'MODERATE';
  }
  
  private static async saveBiddingRecommendations(
    result: BiddingStrategyResult,
    tenantId: string,
    shopId: string,
    taskId: string,
    traceId: string,
    businessType: 'TOC' | 'TOB'
  ): Promise<void> {
    try {
      const records = result.recommendations.map(rec => ({
        id: `BID-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
        tenant_id: tenantId,
        shop_id: shopId,
        ad_id: result.adId,
        keyword: rec.keyword,
        current_bid: rec.currentBid,
        recommended_bid: rec.recommendedBid,
        strategy: rec.strategy,
        confidence: rec.confidence,
        reasoning: rec.reasoning,
        overall_strategy: result.overallStrategy,
        trace_id: traceId,
        task_id: taskId,
        business_type: businessType,
        created_at: new Date(),
      }));
      
      await db(this.TABLE_BIDDING_RECOMMENDATIONS).insert(records);
      
      await AuditService.log({
        action: 'BIDDING_STRATEGY_GENERATED',
        resourceType: 'bidding_strategy',
        resourceId: result.adId,
        tenantId,
        shopId,
        taskId,
        traceId,
        userId: 'SYSTEM',
        module: 'bidding_strategy',
        result: 'success',
        source: 'console',
        metadata: {
          overallStrategy: result.overallStrategy,
          recommendationCount: result.recommendations.length,
          estimatedBudgetChange: result.estimatedBudgetChange,
        },
      });
    } catch (error: any) {
      logger.error(`[BiddingStrategyService] Failed to save recommendations: ${JSON.stringify({
        adId: result.adId,
        error: error.message,
        traceId,
      })}`);
    }
  }
  
  static async applyBiddingStrategy(
    adId: string,
    recommendations: BiddingRecommendation[],
    options: {
      tenantId: string;
      shopId: string;
      taskId: string;
      traceId: string;
      businessType: 'TOC' | 'TOB';
    }
  ): Promise<{ applied: number; failed: number }> {
    const { tenantId, shopId, taskId, traceId, businessType } = options;
    
    logger.info(`[BiddingStrategyService] Applying bidding strategy: ${JSON.stringify({
      adId,
      tenantId,
      shopId,
      recommendationCount: recommendations.length,
      traceId,
    })}`);
    
    let applied = 0;
    let failed = 0;
    
    for (const rec of recommendations) {
      try {
        await this.updateBid(adId, rec, tenantId, shopId, taskId, traceId, businessType);
        applied++;
      } catch (error: any) {
        logger.error(`[BiddingStrategyService] Failed to apply bid: ${JSON.stringify({
          adId,
          keyword: rec.keyword,
          error: error.message,
          traceId,
        })}`);
        failed++;
      }
    }
    
    await AuditService.log({
      action: 'BIDDING_STRATEGY_APPLIED',
      resourceType: 'bidding_strategy',
      resourceId: adId,
      tenantId,
      shopId,
      taskId,
      traceId,
      userId: 'SYSTEM',
      module: 'bidding_strategy',
      result: 'success',
      source: 'console',
      metadata: { applied, failed },
    });
    
    return { applied, failed };
  }
  
  private static async updateBid(
    adId: string,
    recommendation: BiddingRecommendation,
    tenantId: string,
    shopId: string,
    taskId: string,
    traceId: string,
    businessType: 'TOC' | 'TOB'
  ): Promise<void> {
    await db(this.TABLE_BID_HISTORY).insert({
      id: `BIDH-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
      tenant_id: tenantId,
      shop_id: shopId,
      ad_id: adId,
      keyword: recommendation.keyword,
      old_bid: recommendation.currentBid,
      new_bid: recommendation.recommendedBid,
      strategy: recommendation.strategy,
      trace_id: traceId,
      task_id: taskId,
      business_type: businessType,
      created_at: new Date(),
    });
    
    logger.info(`[BiddingStrategyService] Bid updated: ${JSON.stringify({
      adId,
      keyword: recommendation.keyword,
      oldBid: recommendation.currentBid,
      newBid: recommendation.recommendedBid,
      traceId,
    })}`);
  }
  
  static async getBidHistory(
    adId: string,
    options: {
      tenantId: string;
      limit?: number;
      traceId: string;
    }
  ): Promise<BidHistory[]> {
    const { tenantId, limit = 30, traceId } = options;
    
    const history = await db(this.TABLE_BID_HISTORY)
      .where({ ad_id: adId, tenant_id: tenantId })
      .orderBy('created_at', 'desc')
      .limit(limit);
    
    logger.info(`[BiddingStrategyService] Retrieved bid history: ${JSON.stringify({
      adId,
      count: history.length,
      traceId,
    })}`);
    
    return history;
  }
}
