/**
 * Fraud Detection System
 * Detects suspicious payment patterns and transactions
 */

export type FraudRiskLevel = 'low' | 'medium' | 'high' | 'critical';
export type FraudIndicator =
  | 'velocity_check'
  | 'duplicate_transaction'
  | 'unusual_amount'
  | 'geographic_anomaly'
  | 'device_fingerprint'
  | 'behavioral_pattern'
  | 'high_value_transaction'
  | 'rapid_refund_request';

export interface FraudScore {
  playerId: number;
  score: number; // 0-100
  riskLevel: FraudRiskLevel;
  indicators: Array<{
    type: FraudIndicator;
    weight: number; // 0-1
    reason: string;
  }>;
  timestamp: Date;
  reviewed: boolean;
  reviewedBy?: number;
  reviewedAt?: Date;
  decision?: 'approved' | 'blocked' | 'manual_review';
  notes?: string;
}

export interface TransactionVelocity {
  playerId: number;
  transactionCount: number;
  timeWindowMinutes: number;
  averageAmount: number;
  totalAmount: number;
  isAnomalous: boolean;
}

export interface DuplicateTransactionCheck {
  isDuplicate: boolean;
  matchingTransactionId?: string;
  similarity: number; // 0-1
  reason?: string;
}

export interface GeographicAnomaly {
  isAnomalous: boolean;
  previousCountries: string[];
  currentCountry: string;
  distanceKm?: number;
  timeSinceLastTransaction?: number; // hours
  reason?: string;
}

export interface DeviceFingerprint {
  deviceId: string;
  ipAddress: string;
  userAgent: string;
  isNewDevice: boolean;
  previousDevices: string[];
  riskScore: number; // 0-1
}

export interface BehavioralPattern {
  playerId: number;
  averageTransactionAmount: number;
  standardDeviation: number;
  currentAmount: number;
  isOutlier: boolean;
  zScore: number;
  reason?: string;
}

export interface FraudAlert {
  id: string;
  playerId: number;
  transactionId: string;
  riskLevel: FraudRiskLevel;
  indicators: FraudIndicator[];
  message: string;
  timestamp: Date;
  acknowledged: boolean;
  acknowledgedBy?: number;
  action?: 'block' | 'approve' | 'manual_review';
}

export interface FraudStatistics {
  totalTransactionsAnalyzed: number;
  flaggedTransactions: number;
  blockedTransactions: number;
  manualReviewTransactions: number;
  approvedTransactions: number;
  falsePositiveRate: number; // percentage
  detectionAccuracy: number; // percentage
  averageFraudScore: number;
  highRiskPlayers: number;
}

/**
 * Calculate fraud score for a transaction
 */
export function calculateFraudScore(
  indicators: Array<{ type: FraudIndicator; weight: number }>
): { score: number; riskLevel: FraudRiskLevel } {
  const totalWeight = indicators.reduce((sum, i) => sum + i.weight, 0);
  const score = Math.min(100, totalWeight * 20); // Normalize to 0-100

  let riskLevel: FraudRiskLevel = 'low';
  if (score >= 75) riskLevel = 'critical';
  else if (score >= 50) riskLevel = 'high';
  else if (score >= 25) riskLevel = 'medium';

  return { score, riskLevel };
}

/**
 * Check transaction velocity
 */
export function checkTransactionVelocity(
  playerId: number,
  recentTransactions: Array<{ amount: number; timestamp: Date }>,
  currentTime: Date,
  timeWindowMinutes: number = 60,
  maxTransactionsPerWindow: number = 5,
  maxAmountPerWindow: number = 1000
): TransactionVelocity {
  const windowStart = new Date(currentTime.getTime() - timeWindowMinutes * 60 * 1000);
  const transactionsInWindow = recentTransactions.filter((t) => t.timestamp >= windowStart);

  const transactionCount = transactionsInWindow.length;
  const totalAmount = transactionsInWindow.reduce((sum, t) => sum + t.amount, 0);
  const averageAmount = transactionCount > 0 ? totalAmount / transactionCount : 0;

  const isAnomalous =
    transactionCount > maxTransactionsPerWindow || totalAmount > maxAmountPerWindow;

  return {
    playerId,
    transactionCount,
    timeWindowMinutes,
    averageAmount,
    totalAmount,
    isAnomalous,
  };
}

/**
 * Check for duplicate transactions
 */
export function checkDuplicateTransaction(
  currentTransaction: { playerId: number; amount: number; timestamp: Date },
  recentTransactions: Array<{ id: string; playerId: number; amount: number; timestamp: Date }>,
  timeWindowSeconds: number = 300
): DuplicateTransactionCheck {
  const windowStart = new Date(
    currentTransaction.timestamp.getTime() - timeWindowSeconds * 1000
  );

  const potentialDuplicates = recentTransactions.filter(
    (t) =>
      t.playerId === currentTransaction.playerId &&
      t.timestamp >= windowStart &&
      Math.abs(t.amount - currentTransaction.amount) < 0.01 // Allow for floating point errors
  );

  if (potentialDuplicates.length > 0) {
    return {
      isDuplicate: true,
      matchingTransactionId: potentialDuplicates[0].id,
      similarity: 0.95,
      reason: 'Exact amount match within 5 minutes',
    };
  }

  return { isDuplicate: false, similarity: 0 };
}

/**
 * Detect geographic anomalies
 */
export function detectGeographicAnomaly(
  playerId: number,
  currentCountry: string,
  previousTransactions: Array<{ country: string; timestamp: Date }>,
  maxDistanceKm: number = 500
): GeographicAnomaly {
  if (previousTransactions.length === 0) {
    return { isAnomalous: false, previousCountries: [], currentCountry };
  }

  const previousCountries = [...new Set(previousTransactions.map((t) => t.country))];

  // If country is different from all previous countries
  if (!previousCountries.includes(currentCountry)) {
    const lastTransaction = previousTransactions[previousTransactions.length - 1];
    const timeSinceLastTransaction =
      (new Date().getTime() - lastTransaction.timestamp.getTime()) / (1000 * 60 * 60);

    // If different country and less than 2 hours since last transaction, it's suspicious
    if (timeSinceLastTransaction < 2) {
      return {
        isAnomalous: true,
        previousCountries,
        currentCountry,
        timeSinceLastTransaction,
        reason: 'Geographic impossibility - different country in less than 2 hours',
      };
    }
  }

  return { isAnomalous: false, previousCountries, currentCountry };
}

/**
 * Analyze behavioral patterns
 */
export function analyzeBehavioralPattern(
  playerId: number,
  currentAmount: number,
  historicalAmounts: number[]
): BehavioralPattern {
  if (historicalAmounts.length === 0) {
    return {
      playerId,
      averageTransactionAmount: 0,
      standardDeviation: 0,
      currentAmount,
      isOutlier: false,
      zScore: 0,
    };
  }

  const average = historicalAmounts.reduce((sum, a) => sum + a, 0) / historicalAmounts.length;
  const variance =
    historicalAmounts.reduce((sum, a) => sum + Math.pow(a - average, 2), 0) /
    historicalAmounts.length;
  const standardDeviation = Math.sqrt(variance);

  const zScore = standardDeviation > 0 ? (currentAmount - average) / standardDeviation : 0;
  const isOutlier = Math.abs(zScore) > 3; // 3 standard deviations

  let reason: string | undefined;
  if (isOutlier) {
    reason =
      currentAmount > average
        ? `Transaction amount ${((currentAmount / average - 1) * 100).toFixed(0)}% higher than average`
        : `Transaction amount ${((1 - currentAmount / average) * 100).toFixed(0)}% lower than average`;
  }

  return {
    playerId,
    averageTransactionAmount: average,
    standardDeviation,
    currentAmount,
    isOutlier,
    zScore,
    reason,
  };
}

/**
 * Validate device fingerprint
 */
export function validateDeviceFingerprint(
  deviceId: string,
  ipAddress: string,
  userAgent: string,
  previousDevices: Array<{ deviceId: string; ipAddress: string; userAgent: string }>
): DeviceFingerprint {
  const isNewDevice = !previousDevices.some((d) => d.deviceId === deviceId);

  let riskScore = 0;
  if (isNewDevice) riskScore += 0.3;

  // Check for IP spoofing patterns
  const previousIPs = previousDevices.map((d) => d.ipAddress);
  if (previousIPs.length > 0 && !previousIPs.includes(ipAddress)) {
    riskScore += 0.2;
  }

  // Check for user agent changes
  const previousUserAgents = previousDevices.map((d) => d.userAgent);
  if (previousUserAgents.length > 0 && !previousUserAgents.includes(userAgent)) {
    riskScore += 0.15;
  }

  return {
    deviceId,
    ipAddress,
    userAgent,
    isNewDevice,
    previousDevices: previousDevices.map((d) => d.deviceId),
    riskScore: Math.min(1, riskScore),
  };
}

/**
 * Create fraud score record
 */
export function createFraudScore(
  playerId: number,
  indicators: Array<{ type: FraudIndicator; weight: number; reason: string }>
): FraudScore {
  const { score, riskLevel } = calculateFraudScore(indicators);

  return {
    playerId,
    score,
    riskLevel,
    indicators,
    timestamp: new Date(),
    reviewed: false,
  };
}

/**
 * Review fraud score
 */
export function reviewFraudScore(
  fraudScore: FraudScore,
  decision: 'approved' | 'blocked' | 'manual_review',
  reviewedBy: number,
  notes?: string
): void {
  fraudScore.reviewed = true;
  fraudScore.reviewedBy = reviewedBy;
  fraudScore.reviewedAt = new Date();
  fraudScore.decision = decision;
  fraudScore.notes = notes;
}

/**
 * Generate fraud alert
 */
export function generateFraudAlert(
  playerId: number,
  transactionId: string,
  fraudScore: FraudScore
): FraudAlert {
  const indicatorTypes = fraudScore.indicators.map((i) => i.type);

  let message = `Fraud risk detected: ${fraudScore.riskLevel.toUpperCase()}`;
  if (fraudScore.riskLevel === 'critical') {
    message += ` - Transaction flagged for immediate review`;
  }

  return {
    id: `alert_${transactionId}_${Date.now()}`,
    playerId,
    transactionId,
    riskLevel: fraudScore.riskLevel,
    indicators: indicatorTypes,
    message,
    timestamp: new Date(),
    acknowledged: false,
  };
}

/**
 * Calculate fraud statistics
 */
export function calculateFraudStatistics(
  fraudScores: FraudScore[],
  totalTransactions: number
): FraudStatistics {
  const flagged = fraudScores.filter((f) => f.riskLevel !== 'low');
  const blocked = fraudScores.filter((f) => f.decision === 'blocked');
  const manualReview = fraudScores.filter((f) => f.decision === 'manual_review');
  const approved = fraudScores.filter((f) => f.decision === 'approved');

  const averageFraudScore =
    fraudScores.length > 0 ? fraudScores.reduce((sum, f) => sum + f.score, 0) / fraudScores.length : 0;

  const highRiskPlayers = new Set(
    fraudScores.filter((f) => f.riskLevel === 'high' || f.riskLevel === 'critical').map((f) => f.playerId)
  ).size;

  // Estimate false positive rate (approved transactions that were flagged)
  const falsePositiveRate = approved.length > 0 ? (approved.length / flagged.length) * 100 : 0;

  // Estimate detection accuracy (blocked + manual review vs total flagged)
  const detectionAccuracy =
    flagged.length > 0 ? ((blocked.length + manualReview.length) / flagged.length) * 100 : 0;

  return {
    totalTransactionsAnalyzed: totalTransactions,
    flaggedTransactions: flagged.length,
    blockedTransactions: blocked.length,
    manualReviewTransactions: manualReview.length,
    approvedTransactions: approved.length,
    falsePositiveRate,
    detectionAccuracy,
    averageFraudScore,
    highRiskPlayers,
  };
}

/**
 * Comprehensive fraud check
 */
export async function performComprehensiveFraudCheck(
  playerId: number,
  transactionId: string,
  amount: number,
  country: string,
  deviceId: string,
  ipAddress: string,
  userAgent: string,
  recentTransactions: Array<{ id: string; amount: number; timestamp: Date; country: string }>,
  historicalAmounts: number[],
  previousDevices: Array<{ deviceId: string; ipAddress: string; userAgent: string }>
): FraudScore {
  const indicators: Array<{ type: FraudIndicator; weight: number; reason: string }> = [];

  // Check velocity
  const velocity = checkTransactionVelocity(playerId, recentTransactions, new Date());
  if (velocity.isAnomalous) {
    indicators.push({
      type: 'velocity_check',
      weight: 0.4,
      reason: `${velocity.transactionCount} transactions in ${velocity.timeWindowMinutes} minutes`,
    });
  }

  // Check for duplicates
  const duplicate = checkDuplicateTransaction(
    { playerId, amount, timestamp: new Date() },
    recentTransactions
  );
  if (duplicate.isDuplicate) {
    indicators.push({
      type: 'duplicate_transaction',
      weight: 0.5,
      reason: duplicate.reason || 'Potential duplicate transaction',
    });
  }

  // Check geographic anomalies
  const geoAnomaly = detectGeographicAnomaly(
    playerId,
    country,
    recentTransactions.map((t) => ({ country: t.country, timestamp: new Date(t.timestamp) }))
  );
  if (geoAnomaly.isAnomalous) {
    indicators.push({
      type: 'geographic_anomaly',
      weight: 0.6,
      reason: geoAnomaly.reason || 'Geographic anomaly detected',
    });
  }

  // Check behavioral patterns
  const behavior = analyzeBehavioralPattern(playerId, amount, historicalAmounts);
  if (behavior.isOutlier) {
    indicators.push({
      type: 'behavioral_pattern',
      weight: 0.3,
      reason: behavior.reason || 'Unusual transaction amount',
    });
  }

  // Check device fingerprint
  const device = validateDeviceFingerprint(deviceId, ipAddress, userAgent, previousDevices);
  if (device.riskScore > 0.5) {
    indicators.push({
      type: 'device_fingerprint',
      weight: device.riskScore * 0.4,
      reason: 'New or suspicious device detected',
    });
  }

  // Check for high value transactions
  if (amount > 500) {
    indicators.push({
      type: 'high_value_transaction',
      weight: 0.2,
      reason: `High value transaction: $${amount}`,
    });
  }

  return createFraudScore(playerId, indicators);
}
