import db from '../config/database';
import { logger } from '../utils/logger';
import StrategyService, { Strategy } from './StrategyService';
import MerchantMetricsService from './MerchantMetricsService';

export interface RecommendationContext {
  tenantId: string;
  shopId?: string;
  currentMetrics?: {
    revenue: number;
    roi: number;
    orderCount: number;
    productCount: number;
  };
  activeStrategies?: string[];
  preferences?: {
    riskTolerance: 'LOW' | 'MEDIUM' | 'HIGH';
    budget: number;
    categories?: string[];
  };
}

export interface StrategyRecommendation {
  strategy: Strategy;
  score: number;
  reasons: string[];
  expectedRoi: number;
  confidence: number;
  priority: 'HIGH' | 'MEDIUM' | 'LOW';
}

export class StrategyRecommendationService {
  private static instance: StrategyRecommendationService;

  private constructor() {}

  static getInstance(): StrategyRecommendationService {
    if (!StrategyRecommendationService.instance) {
      StrategyRecommendationService.instance = new StrategyRecommendationService();
    }
    return StrategyRecommendationService.instance;
  }

  async getRecommendations(context: RecommendationContext): Promise<StrategyRecommendation[]> {
    const { tenantId, preferences } = context;

    const metrics = context.currentMetrics || await this.fetchCurrentMetrics(tenantId);
    const activeStrategies = context.activeStrategies || await this.fetchActiveStrategies(tenantId);

    const allStrategies = await StrategyService.getAllStrategies({ isActive: true });
    const availableStrategies = allStrategies.strategies.filter(
      s => !activeStrategies.includes(s.id)
    );

    const recommendations: StrategyRecommendation[] = [];

    for (const strategy of availableStrategies) {
      const recommendation = await this.evaluateStrategy(strategy, {
        ...context,
        currentMetrics: metrics,
        activeStrategies
      });

      if (recommendation) {
        recommendations.push(recommendation);
      }
    }

    recommendations.sort((a, b) => b.score - a.score);

    const filtered = this.applyPreferences(recommendations, preferences);

    logger.info(`[StrategyRecommendationService] Generated ${filtered.length} recommendations for tenant ${tenantId}`);
    return filtered.slice(0, 10);
  }

  async getPersonalizedRecommendations(tenantId: string): Promise<StrategyRecommendation[]> {
    const context = await this.buildRecommendationContext(tenantId);
    return this.getRecommendations(context);
  }

  async getCategoryRecommendations(
    category: string,
    tenantId: string
  ): Promise<StrategyRecommendation[]> {
    const context = await this.buildRecommendationContext(tenantId);
    const strategies = await StrategyService.getStrategiesByCategory(category);

    const recommendations: StrategyRecommendation[] = [];

    for (const strategy of strategies) {
      if (context.activeStrategies?.includes(strategy.id)) continue;

      const recommendation = await this.evaluateStrategy(strategy, context);
      if (recommendation) {
        recommendations.push(recommendation);
      }
    }

    recommendations.sort((a, b) => b.score - a.score);
    return recommendations;
  }

  async getSimilarStrategies(strategyId: string): Promise<Strategy[]> {
    const strategy = await StrategyService.getStrategyById(strategyId);
    if (!strategy) return [];

    const allStrategies = await StrategyService.getAllStrategies({ isActive: true });

    return allStrategies.strategies
      .filter(s => s.id !== strategyId)
      .filter(s => s.category === strategy.category || s.tags.some(t => strategy.tags.includes(t)))
      .sort((a, b) => {
        const aScore = this.calculateSimilarity(strategy, a);
        const bScore = this.calculateSimilarity(strategy, b);
        return bScore - aScore;
      })
      .slice(0, 5);
  }

  async getTrendingStrategies(limit: number = 5): Promise<Strategy[]> {
    const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);

    const trending = await db('cf_merchant_strategies as ms')
      .join('cf_strategies as s', 'ms.strategy_id', 's.id')
      .where('ms.activated_at', '>=', thirtyDaysAgo)
      .where('s.is_active', true)
      .groupBy('s.id')
      .select('s.*')
      .count('ms.id as activation_count')
      .orderBy('activation_count', 'desc')
      .limit(limit);

    return trending.map(row => ({
      id: String(row.id),
      name: String(row.name),
      description: String(row.description),
      category: String(row.category) as Strategy['category'],
      risk_level: String(row.risk_level) as Strategy['risk_level'],
      price: parseFloat(String(row.price)) || 0,
      billing_type: String(row.billing_type) as Strategy['billing_type'],
      parameters: typeof row.parameters === 'string' ? JSON.parse(row.parameters) : row.parameters,
      default_config: typeof row.default_config === 'string' ? JSON.parse(row.default_config) : row.default_config,
      avg_roi: parseFloat(String(row.avg_roi)) || 0,
      usage_count: Number(row.usage_count) || 0,
      success_rate: Number(row.success_rate) || 0,
      tags: typeof row.tags === 'string' ? JSON.parse(row.tags) : row.tags,
      is_active: Boolean(row.is_active),
      is_featured: Boolean(row.is_featured),
      created_by: String(row.created_by),
      created_at: new Date(row.created_at),
      updated_at: new Date(row.updated_at)
    }));
  }

  private async evaluateStrategy(
    strategy: Strategy,
    context: RecommendationContext
  ): Promise<StrategyRecommendation | null> {
    const score = this.calculateStrategyScore(strategy, context);
    const reasons = this.generateReasons(strategy, context);
    const expectedRoi = this.estimateRoi(strategy, context);
    const confidence = this.calculateConfidence(strategy, context);
    const priority = this.determinePriority(score, strategy.risk_level);

    return {
      strategy,
      score,
      reasons,
      expectedRoi,
      confidence,
      priority
    };
  }

  private calculateStrategyScore(
    strategy: Strategy,
    context: RecommendationContext
  ): number {
    let score = 0;

    score += strategy.avg_roi * 30;
    score += strategy.success_rate * 0.2;
    score += Math.min(strategy.usage_count / 100, 10);

    if (context.currentMetrics) {
      if (strategy.category === 'PRICING' && context.currentMetrics.roi < 0.5) {
        score += 20;
      }
      if (strategy.category === 'ADVERTISING' && context.currentMetrics.orderCount < 100) {
        score += 15;
      }
      if (strategy.category === 'PRODUCT_SELECTION' && context.currentMetrics.productCount < 50) {
        score += 15;
      }
    }

    if (context.preferences?.riskTolerance) {
      if (context.preferences.riskTolerance === 'LOW' && strategy.risk_level === 'LOW') {
        score += 10;
      } else if (context.preferences.riskTolerance === 'HIGH' && strategy.risk_level === 'HIGH') {
        score += 10;
      }
    }

    if (strategy.is_featured) {
      score += 5;
    }

    return Math.round(score * 100) / 100;
  }

  private generateReasons(strategy: Strategy, context: RecommendationContext): string[] {
    const reasons: string[] = [];

    if (strategy.avg_roi > 0.5) {
      reasons.push(`High average ROI of ${(strategy.avg_roi * 100).toFixed(1)}%`);
    }

    if (strategy.success_rate > 80) {
      reasons.push(`Proven success rate of ${strategy.success_rate}%`);
    }

    if (strategy.usage_count > 100) {
      reasons.push(`Used by ${strategy.usage_count}+ merchants`);
    }

    if (context.currentMetrics) {
      if (strategy.category === 'PRICING' && context.currentMetrics.roi < 0.5) {
        reasons.push('Recommended for improving your current ROI');
      }
      if (strategy.category === 'ADVERTISING' && context.currentMetrics.orderCount < 100) {
        reasons.push('Can help increase your order volume');
      }
    }

    if (strategy.price === 0) {
      reasons.push('Free to use');
    }

    return reasons.slice(0, 4);
  }

  private estimateRoi(strategy: Strategy, context: RecommendationContext): number {
    let estimatedRoi = strategy.avg_roi;

    if (context.currentMetrics) {
      const roiDiff = strategy.avg_roi - context.currentMetrics.roi;
      if (roiDiff > 0) {
        estimatedRoi = context.currentMetrics.roi + roiDiff * 0.7;
      }
    }

    return Math.round(estimatedRoi * 100) / 100;
  }

  private calculateConfidence(strategy: Strategy, context: RecommendationContext): number {
    let confidence = 0.5;

    if (strategy.usage_count > 50) confidence += 0.1;
    if (strategy.usage_count > 200) confidence += 0.1;
    if (strategy.success_rate > 70) confidence += 0.1;
    if (strategy.success_rate > 90) confidence += 0.1;

    return Math.min(confidence, 0.95);
  }

  private determinePriority(score: number, riskLevel: string): 'HIGH' | 'MEDIUM' | 'LOW' {
    if (score > 50 && riskLevel === 'LOW') return 'HIGH';
    if (score > 30) return 'MEDIUM';
    return 'LOW';
  }

  private applyPreferences(
    recommendations: StrategyRecommendation[],
    preferences?: RecommendationContext['preferences']
  ): StrategyRecommendation[] {
    if (!preferences) return recommendations;

    let filtered = recommendations;

    if (preferences.budget !== undefined) {
      filtered = filtered.filter(r => r.strategy.price <= preferences.budget);
    }

    if (preferences.riskTolerance === 'LOW') {
      filtered = filtered.sort((a, b) => {
        const riskOrder = { LOW: 0, MEDIUM: 1, HIGH: 2 };
        return riskOrder[a.strategy.risk_level] - riskOrder[b.strategy.risk_level];
      });
    }

    if (preferences.categories && preferences.categories.length > 0) {
      filtered = filtered.filter(r => preferences.categories!.includes(r.strategy.category));
    }

    return filtered;
  }

  private async buildRecommendationContext(tenantId: string): Promise<RecommendationContext> {
    const metrics = await this.fetchCurrentMetrics(tenantId);
    const activeStrategies = await this.fetchActiveStrategies(tenantId);

    return {
      tenantId,
      currentMetrics: metrics,
      activeStrategies
    };
  }

  private async fetchCurrentMetrics(tenantId: string): Promise<RecommendationContext['currentMetrics']> {
    try {
      const latestMetrics = await MerchantMetricsService.getLatestMetrics(tenantId);

      if (latestMetrics) {
        return {
          revenue: parseFloat(latestMetrics.total_revenue as any) || 0,
          roi: parseFloat(latestMetrics.roi as any) || 0,
          orderCount: latestMetrics.order_count || 0,
          productCount: latestMetrics.product_count || 0
        };
      }
    } catch (error) {
      logger.warn('[StrategyRecommendationService] Could not fetch metrics:', error);
    }

    return {
      revenue: 0,
      roi: 0,
      orderCount: 0,
      productCount: 0
    };
  }

  private async fetchActiveStrategies(tenantId: string): Promise<string[]> {
    const active = await db('cf_merchant_strategies')
      .where('tenant_id', tenantId)
      .where('status', 'ACTIVE')
      .select('strategy_id');

    return active.map(s => s.strategy_id);
  }

  private calculateSimilarity(strategy1: Strategy, strategy2: Strategy): number {
    let score = 0;

    if (strategy1.category === strategy2.category) score += 0.5;

    const commonTags = strategy1.tags.filter(t => strategy2.tags.includes(t));
    score += commonTags.length * 0.1;

    const roiDiff = Math.abs(strategy1.avg_roi - strategy2.avg_roi);
    score -= roiDiff * 0.1;

    return Math.max(0, score);
  }
}

export default StrategyRecommendationService.getInstance();
