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

interface ArbitrageOpportunity {
  opportunityId: string;
  tenantId: string;
  productId: string;
  productTitle: string;
  sourcePlatform: string;
  targetPlatform: string;
  sourcePrice: number;
  targetPrice: number;
  priceDifference: number;
  profitMargin: number;
  netProfit: number;
  costs: {
    platformFees: number;
    shippingCost: number;
    taxes: number;
    paymentFees: number;
    otherCosts: number;
  };
  riskLevel: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
  status: 'NEW' | 'ANALYZING' | 'APPROVED' | 'EXECUTING' | 'COMPLETED' | 'REJECTED' | 'EXPIRED';
  validUntil: Date;
  createdAt: Date;
}

interface PriceComparison {
  productId: string;
  prices: Array<{
    platform: string;
    price: number;
    currency: string;
    available: boolean;
    lastUpdated: Date;
  }>;
  bestSource: {
    platform: string;
    price: number;
  };
  bestTarget: {
    platform: string;
    price: number;
  };
}

interface ArbitrageAnalysis {
  opportunity: ArbitrageOpportunity;
  analysis: {
    demandScore: number;
    competitionScore: number;
    riskFactors: string[];
    recommendations: string[];
  };
}

interface ProfitCalculation {
  grossProfit: number;
  netProfit: number;
  profitMargin: number;
  roi: number;
  breakdown: {
    revenue: number;
    costOfGoods: number;
    platformFees: number;
    shippingCost: number;
    taxes: number;
    paymentFees: number;
    otherCosts: number;
  };
}

export class ArbitrageDetectionService {
  private static PROFIT_THRESHOLD_TOC = 0.20;
  private static PROFIT_THRESHOLD_TOB = 0.15;
  private static MAX_RISK_LEVEL: ArbitrageOpportunity['riskLevel'] = 'HIGH';

  private static PLATFORM_FEES: Record<string, number> = {
    'Shopify': 0.029,
    'TIKTOK': 0.05,
    'Shopee': 0.045,
    'Temu': 0.05,
    'Amazon': 0.15,
    'eBay': 0.12
  };

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

  static async detectOpportunities(params: {
    tenantId: string;
    sourcePlatforms?: string[];
    targetPlatforms?: string[];
    minProfitMargin?: number;
    maxPrice?: number;
    category?: string;
    limit?: number;
  }): Promise<ArbitrageOpportunity[]> {
    const {
      tenantId,
      sourcePlatforms = ['1688', 'AliExpress'],
      targetPlatforms = ['Shopify', 'TIKTOK', 'Shopee'],
      minProfitMargin = 0.15,
      maxPrice = 1000,
      category,
      limit = 50
    } = params;

    logger.info('[ArbitrageDetectionService] Detecting arbitrage opportunities', {
      tenantId,
      sourcePlatforms,
      targetPlatforms,
      minProfitMargin
    });

    const opportunities: ArbitrageOpportunity[] = [];

    const products = await this.getProductsForAnalysis(tenantId, category, limit);

    for (const product of products) {
      const priceComparison = await this.comparePrices(product.id, sourcePlatforms, targetPlatforms);

      if (!priceComparison.bestSource || !priceComparison.bestTarget) {
        continue;
      }

      const profitCalc = await this.calculateProfit({
        sourcePrice: priceComparison.bestSource.price,
        targetPrice: priceComparison.bestTarget.price,
        sourcePlatform: priceComparison.bestSource.platform,
        targetPlatform: priceComparison.bestTarget.platform
      });

      if (profitCalc.profitMargin >= minProfitMargin) {
        const opportunity = await this.createOpportunity({
          tenantId,
          productId: product.id,
          productTitle: product.title,
          sourcePlatform: priceComparison.bestSource.platform,
          targetPlatform: priceComparison.bestTarget.platform,
          sourcePrice: priceComparison.bestSource.price,
          targetPrice: priceComparison.bestTarget.price,
          profitCalc
        });

        opportunities.push(opportunity);
      }
    }

    return opportunities.sort((a, b) => b.profitMargin - a.profitMargin);
  }

  static async analyzeOpportunity(params: {
    opportunityId: string;
    tenantId: string;
  }): Promise<ArbitrageAnalysis> {
    const { opportunityId, tenantId } = params;

    logger.info('[ArbitrageDetectionService] Analyzing opportunity', {
      opportunityId,
      tenantId
    });

    const opportunity = await this.getOpportunity(opportunityId, tenantId);

    if (!opportunity) {
      throw new Error('Opportunity not found');
    }

    const demandScore = await this.calculateDemandScore(opportunity.productId);
    const competitionScore = await this.calculateCompetitionScore(opportunity.productId);
    const riskFactors = this.identifyRiskFactors(opportunity);
    const recommendations = this.generateRecommendations(opportunity, demandScore, competitionScore);

    return {
      opportunity,
      analysis: {
        demandScore,
        competitionScore,
        riskFactors,
        recommendations
      }
    };
  }

  static async calculateProfit(params: {
    sourcePrice: number;
    targetPrice: number;
    sourcePlatform: string;
    targetPlatform: string;
    shippingCost?: number;
    taxRate?: number;
  }): Promise<ProfitCalculation> {
    const {
      sourcePrice,
      targetPrice,
      sourcePlatform,
      targetPlatform,
      shippingCost = 5,
      taxRate = 0.08
    } = params;

    const platformFeeRate = this.PLATFORM_FEES[targetPlatform] || 0.05;
    const paymentFeeRate = 0.029 + 0.30;

    const revenue = targetPrice;
    const costOfGoods = sourcePrice;
    const platformFees = targetPrice * platformFeeRate;
    const paymentFees = targetPrice * paymentFeeRate;
    const taxes = targetPrice * taxRate;

    const totalCosts = costOfGoods + platformFees + shippingCost + taxes + paymentFees;
    const grossProfit = revenue - costOfGoods;
    const netProfit = revenue - totalCosts;
    const profitMargin = netProfit / revenue;
    const roi = netProfit / costOfGoods;

    return {
      grossProfit: Math.round(grossProfit * 100) / 100,
      netProfit: Math.round(netProfit * 100) / 100,
      profitMargin: Math.round(profitMargin * 10000) / 100,
      roi: Math.round(roi * 10000) / 100,
      breakdown: {
        revenue,
        costOfGoods,
        platformFees: Math.round(platformFees * 100) / 100,
        shippingCost,
        taxes: Math.round(taxes * 100) / 100,
        paymentFees: Math.round(paymentFees * 100) / 100,
        otherCosts: 0
      }
    };
  }

  static async comparePrices(
    productId: string,
    sourcePlatforms: string[],
    targetPlatforms: string[]
  ): Promise<PriceComparison> {
    const listings = await db('cf_listing')
      .where({ product_id: productId })
      .whereIn('platform', [...sourcePlatforms, ...targetPlatforms]);

    const prices = listings.map(l => ({
      platform: l.platform,
      price: parseFloat(l.price) || 0,
      currency: l.currency || 'USD',
      available: l.status === 'ACTIVE',
      lastUpdated: l.updated_at
    }));

    const sourcePrices = prices.filter(p => sourcePlatforms.includes(p.platform) && p.available);
    const targetPrices = prices.filter(p => targetPlatforms.includes(p.platform) && p.available);

    const bestSource = sourcePrices.length > 0
      ? sourcePrices.reduce((min, p) => p.price < min.price ? p : min)
      : null;

    const bestTarget = targetPrices.length > 0
      ? targetPrices.reduce((max, p) => p.price > max.price ? p : max)
      : null;

    return {
      productId,
      prices,
      bestSource: bestSource ? { platform: bestSource.platform, price: bestSource.price } : null as any,
      bestTarget: bestTarget ? { platform: bestTarget.platform, price: bestTarget.price } : null as any
    };
  }

  static async getActiveOpportunities(params: {
    tenantId: string;
    status?: ArbitrageOpportunity['status'];
    minProfitMargin?: number;
    limit?: number;
  }): Promise<ArbitrageOpportunity[]> {
    const { tenantId, status, minProfitMargin, limit = 50 } = params;

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

    if (status) {
      query = query.where({ status });
    }

    if (minProfitMargin) {
      query = query.where('profit_margin', '>=', minProfitMargin);
    }

    const results = await query;

    return results.map(this.mapToOpportunity);
  }

  static async updateOpportunityStatus(params: {
    opportunityId: string;
    tenantId: string;
    status: ArbitrageOpportunity['status'];
    reason?: string;
  }): Promise<ArbitrageOpportunity> {
    const { opportunityId, tenantId, status, reason } = params;

    logger.info('[ArbitrageDetectionService] Updating opportunity status', {
      opportunityId,
      status,
      reason
    });

    await db('cf_arbitrage_opportunity')
      .where({ id: opportunityId, tenant_id: tenantId })
      .update({
        status,
        updated_at: new Date()
      });

    const opportunity = await this.getOpportunity(opportunityId, tenantId);

    if (!opportunity) {
      throw new Error('Opportunity not found after update');
    }

    return opportunity;
  }

  static async validateOpportunity(params: {
    opportunityId: string;
    tenantId: string;
  }): Promise<{
    valid: boolean;
    issues: string[];
    currentPrices: {
      source: number;
      target: number;
    };
  }> {
    const { opportunityId, tenantId } = params;

    const opportunity = await this.getOpportunity(opportunityId, tenantId);

    if (!opportunity) {
      throw new Error('Opportunity not found');
    }

    const issues: string[] = [];

    const currentComparison = await this.comparePrices(
      opportunity.productId,
      [opportunity.sourcePlatform],
      [opportunity.targetPlatform]
    );

    const currentSourcePrice = currentComparison.bestSource?.price || opportunity.sourcePrice;
    const currentTargetPrice = currentComparison.bestTarget?.price || opportunity.targetPrice;

    if (currentSourcePrice > opportunity.sourcePrice * 1.1) {
      issues.push(`Source price increased by ${Math.round((currentSourcePrice / opportunity.sourcePrice - 1) * 100)}%`);
    }

    if (currentTargetPrice < opportunity.targetPrice * 0.9) {
      issues.push(`Target price decreased by ${Math.round((1 - currentTargetPrice / opportunity.targetPrice) * 100)}%`);
    }

    const currentProfit = await this.calculateProfit({
      sourcePrice: currentSourcePrice,
      targetPrice: currentTargetPrice,
      sourcePlatform: opportunity.sourcePlatform,
      targetPlatform: opportunity.targetPlatform
    });

    if (currentProfit.profitMargin < this.PROFIT_THRESHOLD_TOC) {
      issues.push(`Profit margin (${currentProfit.profitMargin}%) below threshold`);
    }

    if (opportunity.validUntil < new Date()) {
      issues.push('Opportunity has expired');
    }

    return {
      valid: issues.length === 0,
      issues,
      currentPrices: {
        source: currentSourcePrice,
        target: currentTargetPrice
      }
    };
  }

  private static async getProductsForAnalysis(
    tenantId: string,
    category?: string,
    limit: number = 100
  ): Promise<Array<{ id: string; title: string }>> {
    let query = db('cf_product')
      .where({ tenant_id: tenantId })
      .limit(limit);

    if (category) {
      query = query.where({ category });
    }

    return query.select('id', 'title');
  }

  private static async createOpportunity(params: {
    tenantId: string;
    productId: string;
    productTitle: string;
    sourcePlatform: string;
    targetPlatform: string;
    sourcePrice: number;
    targetPrice: number;
    profitCalc: ProfitCalculation;
  }): Promise<ArbitrageOpportunity> {
    const {
      tenantId,
      productId,
      productTitle,
      sourcePlatform,
      targetPlatform,
      sourcePrice,
      targetPrice,
      profitCalc
    } = params;

    const opportunityId = this.generateId('arb');
    const riskLevel = this.determineRiskLevel(profitCalc.profitMargin);
    const validUntil = new Date(Date.now() + 24 * 60 * 60 * 1000);

    const opportunity: ArbitrageOpportunity = {
      opportunityId,
      tenantId,
      productId,
      productTitle,
      sourcePlatform,
      targetPlatform,
      sourcePrice,
      targetPrice,
      priceDifference: targetPrice - sourcePrice,
      profitMargin: profitCalc.profitMargin,
      netProfit: profitCalc.netProfit,
      costs: profitCalc.breakdown,
      riskLevel,
      status: 'NEW',
      validUntil,
      createdAt: new Date()
    };

    await this.saveOpportunity(opportunity);

    return opportunity;
  }

  private static determineRiskLevel(profitMargin: number): ArbitrageOpportunity['riskLevel'] {
    if (profitMargin >= 40) return 'LOW';
    if (profitMargin >= 25) return 'MEDIUM';
    if (profitMargin >= 15) return 'HIGH';
    return 'CRITICAL';
  }

  private static async calculateDemandScore(productId: string): Promise<number> {
    const listings = await db('cf_listing')
      .where({ product_id: productId })
      .where('sales_count', '>', 0);

    if (listings.length === 0) return 50;

    const totalSales = listings.reduce((sum, l) => sum + (l.sales_count || 0), 0);
    const avgSales = totalSales / listings.length;

    if (avgSales > 100) return 90;
    if (avgSales > 50) return 75;
    if (avgSales > 20) return 60;
    if (avgSales > 10) return 45;
    return 30;
  }

  private static async calculateCompetitionScore(productId: string): Promise<number> {
    const listings = await db('cf_listing')
      .where({ product_id: productId });

    const competitorCount = listings.length;

    if (competitorCount < 5) return 90;
    if (competitorCount < 15) return 70;
    if (competitorCount < 30) return 50;
    if (competitorCount < 50) return 30;
    return 15;
  }

  private static identifyRiskFactors(opportunity: ArbitrageOpportunity): string[] {
    const risks: string[] = [];

    if (opportunity.profitMargin < 20) {
      risks.push('Low profit margin - high sensitivity to price changes');
    }

    if (opportunity.sourcePlatform === '1688') {
      risks.push('Source from 1688 - requires No-API Bridge');
      risks.push('Quality control needed for B2B sourcing');
    }

    if (opportunity.priceDifference > opportunity.sourcePrice * 2) {
      risks.push('Large price gap - verify product authenticity');
    }

    if (opportunity.costs.shippingCost > opportunity.netProfit * 0.3) {
      risks.push('Shipping cost represents significant portion of profit');
    }

    return risks;
  }

  private static generateRecommendations(
    opportunity: ArbitrageOpportunity,
    demandScore: number,
    competitionScore: number
  ): string[] {
    const recommendations: string[] = [];

    if (demandScore >= 70) {
      recommendations.push('High demand product - prioritize this opportunity');
    }

    if (competitionScore >= 70) {
      recommendations.push('Low competition - good entry opportunity');
    }

    if (opportunity.riskLevel === 'LOW') {
      recommendations.push('Low risk - suitable for automated execution');
    } else if (opportunity.riskLevel === 'HIGH' || opportunity.riskLevel === 'CRITICAL') {
      recommendations.push('High risk - requires manual review before execution');
    }

    if (opportunity.sourcePlatform === '1688') {
      recommendations.push('Order sample before bulk purchase');
      recommendations.push('Verify supplier credentials and ratings');
    }

    return recommendations;
  }

  private static async getOpportunity(
    opportunityId: string,
    tenantId: string
  ): Promise<ArbitrageOpportunity | null> {
    const result = await db('cf_arbitrage_opportunity')
      .where({ id: opportunityId, tenant_id: tenantId })
      .first();

    if (!result) return null;

    return this.mapToOpportunity(result);
  }

  private static async saveOpportunity(opportunity: ArbitrageOpportunity): Promise<void> {
    try {
      await db('cf_arbitrage_opportunity').insert({
        id: opportunity.opportunityId,
        tenant_id: opportunity.tenantId,
        product_id: opportunity.productId,
        product_title: opportunity.productTitle,
        source_platform: opportunity.sourcePlatform,
        target_platform: opportunity.targetPlatform,
        source_price: opportunity.sourcePrice,
        target_price: opportunity.targetPrice,
        price_difference: opportunity.priceDifference,
        profit_margin: opportunity.profitMargin,
        net_profit: opportunity.netProfit,
        costs: JSON.stringify(opportunity.costs),
        risk_level: opportunity.riskLevel,
        status: opportunity.status,
        valid_until: opportunity.validUntil,
        created_at: opportunity.createdAt,
        updated_at: new Date()
      });
    } catch (error: any) {
      logger.error('[ArbitrageDetectionService] Failed to save opportunity', {
        opportunityId: opportunity.opportunityId,
        error: error.message
      });
    }
  }

  private static mapToOpportunity(result: any): ArbitrageOpportunity {
    return {
      opportunityId: result.id,
      tenantId: result.tenant_id,
      productId: result.product_id,
      productTitle: result.product_title,
      sourcePlatform: result.source_platform,
      targetPlatform: result.target_platform,
      sourcePrice: parseFloat(result.source_price),
      targetPrice: parseFloat(result.target_price),
      priceDifference: parseFloat(result.price_difference),
      profitMargin: parseFloat(result.profit_margin),
      netProfit: parseFloat(result.net_profit),
      costs: JSON.parse(result.costs || '{}'),
      riskLevel: result.risk_level,
      status: result.status,
      validUntil: result.valid_until,
      createdAt: result.created_at
    };
  }
}

export default ArbitrageDetectionService;
