// Configurable Risk Rules Engine
// Provides configurable fraud detection and risk scoring rules

import { v4 as uuidv4 } from 'uuid';
import prisma from '@/shared/database/prisma';
import { logger } from '@/infrastructure/monitoring/logger';
import { expressionEvaluator } from '@/core/solacenet/policy/expression-evaluator';

export interface RiskRule {
  id: string;
  ruleId: string;
  name: string;
  description?: string;
  ruleType: 'fraud' | 'velocity' | 'device' | 'behavior' | 'custom';
  condition: Record<string, unknown>; // JSON expression
  action: 'block' | 'flag' | 'require_verification' | 'allow';
  riskScore: number; // 0-100
  priority: number;
  status: 'active' | 'inactive';
  metadata?: Record<string, unknown>;
  createdAt: Date;
  updatedAt: Date;
}

export interface RiskAssessmentRequest {
  transactionId?: string;
  userId?: string;
  amount: string;
  currencyCode: string;
  deviceFingerprint?: string;
  ipAddress?: string;
  userAgent?: string;
  velocityData?: {
    count24h?: number;
    amount24h?: string;
    count7d?: number;
    amount7d?: string;
  };
  behaviorData?: Record<string, unknown>;
  context?: Record<string, unknown>;
}

export interface RiskAssessmentResult {
  riskScore: number; // 0-100
  decision: 'allow' | 'block' | 'flag' | 'require_verification';
  matchedRules: Array<{
    ruleId: string;
    riskScore: number;
    action: string;
  }>;
  signals: Array<{
    type: string;
    value: unknown;
    riskLevel: 'low' | 'medium' | 'high';
  }>;
  recommendation?: string;
}

export class RiskRulesEngine {
  /**
   * Assess risk for a transaction or action
   */
  async assessRisk(request: RiskAssessmentRequest): Promise<RiskAssessmentResult> {
    // Get active rules
    const rules = await this.getActiveRules();

    const matchedRules: Array<{ ruleId: string; riskScore: number; action: string }> = [];
    const signals: Array<{ type: string; value: unknown; riskLevel: 'low' | 'medium' | 'high' }> = [];
    let totalRiskScore = 0;
    let maxRiskScore = 0;
    let finalDecision: 'allow' | 'block' | 'flag' | 'require_verification' = 'allow';

    // Build context for rule evaluation
    const context: Record<string, unknown> = {
      amount: parseFloat(request.amount),
      currencyCode: request.currencyCode,
      deviceFingerprint: request.deviceFingerprint,
      ipAddress: request.ipAddress,
      userAgent: request.userAgent,
      ...request.velocityData,
      ...request.behaviorData,
      ...request.context,
    };

    // Evaluate each rule
    for (const rule of rules) {
      try {
        const conditionMet = expressionEvaluator.evaluate(rule.condition, context);

        if (conditionMet) {
          matchedRules.push({
            ruleId: rule.ruleId,
            riskScore: rule.riskScore,
            action: rule.action,
          });

          totalRiskScore += rule.riskScore;
          maxRiskScore = Math.max(maxRiskScore, rule.riskScore);

          // Determine decision based on action priority
          if (rule.action === 'block' && finalDecision !== 'block') {
            finalDecision = 'block';
          } else if (rule.action === 'require_verification' && finalDecision === 'allow') {
            finalDecision = 'require_verification';
          } else if (rule.action === 'flag' && finalDecision === 'allow') {
            finalDecision = 'flag';
          }

          // Add signal
          signals.push({
            type: rule.ruleType,
            value: rule.name,
            riskLevel: this.getRiskLevel(rule.riskScore),
          });
        }
      } catch (error) {
        logger.warn(`Failed to evaluate rule ${rule.ruleId}`, { error });
      }
    }

    // Calculate final risk score (weighted average, capped at 100)
    const finalRiskScore = Math.min(100, Math.max(maxRiskScore, totalRiskScore / Math.max(1, matchedRules.length)));

    // Device fingerprinting signal
    if (request.deviceFingerprint) {
      signals.push({
        type: 'device',
        value: request.deviceFingerprint,
        riskLevel: 'low',
      });
    }

    // Velocity signals
    if (request.velocityData) {
      if (request.velocityData.count24h && request.velocityData.count24h > 10) {
        signals.push({
          type: 'velocity',
          value: `High transaction count: ${request.velocityData.count24h} in 24h`,
          riskLevel: 'medium',
        });
      }
      if (request.velocityData.amount24h) {
        const amount24h = parseFloat(request.velocityData.amount24h);
        const currentAmount = parseFloat(request.amount);
        if (amount24h > currentAmount * 5) {
          signals.push({
            type: 'velocity',
            value: `High velocity: ${amount24h} in 24h`,
            riskLevel: 'high',
          });
        }
      }
    }

    // Generate recommendation
    let recommendation: string | undefined;
    if (finalRiskScore >= 80) {
      recommendation = 'Block transaction - high risk detected';
    } else if (finalRiskScore >= 60) {
      recommendation = 'Require additional verification';
    } else if (finalRiskScore >= 40) {
      recommendation = 'Flag for review';
    }

    logger.info(`Risk assessment completed`, {
      riskScore: finalRiskScore,
      decision: finalDecision,
      matchedRules: matchedRules.length,
    });

    return {
      riskScore: finalRiskScore,
      decision: finalDecision,
      matchedRules,
      signals,
      recommendation,
    };
  }

  /**
   * Create a risk rule
   */
  async createRule(rule: Omit<RiskRule, 'id' | 'createdAt' | 'updatedAt'>): Promise<RiskRule> {
    // In production, store in database
    // For now, return the rule with generated ID
    const newRule: RiskRule = {
      ...rule,
      id: uuidv4(),
      createdAt: new Date(),
      updatedAt: new Date(),
    };

    logger.info(`Risk rule created: ${rule.ruleId}`);
    return newRule;
  }

  /**
   * Get active rules
   */
  async getActiveRules(): Promise<RiskRule[]> {
    // In production, fetch from database
    // For now, return default rules
    return this.getDefaultRules();
  }

  /**
   * Get default risk rules
   */
  private getDefaultRules(): RiskRule[] {
    return [
      {
        id: uuidv4(),
        ruleId: 'high_amount',
        name: 'High Amount Transaction',
        description: 'Flag transactions above threshold',
        ruleType: 'fraud',
        condition: {
          gt: { amount: 10000 },
        },
        action: 'flag',
        riskScore: 30,
        priority: 100,
        status: 'active',
        createdAt: new Date(),
        updatedAt: new Date(),
      },
      {
        id: uuidv4(),
        ruleId: 'velocity_24h',
        name: 'High Velocity 24h',
        description: 'Block if more than 20 transactions in 24h',
        ruleType: 'velocity',
        condition: {
          gt: { count24h: 20 },
        },
        action: 'block',
        riskScore: 80,
        priority: 50,
        status: 'active',
        createdAt: new Date(),
        updatedAt: new Date(),
      },
      {
        id: uuidv4(),
        ruleId: 'new_device',
        name: 'New Device',
        description: 'Require verification for new device',
        ruleType: 'device',
        condition: {
          eq: { deviceFingerprint: null },
        },
        action: 'require_verification',
        riskScore: 40,
        priority: 75,
        status: 'active',
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    ];
  }

  /**
   * Get risk level from score
   */
  private getRiskLevel(score: number): 'low' | 'medium' | 'high' {
    if (score >= 70) return 'high';
    if (score >= 40) return 'medium';
    return 'low';
  }
}

export const riskRulesEngine = new RiskRulesEngine();
