/**
 * A/B Testing Service for Email Campaigns
 * Manages split testing of email templates and subject lines
 */

export interface ABTestVariant {
  id: string;
  name: string;
  subject: string;
  htmlContent: string;
  percentage: number; // 0-100
}

export interface ABTestConfig {
  campaignId: number;
  testName: string;
  variants: ABTestVariant[];
  winnerCriteria: 'open_rate' | 'click_rate' | 'conversion_rate';
  minSampleSize: number; // Minimum recipients before determining winner
  testDuration: number; // milliseconds
  autoSelectWinner: boolean;
  createdAt: Date;
  startedAt?: Date;
  endedAt?: Date;
  winnerId?: string;
}

export interface ABTestMetrics {
  variantId: string;
  sent: number;
  delivered: number;
  opened: number;
  clicked: number;
  bounced: number;
  complained: number;
  openRate: number;
  clickRate: number;
  bounceRate: number;
}

/**
 * Create A/B test configuration
 */
export function createABTest(config: {
  campaignId: number;
  testName: string;
  variants: ABTestVariant[];
  winnerCriteria?: 'open_rate' | 'click_rate' | 'conversion_rate';
  minSampleSize?: number;
  testDuration?: number;
  autoSelectWinner?: boolean;
}): ABTestConfig {
  // Validate variants
  const totalPercentage = config.variants.reduce((sum, v) => sum + v.percentage, 0);
  if (totalPercentage !== 100) {
    throw new Error(`Variant percentages must sum to 100, got ${totalPercentage}`);
  }

  if (config.variants.length < 2) {
    throw new Error('A/B test must have at least 2 variants');
  }

  return {
    campaignId: config.campaignId,
    testName: config.testName,
    variants: config.variants,
    winnerCriteria: config.winnerCriteria || 'open_rate',
    minSampleSize: config.minSampleSize || 100,
    testDuration: config.testDuration || 7 * 24 * 60 * 60 * 1000, // 7 days
    autoSelectWinner: config.autoSelectWinner ?? true,
    createdAt: new Date(),
  };
}

/**
 * Assign recipient to a variant based on percentages
 */
export function assignVariant(variants: ABTestVariant[], recipientId: string): ABTestVariant {
  // Use recipient ID hash for consistent assignment
  const hash = hashString(recipientId);
  const random = (hash % 100) + 1; // 1-100

  let cumulative = 0;
  for (const variant of variants) {
    cumulative += variant.percentage;
    if (random <= cumulative) {
      return variant;
    }
  }

  // Fallback to last variant
  return variants[variants.length - 1];
}

/**
 * Calculate metrics for a variant
 */
export function calculateMetrics(
  variantId: string,
  stats: {
    sent: number;
    delivered: number;
    opened: number;
    clicked: number;
    bounced: number;
    complained: number;
  }
): ABTestMetrics {
  const openRate = stats.delivered > 0 ? (stats.opened / stats.delivered) * 100 : 0;
  const clickRate = stats.delivered > 0 ? (stats.clicked / stats.delivered) * 100 : 0;
  const bounceRate = stats.sent > 0 ? (stats.bounced / stats.sent) * 100 : 0;

  return {
    variantId,
    sent: stats.sent,
    delivered: stats.delivered,
    opened: stats.opened,
    clicked: stats.clicked,
    bounced: stats.bounced,
    complained: stats.complained,
    openRate,
    clickRate,
    bounceRate,
  };
}

/**
 * Determine winner based on criteria
 */
export function determineWinner(
  metrics: ABTestMetrics[],
  criteria: 'open_rate' | 'click_rate' | 'conversion_rate',
  minSampleSize: number
): { winnerId: string; confidence: number } | null {
  // Check minimum sample size
  const totalSent = metrics.reduce((sum, m) => sum + m.sent, 0);
  if (totalSent < minSampleSize) {
    return null; // Not enough data
  }

  let bestVariant = metrics[0];
  let bestValue = 0;

  if (criteria === 'open_rate') {
    for (const m of metrics) {
      if (m.openRate > bestValue) {
        bestValue = m.openRate;
        bestVariant = m;
      }
    }
  } else if (criteria === 'click_rate') {
    for (const m of metrics) {
      if (m.clickRate > bestValue) {
        bestValue = m.clickRate;
        bestVariant = m;
      }
    }
  } else if (criteria === 'conversion_rate') {
    // Conversion rate = clicked / sent
    for (const m of metrics) {
      const conversionRate = (m.clicked / m.sent) * 100;
      if (conversionRate > bestValue) {
        bestValue = conversionRate;
        bestVariant = m;
      }
    }
  }

  // Calculate confidence (simplified)
  const confidence = Math.min(100, (totalSent / 1000) * 100);

  return {
    winnerId: bestVariant.variantId,
    confidence,
  };
}

/**
 * Generate A/B test report
 */
export function generateABTestReport(
  testConfig: ABTestConfig,
  metrics: ABTestMetrics[]
): {
  testName: string;
  variants: Array<{
    name: string;
    metrics: ABTestMetrics;
    percentage: number;
  }>;
  winner?: {
    variantId: string;
    confidence: number;
  };
  recommendation: string;
} {
  const winner = determineWinner(
    metrics,
    testConfig.winnerCriteria,
    testConfig.minSampleSize
  );

  const variantMetrics = testConfig.variants.map((variant) => {
    const metric = metrics.find((m) => m.variantId === variant.id);
    return {
      name: variant.name,
      metrics: metric || {
        variantId: variant.id,
        sent: 0,
        delivered: 0,
        opened: 0,
        clicked: 0,
        bounced: 0,
        complained: 0,
        openRate: 0,
        clickRate: 0,
        bounceRate: 0,
      },
      percentage: variant.percentage,
    };
  });

  let recommendation = 'Continue testing - insufficient data';
  if (winner && winner.confidence >= 80) {
    const winnerVariant = testConfig.variants.find((v) => v.id === winner.variantId);
    recommendation = `Winner: ${winnerVariant?.name} (${winner.confidence.toFixed(1)}% confidence)`;
  } else if (winner && winner.confidence >= 50) {
    const winnerVariant = testConfig.variants.find((v) => v.id === winner.variantId);
    recommendation = `Likely winner: ${winnerVariant?.name} (${winner.confidence.toFixed(1)}% confidence) - continue testing`;
  }

  return {
    testName: testConfig.testName,
    variants: variantMetrics,
    winner,
    recommendation,
  };
}

/**
 * Simple hash function for consistent variant assignment
 */
function hashString(str: string): number {
  let hash = 0;
  for (let i = 0; i < str.length; i++) {
    const char = str.charCodeAt(i);
    hash = (hash << 5) - hash + char;
    hash = hash & hash; // Convert to 32-bit integer
  }
  return Math.abs(hash);
}

/**
 * Validate A/B test configuration
 */
export function validateABTest(config: ABTestConfig): { valid: boolean; errors: string[] } {
  const errors: string[] = [];

  if (!config.testName || config.testName.trim() === '') {
    errors.push('Test name is required');
  }

  if (!config.variants || config.variants.length < 2) {
    errors.push('At least 2 variants are required');
  }

  const totalPercentage = config.variants.reduce((sum, v) => sum + v.percentage, 0);
  if (totalPercentage !== 100) {
    errors.push(`Variant percentages must sum to 100, got ${totalPercentage}`);
  }

  if (config.minSampleSize < 10) {
    errors.push('Minimum sample size must be at least 10');
  }

  if (config.testDuration < 60000) {
    // 1 minute minimum
    errors.push('Test duration must be at least 1 minute');
  }

  return {
    valid: errors.length === 0,
    errors,
  };
}
