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

interface CompetitorPrice {
  competitorId: string;
  productId: string;
  platform: string;
  competitorName: string;
  price: number;
  currency: string;
  stockStatus: 'IN_STOCK' | 'LOW_STOCK' | 'OUT_OF_STOCK';
  lastChecked: Date;
  priceHistory: Array<{
    price: number;
    recordedAt: Date;
  }>;
}

interface PriceAlert {
  alertId: string;
  tenantId: string;
  productId: string;
  platform: string;
  type: 'PRICE_DROP' | 'PRICE_INCREASE' | 'COMPETITOR_OUT_OF_STOCK' | 'PRICE_MATCH_NEEDED';
  threshold: number;
  currentValue: number;
  message: string;
  status: 'ACTIVE' | 'TRIGGERED' | 'DISMISSED';
  createdAt: Date;
  triggeredAt?: Date;
}

interface PriceChangeNotification {
  notificationId: string;
  tenantId: string;
  productId: string;
  productTitle: string;
  platform: string;
  changeType: 'INCREASE' | 'DECREASE';
  oldPrice: number;
  newPrice: number;
  changePercent: number;
  competitors: Array<{
    name: string;
    price: number;
    priceDiff: number;
  }>;
  recommendation: string;
  createdAt: Date;
}

interface MonitoringTask {
  taskId: string;
  tenantId: string;
  productId: string;
  platform: string;
  competitorIds: string[];
  frequency: 'HOURLY' | 'DAILY' | 'WEEKLY';
  lastRun: Date;
  nextRun: Date;
  status: 'ACTIVE' | 'PAUSED' | 'COMPLETED';
}

export class CompetitorPriceMonitor {
  private static PRICE_CHANGE_THRESHOLD = 0.05;
  private static MAX_COMPETITORS_PER_PRODUCT = 10;

  private static generateId(prefix: string): string {
    return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  static async monitorCompetitorPrices(params: {
    tenantId: string;
    productId: string;
    platform: string;
  }): Promise<CompetitorPrice[]> {
    const { tenantId, productId, platform } = params;

    logger.info('[CompetitorPriceMonitor] Monitoring competitor prices', {
      tenantId,
      productId,
      platform
    });

    const competitors = await this.getCompetitors(productId, platform);
    const priceResults: CompetitorPrice[] = [];

    for (const competitor of competitors) {
      const price = await this.fetchCompetitorPrice(competitor, platform);
      
      if (price) {
        const history = await this.getPriceHistory(competitor.competitorId, productId);
        
        priceResults.push({
          competitorId: competitor.competitorId,
          productId,
          platform,
          competitorName: competitor.name,
          price: price.currentPrice,
          currency: price.currency,
          stockStatus: price.stockStatus,
          lastChecked: new Date(),
          priceHistory: history
        });

        await this.savePriceRecord({
          competitorId: competitor.competitorId,
          productId,
          platform,
          price: price.currentPrice,
          stockStatus: price.stockStatus
        });
      }
    }

    await this.checkForPriceAlerts(tenantId, productId, platform, priceResults);

    return priceResults;
  }

  static async setupMonitoring(params: {
    tenantId: string;
    productId: string;
    platforms: string[];
    frequency: 'HOURLY' | 'DAILY' | 'WEEKLY';
  }): Promise<MonitoringTask[]> {
    const { tenantId, productId, platforms, frequency } = params;

    logger.info('[CompetitorPriceMonitor] Setting up monitoring', {
      tenantId,
      productId,
      platforms,
      frequency
    });

    const tasks: MonitoringTask[] = [];

    for (const platform of platforms) {
      const taskId = this.generateId('mon_task');
      const nextRun = this.calculateNextRun(frequency);

      await db('cf_price_monitoring_task').insert({
        id: taskId,
        tenant_id: tenantId,
        product_id: productId,
        platform,
        frequency,
        last_run: null,
        next_run: nextRun,
        status: 'ACTIVE',
        created_at: new Date(),
        updated_at: new Date()
      });

      tasks.push({
        taskId,
        tenantId,
        productId,
        platform,
        competitorIds: [],
        frequency,
        lastRun: new Date(0),
        nextRun,
        status: 'ACTIVE'
      });
    }

    return tasks;
  }

  static async createPriceAlert(params: {
    tenantId: string;
    productId: string;
    platform: string;
    type: PriceAlert['type'];
    threshold: number;
  }): Promise<PriceAlert> {
    const { tenantId, productId, platform, type, threshold } = params;

    logger.info('[CompetitorPriceMonitor] Creating price alert', {
      tenantId,
      productId,
      platform,
      type,
      threshold
    });

    const alertId = this.generateId('alert');

    const alert: PriceAlert = {
      alertId,
      tenantId,
      productId,
      platform,
      type,
      threshold,
      currentValue: 0,
      message: '',
      status: 'ACTIVE',
      createdAt: new Date()
    };

    await db('cf_price_alert').insert({
      id: alertId,
      tenant_id: tenantId,
      product_id: productId,
      platform,
      type,
      threshold,
      current_value: 0,
      message: '',
      status: 'ACTIVE',
      created_at: new Date()
    });

    return alert;
  }

  static async getPriceChangeNotifications(params: {
    tenantId: string;
    limit?: number;
    unreadOnly?: boolean;
  }): Promise<PriceChangeNotification[]> {
    const { tenantId, limit = 20, unreadOnly = false } = params;

    let query = db('cf_price_change_notification')
      .where({ tenant_id: tenantId })
      .orderBy('created_at', 'desc')
      .limit(limit);

    if (unreadOnly) {
      query = query.where({ read: false });
    }

    const notifications = await query;

    return notifications.map(n => ({
      notificationId: n.id,
      tenantId: n.tenant_id,
      productId: n.product_id,
      productTitle: n.product_title,
      platform: n.platform,
      changeType: n.change_type,
      oldPrice: parseFloat(n.old_price),
      newPrice: parseFloat(n.new_price),
      changePercent: parseFloat(n.change_percent),
      competitors: JSON.parse(n.competitors || '[]'),
      recommendation: n.recommendation,
      createdAt: n.created_at
    }));
  }

  static async getCompetitorAnalysis(params: {
    tenantId: string;
    productId: string;
    platform: string;
  }): Promise<{
    ourPrice: number;
    avgCompetitorPrice: number;
    minCompetitorPrice: number;
    maxCompetitorPrice: number;
    pricePosition: 'LOWEST' | 'BELOW_AVG' | 'AVERAGE' | 'ABOVE_AVG' | 'HIGHEST';
    competitors: CompetitorPrice[];
    recommendation: string;
  }> {
    const { tenantId, productId, platform } = params;

    logger.info('[CompetitorPriceMonitor] Getting competitor analysis', {
      tenantId,
      productId,
      platform
    });

    const ourListing = await db('cf_listing')
      .where({ tenant_id: tenantId, product_id: productId, platform })
      .first();

    const ourPrice = ourListing?.price || 0;

    const competitors = await this.monitorCompetitorPrices({
      tenantId,
      productId,
      platform
    });

    const prices = competitors.map(c => c.price).filter(p => p > 0);

    const avgCompetitorPrice = prices.length > 0
      ? prices.reduce((a, b) => a + b, 0) / prices.length
      : 0;

    const minCompetitorPrice = prices.length > 0 ? Math.min(...prices) : 0;
    const maxCompetitorPrice = prices.length > 0 ? Math.max(...prices) : 0;

    const pricePosition = this.determinePricePosition(ourPrice, avgCompetitorPrice, minCompetitorPrice, maxCompetitorPrice);
    const recommendation = this.generatePriceRecommendation(ourPrice, avgCompetitorPrice, pricePosition);

    return {
      ourPrice,
      avgCompetitorPrice,
      minCompetitorPrice,
      maxCompetitorPrice,
      pricePosition,
      competitors,
      recommendation
    };
  }

  static async runScheduledMonitoring(): Promise<{
    tasksRun: number;
    alertsTriggered: number;
    errors: string[];
  }> {
    logger.info('[CompetitorPriceMonitor] Running scheduled monitoring');

    const now = new Date();
    const tasks = await db('cf_price_monitoring_task')
      .where('status', 'ACTIVE')
      .where('next_run', '<=', now);

    let tasksRun = 0;
    let alertsTriggered = 0;
    const errors: string[] = [];

    for (const task of tasks) {
      try {
        await this.monitorCompetitorPrices({
          tenantId: task.tenant_id,
          productId: task.product_id,
          platform: task.platform
        });

        const nextRun = this.calculateNextRun(task.frequency);

        await db('cf_price_monitoring_task')
          .where({ id: task.id })
          .update({
            last_run: now,
            next_run: nextRun,
            updated_at: now
          });

        tasksRun++;

        const triggeredAlerts = await db('cf_price_alert')
          .where({
            tenant_id: task.tenant_id,
            product_id: task.product_id,
            platform: task.platform,
            status: 'TRIGGERED'
          })
          .where('triggered_at', '>=', new Date(Date.now() - 60000));

        alertsTriggered += triggeredAlerts.length;
      } catch (error: any) {
        errors.push(`Task ${task.id}: ${error.message}`);
        logger.error('[CompetitorPriceMonitor] Task failed', {
          taskId: task.id,
          error: error.message
        });
      }
    }

    return { tasksRun, alertsTriggered, errors };
  }

  private static async getCompetitors(
    productId: string,
    platform: string
  ): Promise<Array<{ competitorId: string; name: string; externalId: string }>> {
    const competitors = await db('cf_competitor_listing')
      .where({ product_id: productId, platform })
      .limit(this.MAX_COMPETITORS_PER_PRODUCT);

    return competitors.map(c => ({
      competitorId: c.id,
      name: c.competitor_name,
      externalId: c.external_id
    }));
  }

  private static async fetchCompetitorPrice(
    competitor: { competitorId: string; name: string; externalId: string },
    platform: string
  ): Promise<{ currentPrice: number; currency: string; stockStatus: 'IN_STOCK' | 'LOW_STOCK' | 'OUT_OF_STOCK' } | null> {
    return {
      currentPrice: Math.random() * 100 + 10,
      currency: 'USD',
      stockStatus: Math.random() > 0.1 ? 'IN_STOCK' : 'OUT_OF_STOCK'
    };
  }

  private static async getPriceHistory(
    competitorId: string,
    productId: string
  ): Promise<Array<{ price: number; recordedAt: Date }>> {
    const history = await db('cf_competitor_price_history')
      .where({ competitor_id: competitorId, product_id: productId })
      .orderBy('recorded_at', 'desc')
      .limit(30);

    return history.map(h => ({
      price: parseFloat(h.price),
      recordedAt: h.recorded_at
    }));
  }

  private static async savePriceRecord(params: {
    competitorId: string;
    productId: string;
    platform: string;
    price: number;
    stockStatus: string;
  }): Promise<void> {
    await db('cf_competitor_price_history').insert({
      id: this.generateId('price_hist'),
      competitor_id: params.competitorId,
      product_id: params.productId,
      platform: params.platform,
      price: params.price,
      stock_status: params.stockStatus,
      recorded_at: new Date()
    });
  }

  private static async checkForPriceAlerts(
    tenantId: string,
    productId: string,
    platform: string,
    competitorPrices: CompetitorPrice[]
  ): Promise<void> {
    const alerts = await db('cf_price_alert')
      .where({
        tenant_id: tenantId,
        product_id: productId,
        platform,
        status: 'ACTIVE'
      });

    for (const alert of alerts) {
      const shouldTrigger = await this.evaluateAlert(alert, competitorPrices);

      if (shouldTrigger) {
        await this.triggerAlert(alert, competitorPrices);
      }
    }
  }

  private static async evaluateAlert(
    alert: any,
    competitorPrices: CompetitorPrice[]
  ): Promise<boolean> {
    switch (alert.type) {
      case 'PRICE_DROP':
        return competitorPrices.some(c => 
          c.price < alert.threshold
        );
      case 'PRICE_INCREASE':
        return competitorPrices.some(c => 
          c.price > alert.threshold
        );
      case 'COMPETITOR_OUT_OF_STOCK':
        return competitorPrices.some(c => 
          c.stockStatus === 'OUT_OF_STOCK'
        );
      case 'PRICE_MATCH_NEEDED':
        return competitorPrices.some(c => 
          c.price < alert.threshold
        );
      default:
        return false;
    }
  }

  private static async triggerAlert(
    alert: any,
    competitorPrices: CompetitorPrice[]
  ): Promise<void> {
    await db('cf_price_alert')
      .where({ id: alert.id })
      .update({
        status: 'TRIGGERED',
        triggered_at: new Date(),
        updated_at: new Date()
      });

    logger.info('[CompetitorPriceMonitor] Alert triggered', {
      alertId: alert.id,
      type: alert.type
    });
  }

  private static determinePricePosition(
    ourPrice: number,
    avgPrice: number,
    minPrice: number,
    maxPrice: number
  ): 'LOWEST' | 'BELOW_AVG' | 'AVERAGE' | 'ABOVE_AVG' | 'HIGHEST' {
    if (ourPrice <= minPrice) return 'LOWEST';
    if (ourPrice >= maxPrice) return 'HIGHEST';
    if (ourPrice < avgPrice * 0.9) return 'BELOW_AVG';
    if (ourPrice > avgPrice * 1.1) return 'ABOVE_AVG';
    return 'AVERAGE';
  }

  private static generatePriceRecommendation(
    ourPrice: number,
    avgPrice: number,
    position: string
  ): string {
    switch (position) {
      case 'LOWEST':
        return 'You have the lowest price. Consider increasing slightly to improve margins while remaining competitive.';
      case 'BELOW_AVG':
        return 'Your price is below average. Good competitive position with room for margin improvement.';
      case 'AVERAGE':
        return 'Your price is at market average. Consider differentiation through value-add or slight price adjustment.';
      case 'ABOVE_AVG':
        return 'Your price is above average. Ensure your value proposition justifies the premium.';
      case 'HIGHEST':
        return 'You have the highest price. Consider reducing to remain competitive or emphasize premium value.';
      default:
        return 'Monitor competitor prices regularly to maintain competitive positioning.';
    }
  }

  private static calculateNextRun(frequency: 'HOURLY' | 'DAILY' | 'WEEKLY'): Date {
    const now = new Date();
    switch (frequency) {
      case 'HOURLY':
        return new Date(now.getTime() + 60 * 60 * 1000);
      case 'DAILY':
        return new Date(now.getTime() + 24 * 60 * 60 * 1000);
      case 'WEEKLY':
        return new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
    }
  }
}

export default CompetitorPriceMonitor;
