// AML Velocity Engine
// Velocity anomaly detection
// Circular transfer identification
// Synthetic layering detection
// SSU manipulation signal detection

import prisma from '@/shared/database/prisma';
import { Decimal } from '@prisma/client/runtime/library';
import { v4 as uuidv4 } from 'uuid';


export interface VelocityAnomaly {
  entityId: string;
  entityType: string;
  velocity: Decimal;
  threshold: Decimal;
  anomalyScore: number;
}

export class AmlVelocityEngineService {
  /**
   * Detect velocity anomalies
   */
  async detectVelocityAnomalies(
    entityId: string,
    entityType: string,
    timeWindow: number = 3600000 // 1 hour in ms
  ): Promise<VelocityAnomaly[]> {
    const anomalies: VelocityAnomaly[] = [];

    const windowStart = new Date(Date.now() - timeWindow);

    // Get transactions in time window
    const transactions = await this.getTransactionsInWindow(
      entityId,
      entityType,
      windowStart
    );

    if (transactions.length === 0) {
      return anomalies;
    }

    // Calculate velocity (transactions per time window)
    const velocity = new Decimal(transactions.length);

    // Get threshold for entity type
    const threshold = this.getVelocityThreshold(entityType);

    // Check if velocity exceeds threshold
    if (velocity.gt(threshold)) {
      // Calculate anomaly score
      const anomalyScore = this.calculateAnomalyScore(velocity, threshold);

      anomalies.push({
        entityId,
        entityType,
        velocity,
        threshold,
        anomalyScore,
      });
    }

    // Check for unusual patterns
    const patterns = await this.detectUnusualPatterns(transactions);

    for (const pattern of patterns) {
      anomalies.push({
        entityId: pattern.entityId,
        entityType: pattern.entityType,
        velocity: pattern.velocity,
        threshold: pattern.threshold,
        anomalyScore: pattern.anomalyScore,
      });
    }

    return anomalies;
  }

  /**
   * Identify circular transfers
   */
  async identifyCircularTransfers(
    sourceBankId: string,
    destinationBankId: string,
    timeWindow: number = 86400000 // 24 hours
  ): Promise<boolean> {
    const windowStart = new Date(Date.now() - timeWindow);

    // Get account IDs for source and destination banks
    const sourceAccounts = await prisma.bank_accounts.findMany({
      where: { sovereignBankId: sourceBankId },
      select: { id: true },
    });
    const destinationAccounts = await prisma.bank_accounts.findMany({
      where: { sovereignBankId: destinationBankId },
      select: { id: true },
    });

    const sourceAccountIds = sourceAccounts.map(a => a.id);
    const destinationAccountIds = destinationAccounts.map(a => a.id);

    // Get transactions from source to destination
    const forwardTransactions = await prisma.ledger_entries.findMany({
      where: {
        debitAccountId: { in: sourceAccountIds },
        creditAccountId: { in: destinationAccountIds },
        createdAt: {
          gte: windowStart,
        },
        status: 'settled',
      },
    });

    // Get transactions from destination to source
    const reverseTransactions = await prisma.ledger_entries.findMany({
      where: {
        debitAccountId: { in: destinationAccountIds },
        creditAccountId: { in: sourceAccountIds },
        createdAt: {
          gte: windowStart,
        },
        status: 'settled',
      },
    });

    // Check if amounts match (circular pattern)
    if (forwardTransactions.length > 0 && reverseTransactions.length > 0) {
      const forwardAmount = forwardTransactions.reduce(
        (sum, tx) => sum.plus(tx.amount),
        new Decimal(0)
      );

      const reverseAmount = reverseTransactions.reduce(
        (sum, tx) => sum.plus(tx.amount),
        new Decimal(0)
      );

      // If amounts are similar, likely circular transfer
      const diff = forwardAmount.minus(reverseAmount).abs();
      const avgAmount = forwardAmount.plus(reverseAmount).div(2);

      if (avgAmount.gt(0) && diff.div(avgAmount).lt(0.1)) {
        // Within 10% difference
        return true;
      }
    }

    return false;
  }

  /**
   * Detect synthetic layering attempts
   */
  async detectSyntheticLayering(
    entityId: string,
    timeWindow: number = 86400000
  ): Promise<boolean> {
    const windowStart = new Date(Date.now() - timeWindow);

    // Get all transactions for entity
    const transactions = await this.getTransactionsInWindow(
      entityId,
      'account',
      windowStart
    );

    if (transactions.length < 3) {
      return false;
    }

    // Check for layering patterns:
    // 1. Multiple small transactions followed by large transaction
    // 2. Multiple intermediate accounts
    // 3. Unusual timing patterns

    // Group transactions by account
    const accountGroups = new Map<string, any[]>();

    for (const tx of transactions) {
      const accountId = tx.debitAccountId || tx.creditAccountId;
      if (!accountGroups.has(accountId)) {
        accountGroups.set(accountId, []);
      }
      accountGroups.get(accountId)!.push(tx);
    }

    // Check if multiple intermediate accounts are used
    if (accountGroups.size > 5) {
      // Likely layering attempt
      return true;
    }

    // Check for timing patterns
    const sortedTransactions = transactions.sort(
      (a, b) => a.createdAt.getTime() - b.createdAt.getTime()
    );

    // Check for rapid sequential transactions
    for (let i = 1; i < sortedTransactions.length; i++) {
      const timeDiff = sortedTransactions[i].createdAt.getTime() -
        sortedTransactions[i - 1].createdAt.getTime();

      if (timeDiff < 60000) {
        // Transactions within 1 minute
        return true; // Likely layering
      }
    }

    return false;
  }

  /**
   * Detect SSU manipulation signals
   */
  async detectSsuManipulation(
    ssuId: string,
    timeWindow: number = 86400000
  ): Promise<boolean> {
    const windowStart = new Date(Date.now() - timeWindow);

    // Get SSU transactions
    const transactions = await prisma.ssu_transactions.findMany({
      where: {
        ssuId,
        createdAt: {
          gte: windowStart,
        },
      },
    });

    if (transactions.length === 0) {
      return false;
    }

    // Check for manipulation patterns:
    // 1. Rapid mint/burn cycles
    // 2. Unusual transaction volumes
    // 3. Price manipulation attempts

    // Check for rapid mint/burn cycles
    let mintCount = 0;
    let burnCount = 0;

    for (const tx of transactions) {
      if (tx.transactionType === 'mint') {
        mintCount++;
      } else if (tx.transactionType === 'burn') {
        burnCount++;
      }
    }

    // If multiple mint/burn cycles in short time, likely manipulation
    if (mintCount > 5 && burnCount > 5) {
      return true;
    }

    // Check for unusual volumes
    const totalAmount = transactions.reduce(
      (sum, tx) => sum.plus(tx.amount),
      new Decimal(0)
    );

    // Get average transaction amount
    const avgAmount = totalAmount.div(transactions.length);

    // Check for outliers
    for (const tx of transactions) {
      if (tx.amount.gt(avgAmount.mul(10))) {
        // Transaction is 10x average
        return true; // Likely manipulation
      }
    }

    return false;
  }

  /**
   * Get transactions in time window
   */
  private async getTransactionsInWindow(
    entityId: string,
    entityType: string,
    windowStart: Date
  ): Promise<any[]> {
    if (entityType === 'account') {
      return await prisma.ledger_entries.findMany({
        where: {
          OR: [
            { debitAccountId: entityId },
            { creditAccountId: entityId },
          ],
          createdAt: {
            gte: windowStart,
          },
        },
      });
    }

    return [];
  }

  /**
   * Get velocity threshold for entity type
   */
  private getVelocityThreshold(entityType: string): Decimal {
    const thresholds: Record<string, Decimal> = {
      account: new Decimal(100), // 100 transactions per hour
      bank: new Decimal(1000),
      wallet: new Decimal(50),
    };

    return thresholds[entityType] || new Decimal(100);
  }

  /**
   * Calculate anomaly score
   */
  private calculateAnomalyScore(
    velocity: Decimal,
    threshold: Decimal
  ): number {
    const ratio = velocity.div(threshold);
    // Score: 0-100, higher = more anomalous
    return Math.min(100, ratio.toNumber() * 50);
  }

  /**
   * Detect unusual patterns
   */
  private async detectUnusualPatterns(transactions: any[]): Promise<any[]> {
    const patterns: any[] = [];

    // Check for rapid-fire transactions
    if (transactions.length > 50) {
      patterns.push({
        entityId: transactions[0].debitAccountId || transactions[0].creditAccountId,
        entityType: 'account',
        velocity: new Decimal(transactions.length),
        threshold: new Decimal(50),
        anomalyScore: 80,
      });
    }

    return patterns;
  }
}

export const amlVelocityEngineService = new AmlVelocityEngineService();

