// Supervision Engine Service
import { Prisma } from '@prisma/client';
// Automated monitoring (AML behaviors, velocity, clustering)

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

export interface MonitoringResult {
  ruleId: string;
  ruleName: string;
  triggered: boolean;
  severity: string;
  details: Prisma.InputJsonValue;
}

export class SupervisionEngineService {
  /**
   * Monitor transaction for AML behaviors
   */
  async monitorAMLBehaviors(transactionId: string, sovereignBankId: string): Promise<MonitoringResult[]> {
    const results: MonitoringResult[] = [];

    // Get AML behavior rules
    const amlRules = await prisma.supervision_rules.findMany({
      where: {
        ruleType: 'aml_behavior',
        status: 'active',
      },
    });

    for (const rule of amlRules) {
      const triggered = await this.evaluateRule(rule, transactionId, sovereignBankId);
      if (triggered) {
        results.push({
          ruleId: rule.ruleId,
          ruleName: rule.ruleName,
          triggered: true,
          severity: rule.severity,
          details: {
            threshold: rule.threshold?.toString(),
            ruleLogic: rule.ruleLogic,
          },
        });
      }
    }

    return results;
  }

  /**
   * Monitor transaction velocity
   */
  async monitorTransactionVelocity(
    sovereignBankId: string,
    entityId: string,
    timeWindowMinutes: number = 60
  ): Promise<MonitoringResult[]> {
    const results: MonitoringResult[] = [];

    // Get velocity rules
    const velocityRules = await prisma.supervision_rules.findMany({
      where: {
        ruleType: 'transaction_velocity',
        status: 'active',
      },
    });

    const cutoffTime = new Date();
    cutoffTime.setMinutes(cutoffTime.getMinutes() - timeWindowMinutes);

    // Get account IDs for the sovereign bank
    const accounts = await prisma.bank_accounts.findMany({
      where: { sovereignBankId },
      select: { id: true },
    });
    const accountIds = accounts.map(a => a.id);

    // Count transactions in time window
    const transactionCount = await prisma.ledger_entries.count({
      where: {
        OR: [
          {
            debitAccountId: { in: accountIds },
          },
          {
            creditAccountId: { in: accountIds },
          },
        ],
        createdAt: {
          gte: cutoffTime,
        },
        status: 'settled',
      },
    });

    for (const rule of velocityRules) {
      const threshold = rule.threshold ? parseFloat(rule.threshold.toString()) : 100;
      if (transactionCount > threshold) {
        results.push({
          ruleId: rule.ruleId,
          ruleName: rule.ruleName,
          triggered: true,
          severity: rule.severity,
          details: {
            transactionCount,
            threshold,
            timeWindowMinutes,
          },
        });
      }
    }

    return results;
  }

  /**
   * Monitor transaction clustering
   */
  async monitorClustering(
    sovereignBankId: string,
    timeWindowHours: number = 24
  ): Promise<MonitoringResult[]> {
    const results: MonitoringResult[] = [];

    // Get clustering rules
    const clusteringRules = await prisma.supervision_rules.findMany({
      where: {
        ruleType: 'clustering',
        status: 'active',
      },
    });

    const cutoffTime = new Date();
    cutoffTime.setHours(cutoffTime.getHours() - timeWindowHours);

    // Get account IDs for the sovereign bank
    const accounts = await prisma.bank_accounts.findMany({
      where: { sovereignBankId },
      select: { id: true },
    });
    const accountIds = accounts.map(a => a.id);

    // Get transactions in time window
    const transactions = await prisma.ledger_entries.findMany({
      where: {
        OR: [
          {
            debitAccountId: { in: accountIds },
          },
          {
            creditAccountId: { in: accountIds },
          },
        ],
        createdAt: {
          gte: cutoffTime,
        },
        status: 'settled',
      },
      include: {
        bank_accounts_ledger_entries_debitAccountIdTobank_accounts: true,
        bank_accounts_ledger_entries_creditAccountIdTobank_accounts: true,
      },
    });

    // Analyze clustering patterns
    const accountClusters: Record<string, number> = {};
    for (const tx of transactions) {
      const key = `${tx.debitAccountId}_${tx.creditAccountId}`;
      accountClusters[key] = (accountClusters[key] || 0) + 1;
    }

    // Find clusters above threshold
    for (const rule of clusteringRules) {
      const threshold = rule.threshold ? parseFloat(rule.threshold.toString()) : 10;
      const clusters = Object.entries(accountClusters).filter(([_, count]) => count > threshold);

      if (clusters.length > 0) {
        results.push({
          ruleId: rule.ruleId,
          ruleName: rule.ruleName,
          triggered: true,
          severity: rule.severity,
          details: {
            clusterCount: clusters.length,
            threshold,
            clusters: clusters.map(([key, count]) => ({ accountPair: key, count })),
          },
        });
      }
    }

    return results;
  }

  /**
   * Monitor FX anomalies
   */
  async monitorFXAnomalies(sovereignBankId: string): Promise<MonitoringResult[]> {
    const results: MonitoringResult[] = [];

    // Get FX anomaly rules
    const fxRules = await prisma.supervision_rules.findMany({
      where: {
        ruleType: 'fx_anomaly',
        status: 'active',
      },
    });

    // Get recent FX trades
    const recentTrades = await prisma.fx_trades.findMany({
      where: {
        sovereignBankId,
        timestampUtc: {
          gte: new Date(Date.now() - 24 * 60 * 60 * 1000), // Last 24 hours
        },
        status: 'settled',
      },
      orderBy: { timestampUtc: 'desc' },
      take: 100,
    });

    if (recentTrades.length < 2) {
      return results;
    }

    // Calculate price volatility
    const prices = recentTrades.map((t) => parseFloat(t.price.toString()));
    const mean = prices.reduce((a: number, b: number) => a + b, 0) / prices.length;
    const variance = prices.reduce((sum: number, p: number) => sum + Math.pow(p - mean, 2), 0) / prices.length;
    const stdDev = Math.sqrt(variance);

    for (const rule of fxRules) {
      const threshold = rule.threshold ? parseFloat(rule.threshold.toString()) : mean * 0.1;
      if (stdDev > threshold) {
        results.push({
          ruleId: rule.ruleId,
          ruleName: rule.ruleName,
          triggered: true,
          severity: rule.severity,
          details: {
            volatility: stdDev,
            threshold,
            meanPrice: mean,
            tradeCount: recentTrades.length,
          },
        });
      }
    }

    return results;
  }

  /**
   * Monitor sanctions matching
   */
  async monitorSanctionsMatching(entityName: string): Promise<MonitoringResult[]> {
    const results: MonitoringResult[] = [];

    // Get sanctions matching rules
    const sanctionsRules = await prisma.supervision_rules.findMany({
      where: {
        ruleType: 'sanctions_matching',
        status: 'active',
      },
    });

    // Check sanctions list
    const sanctionsMatch = await prisma.sanctions_lists.findFirst({
      where: {
        entityName: {
          contains: entityName,
          mode: 'insensitive',
        },
        status: 'active',
      },
    });

    if (sanctionsMatch) {
      for (const rule of sanctionsRules) {
        results.push({
          ruleId: rule.ruleId,
          ruleName: rule.ruleName,
          triggered: true,
          severity: 'critical',
          details: {
            entityName,
            sanctionsList: sanctionsMatch.listSource,
            listId: sanctionsMatch.listId,
          },
        });
      }
    }

    return results;
  }

  /**
   * Evaluate supervision rule
   */
  private async evaluateRule(
    rule: any,
    transactionId: string,
    sovereignBankId: string
  ): Promise<boolean> {
    // In production, this would evaluate the rule logic
    // For now, simplified evaluation
    const ruleLogic = rule.ruleLogic as Prisma.InputJsonValue;
    const ruleLogicObj = ruleLogic as Record<string, unknown>;

    if (ruleLogicObj.type === 'threshold') {
      const threshold = rule.threshold ? parseFloat(rule.threshold.toString()) : 0;
      // Get transaction value and compare
      const transaction = await prisma.ledger_entries.findFirst({
        where: { referenceId: transactionId },
      });

      if (transaction) {
        const amount = parseFloat(transaction.amount.toString());
        return amount > threshold;
      }
    }

    return false;
  }

  /**
   * Create supervision rule
   */
  async createSupervisionRule(
    ruleName: string,
    ruleType: string,
    ruleLogic: Prisma.InputJsonValue,
    threshold?: number,
    severity: string = 'medium'
  ) {
    return await prisma.supervision_rules.create({
      data: {
        id: uuidv4(),
        ruleId: uuidv4(),
        ruleName,
        ruleType,
        ruleLogic,
        threshold: threshold ? new Decimal(threshold) : undefined,
        severity,
        status: 'active',
        effectiveDate: new Date(),
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    });
  }
}

export const supervisionEngineService = new SupervisionEngineService();

