import { logger } from '../utils/logger';
import { B2BCustomer } from '../models/B2B';

export interface CustomerProfile {
  customerId: string;
  companyName: string;
  tierLevel: 'BRONZE' | 'SILVER' | 'GOLD' | 'PLATINUM';
  totalOrders: number;
  totalSpent: number;
  averageOrderValue: number;
  orderFrequency: number;
  preferredCategories: string[];
  paymentReliability: number;
  growthPotential: 'LOW' | 'MEDIUM' | 'HIGH';
  riskLevel: 'LOW' | 'MEDIUM' | 'HIGH';
  lastOrderDate: Date;
  customerSince: Date;
  traceId: string;
}

export interface DiscountRecommendation {
  customerId: string;
  productId: string;
  recommendedDiscount: number;
  maxDiscount: number;
  reason: string;
  expectedMargin: number;
  confidence: number;
  traceId: string;
}

export interface CreditRiskAssessment {
  customerId: string;
  riskScore: number;
  riskLevel: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
  factors: RiskFactor[];
  recommendedCreditLimit: number;
  currentCreditLimit: number;
  creditUtilization: number;
  paymentHistory: PaymentHistorySummary;
  traceId: string;
}

export interface RiskFactor {
  factor: string;
  impact: 'POSITIVE' | 'NEGATIVE' | 'NEUTRAL';
  weight: number;
  description: string;
}

export interface PaymentHistorySummary {
  onTimePayments: number;
  latePayments: number;
  defaultedPayments: number;
  averagePaymentDays: number;
  totalTransactions: number;
}

export interface TransactionData {
  orderId: string;
  amount: number;
  currency: string;
  status: string;
  createdAt: Date;
  paidAt?: Date;
}

export class B2BAIService {
  private static readonly TIER_THRESHOLDS = {
    BRONZE: { minSpent: 0, minOrders: 0 },
    SILVER: { minSpent: 10000, minOrders: 5 },
    GOLD: { minSpent: 50000, minOrders: 20 },
    PLATINUM: { minSpent: 200000, minOrders: 50 },
  };

  private static readonly PROFIT_MARGIN_THRESHOLD = 0.15;

  static async analyzeCustomerProfile(
    customerId: string,
    transactionData: TransactionData[],
    options: {
      tenantId: string;
      traceId: string;
      businessType: 'TOC' | 'TOB';
    }
  ): Promise<CustomerProfile> {
    const { tenantId, traceId, businessType } = options;

    logger.info('[B2BAIService] Analyzing customer profile', {
      customerId,
      tenantId,
      traceId,
      businessType,
      transactionCount: transactionData.length,
    });

    try {
      const customer = await this.getCustomerData(customerId, tenantId, traceId);

      const totalOrders = transactionData.length;
      const totalSpent = transactionData.reduce((sum, t) => sum + t.amount, 0);
      const averageOrderValue = totalOrders > 0 ? totalSpent / totalOrders : 0;

      const orderDates = transactionData.map(t => t.createdAt).sort((a, b) => a.getTime() - b.getTime());
      const orderFrequency = this.calculateOrderFrequency(orderDates);

      const preferredCategories = await this.analyzePreferredCategories(customerId, tenantId, traceId);

      const paymentReliability = this.calculatePaymentReliability(transactionData);

      const growthPotential = this.assessGrowthPotential(totalSpent, orderFrequency, paymentReliability);

      const riskLevel = this.assessRiskLevel(paymentReliability, transactionData);

      const lastOrderDate = orderDates.length > 0 ? orderDates[orderDates.length - 1] : new Date();

      const profile: CustomerProfile = {
        customerId,
        companyName: customer.company_name,
        tierLevel: customer.tier_level,
        totalOrders,
        totalSpent: Number(totalSpent.toFixed(2)),
        averageOrderValue: Number(averageOrderValue.toFixed(2)),
        orderFrequency,
        preferredCategories,
        paymentReliability: Number(paymentReliability.toFixed(2)),
        growthPotential,
        riskLevel,
        lastOrderDate,
        customerSince: customer.created_at || new Date(),
        traceId,
      };

      logger.info('[B2BAIService] Customer profile analyzed', {
        customerId,
        tenantId,
        traceId,
        tierLevel: profile.tierLevel,
        totalSpent: profile.totalSpent,
        riskLevel: profile.riskLevel,
      });

      return profile;
    } catch (error: any) {
      logger.error('[B2BAIService] Customer profile analysis failed', {
        customerId,
        tenantId,
        traceId,
        error: error.message,
      });
      throw error;
    }
  }

  static async generateDiscountRecommendation(
    customerId: string,
    productId: string,
    quantity: number,
    options: {
      tenantId: string;
      traceId: string;
      businessType: 'TOC' | 'TOB';
    }
  ): Promise<DiscountRecommendation> {
    const { tenantId, traceId, businessType } = options;

    logger.info('[B2BAIService] Generating discount recommendation', {
      customerId,
      productId,
      quantity,
      tenantId,
      traceId,
    });

    try {
      const customer = await this.getCustomerData(customerId, tenantId, traceId);
      const profile = await this.analyzeCustomerProfile(customerId, [], { tenantId, traceId, businessType });

      let baseDiscount = 0;
      let reason = '';

      switch (customer.tier_level) {
        case 'PLATINUM':
          baseDiscount = 15;
          reason = 'Platinum tier customer - maximum loyalty discount';
          break;
        case 'GOLD':
          baseDiscount = 10;
          reason = 'Gold tier customer - premium loyalty discount';
          break;
        case 'SILVER':
          baseDiscount = 5;
          reason = 'Silver tier customer - standard loyalty discount';
          break;
        default:
          baseDiscount = 0;
          reason = 'Bronze tier customer - base pricing';
      }

      if (quantity >= 500) {
        baseDiscount = Math.min(baseDiscount + 10, 25);
        reason += ' + bulk order discount (500+ units)';
      } else if (quantity >= 100) {
        baseDiscount = Math.min(baseDiscount + 7, 22);
        reason += ' + bulk order discount (100+ units)';
      } else if (quantity >= 50) {
        baseDiscount = Math.min(baseDiscount + 5, 20);
        reason += ' + bulk order discount (50+ units)';
      }

      if (profile.paymentReliability > 0.95) {
        baseDiscount = Math.min(baseDiscount + 2, 25);
        reason += ' + excellent payment history bonus';
      }

      const maxDiscount = 25;
      const recommendedDiscount = Math.min(baseDiscount, maxDiscount);

      const basePrice = await this.getProductBasePrice(productId, tenantId, traceId);
      const discountMultiplier = 1 - (recommendedDiscount / 100);
      const discountedPrice = basePrice * discountMultiplier;
      const costPrice = basePrice * 0.7;
      const expectedMargin = (discountedPrice - costPrice) / discountedPrice;

      if (expectedMargin < this.PROFIT_MARGIN_THRESHOLD) {
        const maxAllowedDiscount = (1 - (costPrice / (basePrice * (1 - this.PROFIT_MARGIN_THRESHOLD)))) * 100;
        const adjustedDiscount = Math.max(0, Math.min(recommendedDiscount, maxAllowedDiscount));
        
        logger.warn('[B2BAIService] Discount adjusted to maintain profit margin', {
          customerId,
          productId,
          originalDiscount: recommendedDiscount,
          adjustedDiscount,
          expectedMargin,
          threshold: this.PROFIT_MARGIN_THRESHOLD,
        });

        return {
          customerId,
          productId,
          recommendedDiscount: Number(adjustedDiscount.toFixed(2)),
          maxDiscount: Number(maxAllowedDiscount.toFixed(2)),
          reason: reason + ' (adjusted for profit margin)',
          expectedMargin: Number(expectedMargin.toFixed(4)),
          confidence: 0.85,
          traceId,
        };
      }

      const recommendation: DiscountRecommendation = {
        customerId,
        productId,
        recommendedDiscount: Number(recommendedDiscount.toFixed(2)),
        maxDiscount,
        reason,
        expectedMargin: Number(expectedMargin.toFixed(4)),
        confidence: 0.9,
        traceId,
      };

      logger.info('[B2BAIService] Discount recommendation generated', {
        customerId,
        productId,
        recommendedDiscount,
        expectedMargin,
        traceId,
      });

      return recommendation;
    } catch (error: any) {
      logger.error('[B2BAIService] Discount recommendation failed', {
        customerId,
        productId,
        tenantId,
        traceId,
        error: error.message,
      });
      throw error;
    }
  }

  static async assessCreditRisk(
    customerId: string,
    transactionData: TransactionData[],
    options: {
      tenantId: string;
      traceId: string;
      businessType: 'TOC' | 'TOB';
    }
  ): Promise<CreditRiskAssessment> {
    const { tenantId, traceId, businessType } = options;

    logger.info('[B2BAIService] Assessing credit risk', {
      customerId,
      tenantId,
      traceId,
      transactionCount: transactionData.length,
    });

    try {
      const customer = await this.getCustomerData(customerId, tenantId, traceId);
      const factors: RiskFactor[] = [];

      const paymentHistory = this.calculatePaymentHistorySummary(transactionData);

      const paymentHistoryScore = this.calculatePaymentHistoryScore(paymentHistory);
      factors.push({
        factor: 'Payment History',
        impact: paymentHistoryScore > 0.8 ? 'POSITIVE' : paymentHistoryScore > 0.5 ? 'NEUTRAL' : 'NEGATIVE',
        weight: 0.35,
        description: `On-time payment rate: ${(paymentHistoryScore * 100).toFixed(1)}%`,
      });

      const utilizationRate = customer.credit_used / customer.credit_limit;
      const utilizationScore = 1 - utilizationRate;
      factors.push({
        factor: 'Credit Utilization',
        impact: utilizationRate < 0.5 ? 'POSITIVE' : utilizationRate < 0.8 ? 'NEUTRAL' : 'NEGATIVE',
        weight: 0.25,
        description: `Current utilization: ${(utilizationRate * 100).toFixed(1)}%`,
      });

      const tierScore = this.getTierScore(customer.tier_level);
      factors.push({
        factor: 'Customer Tier',
        impact: tierScore > 0.7 ? 'POSITIVE' : tierScore > 0.4 ? 'NEUTRAL' : 'NEGATIVE',
        weight: 0.20,
        description: `${customer.tier_level} tier customer`,
      });

      const transactionVolume = transactionData.length;
      const volumeScore = Math.min(transactionVolume / 50, 1);
      factors.push({
        factor: 'Transaction Volume',
        impact: volumeScore > 0.6 ? 'POSITIVE' : volumeScore > 0.3 ? 'NEUTRAL' : 'NEGATIVE',
        weight: 0.20,
        description: `${transactionVolume} transactions recorded`,
      });

      let riskScore = 0;
      for (const factor of factors) {
        const factorValue = factor.impact === 'POSITIVE' ? 1 : factor.impact === 'NEUTRAL' ? 0.5 : 0;
        riskScore += factorValue * factor.weight;
      }
      riskScore = Number((riskScore * 100).toFixed(2));

      let riskLevel: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
      if (riskScore >= 75) {
        riskLevel = 'LOW';
      } else if (riskScore >= 50) {
        riskLevel = 'MEDIUM';
      } else if (riskScore >= 25) {
        riskLevel = 'HIGH';
      } else {
        riskLevel = 'CRITICAL';
      }

      const recommendedCreditLimit = this.calculateRecommendedCreditLimit(
        customer.credit_limit,
        riskScore,
        customer.tier_level
      );

      const assessment: CreditRiskAssessment = {
        customerId,
        riskScore,
        riskLevel,
        factors,
        recommendedCreditLimit,
        currentCreditLimit: customer.credit_limit,
        creditUtilization: Number(utilizationRate.toFixed(4)),
        paymentHistory,
        traceId,
      };

      logger.info('[B2BAIService] Credit risk assessment completed', {
        customerId,
        tenantId,
        traceId,
        riskScore,
        riskLevel,
        recommendedCreditLimit,
      });

      return assessment;
    } catch (error: any) {
      logger.error('[B2BAIService] Credit risk assessment failed', {
        customerId,
        tenantId,
        traceId,
        error: error.message,
      });
      throw error;
    }
  }

  private static async getCustomerData(customerId: string, tenantId: string, traceId: string): Promise<B2BCustomer> {
    logger.info('[B2BAIService] Fetching customer data', {
      customerId,
      tenantId,
      traceId,
    });

    return {
      id: customerId,
      tenant_id: tenantId,
      company_name: 'Sample B2B Customer',
      credit_limit: 50000,
      credit_used: 15000,
      payment_terms: 30,
      tier_level: 'SILVER',
      status: 'ACTIVE',
      trace_id: traceId,
      created_at: new Date('2024-01-01'),
    };
  }

  private static async getProductBasePrice(productId: string, tenantId: string, traceId: string): Promise<number> {
    return 100;
  }

  private static calculateOrderFrequency(orderDates: Date[]): number {
    if (orderDates.length < 2) return 0;

    const firstDate = orderDates[0];
    const lastDate = orderDates[orderDates.length - 1];
    const daysDiff = (lastDate.getTime() - firstDate.getTime()) / (1000 * 60 * 60 * 24);

    return daysDiff > 0 ? Number((orderDates.length / (daysDiff / 30)).toFixed(2)) : 0;
  }

  private static calculatePaymentReliability(transactions: TransactionData[]): number {
    if (transactions.length === 0) return 1;

    const paidTransactions = transactions.filter(t => t.status === 'PAID' && t.paidAt);
    if (paidTransactions.length === 0) return 0.5;

    let onTimeCount = 0;
    for (const t of paidTransactions) {
      const expectedDays = 30;
      const actualDays = (t.paidAt!.getTime() - t.createdAt.getTime()) / (1000 * 60 * 60 * 24);
      if (actualDays <= expectedDays) onTimeCount++;
    }

    return onTimeCount / paidTransactions.length;
  }

  private static async analyzePreferredCategories(customerId: string, tenantId: string, traceId: string): Promise<string[]> {
    return ['Electronics', 'Office Supplies'];
  }

  private static assessGrowthPotential(totalSpent: number, orderFrequency: number, paymentReliability: number): 'LOW' | 'MEDIUM' | 'HIGH' {
    const score = (totalSpent / 100000) * 0.4 + (orderFrequency / 10) * 0.3 + paymentReliability * 0.3;

    if (score > 0.6) return 'HIGH';
    if (score > 0.3) return 'MEDIUM';
    return 'LOW';
  }

  private static assessRiskLevel(paymentReliability: number, transactions: TransactionData[]): 'LOW' | 'MEDIUM' | 'HIGH' {
    if (paymentReliability > 0.9 && transactions.length > 10) return 'LOW';
    if (paymentReliability > 0.7) return 'MEDIUM';
    return 'HIGH';
  }

  private static calculatePaymentHistorySummary(transactions: TransactionData[]): PaymentHistorySummary {
    const paidTransactions = transactions.filter(t => t.status === 'PAID' && t.paidAt);
    const lateTransactions = transactions.filter(t => t.status === 'LATE');
    const defaultedTransactions = transactions.filter(t => t.status === 'DEFAULTED');

    let totalPaymentDays = 0;
    for (const t of paidTransactions) {
      totalPaymentDays += (t.paidAt!.getTime() - t.createdAt.getTime()) / (1000 * 60 * 60 * 24);
    }

    return {
      onTimePayments: paidTransactions.length - lateTransactions.length,
      latePayments: lateTransactions.length,
      defaultedPayments: defaultedTransactions.length,
      averagePaymentDays: paidTransactions.length > 0 ? Number((totalPaymentDays / paidTransactions.length).toFixed(1)) : 0,
      totalTransactions: transactions.length,
    };
  }

  private static calculatePaymentHistoryScore(history: PaymentHistorySummary): number {
    if (history.totalTransactions === 0) return 0.5;

    const onTimeRate = history.onTimePayments / history.totalTransactions;
    const lateRate = history.latePayments / history.totalTransactions;
    const defaultRate = history.defaultedPayments / history.totalTransactions;

    return onTimeRate * 1 - lateRate * 0.3 - defaultRate * 0.5;
  }

  private static getTierScore(tier: string): number {
    const scores: Record<string, number> = {
      'PLATINUM': 1.0,
      'GOLD': 0.75,
      'SILVER': 0.5,
      'BRONZE': 0.25,
    };
    return scores[tier] || 0.25;
  }

  private static calculateRecommendedCreditLimit(currentLimit: number, riskScore: number, tier: string): number {
    const tierMultiplier: Record<string, number> = {
      'PLATINUM': 2.0,
      'GOLD': 1.5,
      'SILVER': 1.0,
      'BRONZE': 0.5,
    };

    const riskMultiplier = riskScore / 100;
    const tierMult = tierMultiplier[tier] || 0.5;

    return Number((currentLimit * riskMultiplier * tierMult).toFixed(2));
  }
}
