import { Injectable, Logger } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { PrismaService } from '../../common/prisma.service';

export interface FraudRuleConfig {
  type: 'VELOCITY' | 'HIGH_VALUE' | 'FAILED_PAYMENTS' | 'MULTIPLE_METHODS';
  enabled: boolean;
  threshold: number;
  timeWindowMinutes?: number;
  blockDurationMinutes?: number;
}

@Injectable()
export class FraudService {
  private readonly logger = new Logger(FraudService.name);

  constructor(private prisma: PrismaService) {}

  /**
   * Evaluate fraud rules and create alerts if triggered
   */
  async evaluateRules(
    userId: string,
    eventType: 'booking.created' | 'payment.failed' | 'auth.login.failed',
    context: Record<string, unknown>,
  ): Promise<{ triggered: boolean; rules: string[] }> {
    const triggeredRules: string[] = [];
    const user = await this.prisma.user.findUnique({ where: { id: userId } });

    if (!user) return { triggered: false, rules: [] };

    // Check velocity rule (multiple bookings in short time)
    if (eventType === 'booking.created') {
      const velocityTriggered = await this.checkVelocityRule(userId);
      if (velocityTriggered) {
        triggeredRules.push('VELOCITY');
      }

      // Check high-value booking
      const amount = (context.amountMinor as number) || 0;
      const highValueTriggered = await this.checkHighValueRule(amount);
      if (highValueTriggered) {
        triggeredRules.push('HIGH_VALUE');
      }
    }

    // Check repeated failed payments
    if (eventType === 'payment.failed') {
      const failedPaymentTriggered = await this.checkFailedPaymentRule(userId);
      if (failedPaymentTriggered) {
        triggeredRules.push('FAILED_PAYMENTS');
      }
    }

    // Create alert if rules triggered
    if (triggeredRules.length > 0) {
      await this.createFraudAlert(userId, eventType, triggeredRules, context);
      return { triggered: true, rules: triggeredRules };
    }

    return { triggered: false, rules: [] };
  }

  /**
   * Check velocity rule: X bookings in Y minutes
   */
  private async checkVelocityRule(userId: string): Promise<boolean> {
    const rule = await this.prisma.fraudRule.findFirst({
      where: { type: 'VELOCITY', enabled: true },
    });

    if (!rule) return false;

    const timeWindowMinutes = (rule.config as any)?.timeWindowMinutes || 30;
    const threshold = rule.threshold;

    const bookingCount = await this.prisma.booking.count({
      where: {
        passengerId: userId,
        createdAt: {
          gte: new Date(Date.now() - timeWindowMinutes * 60 * 1000),
        },
      },
    });

    return bookingCount > threshold;
  }

  /**
   * Check high-value booking rule
   */
  private async checkHighValueRule(amountMinor: number): Promise<boolean> {
    const rule = await this.prisma.fraudRule.findFirst({
      where: { type: 'HIGH_VALUE', enabled: true },
    });

    if (!rule) return false;

    // threshold is in ETB (convert minor units to ETB)
    const amountEtb = amountMinor / 100;
    return amountEtb > rule.threshold;
  }

  /**
   * Check failed payment rule: X failed attempts in Y minutes
   */
  private async checkFailedPaymentRule(userId: string): Promise<boolean> {
    const rule = await this.prisma.fraudRule.findFirst({
      where: { type: 'FAILED_PAYMENTS', enabled: true },
    });

    if (!rule) return false;

    const timeWindowMinutes = (rule.config as any)?.timeWindowMinutes || 60;
    const threshold = rule.threshold;

    const failedCount = await this.prisma.paymentIntent.count({
      where: {
        booking: { passengerId: userId },
        status: 'FAILED',
        updatedAt: {
          gte: new Date(Date.now() - timeWindowMinutes * 60 * 1000),
        },
      },
    });

    return failedCount > threshold;
  }

  /**
   * Create a fraud alert
   */
  private async createFraudAlert(
    userId: string,
    eventType: string,
    triggeredRules: string[],
    context: Record<string, unknown>,
  ): Promise<void> {
    const alert = await this.prisma.fraudAlert.create({
      data: {
        userId,
        eventType,
        triggeredRules,
        context: context as any,
        severity: triggeredRules.length > 1 ? 'HIGH' : 'MEDIUM',
      },
    });

    this.logger.warn(`Fraud alert created: ${alert.id} for user ${userId} - rules: ${triggeredRules.join(', ')}`);

    // Trigger blocking if needed
    if (triggeredRules.includes('HIGH_VALUE') || triggeredRules.length > 1) {
      await this.blockUserTemporarily(userId, 30); // Block for 30 minutes
    }
  }

  /**
   * Block user temporarily
   */
  async blockUserTemporarily(userId: string, durationMinutes: number): Promise<void> {
    const blockedUntil = new Date(Date.now() + durationMinutes * 60 * 1000);
    await this.prisma.user.update({
      where: { id: userId },
      data: { blockedUntil },
    });
    this.logger.warn(`User ${userId} blocked until ${blockedUntil.toISOString()}`);
  }

  /**
   * Unblock user
   */
  async unblockUser(userId: string): Promise<void> {
    await this.prisma.user.update({
      where: { id: userId },
      data: { blockedUntil: null },
    });
    this.logger.log(`User ${userId} unblocked`);
  }

  /**
   * Get all fraud alerts
   */
  async getAlerts(userId?: string, limit = 100, offset = 0) {
    return this.prisma.fraudAlert.findMany({
      where: userId ? { userId } : {},
      orderBy: { createdAt: 'desc' },
      take: limit,
      skip: offset,
    });
  }

  /**
   * Create or update a fraud rule
   */
  async upsertRule(
    type: string,
    config: FraudRuleConfig,
  ) {
    return this.prisma.fraudRule.upsert({
      where: { type: type as any },
      update: {
        enabled: config.enabled,
        threshold: config.threshold,
        config: config as any,
      },
      create: {
        type: type as any,
        enabled: config.enabled,
        threshold: config.threshold,
        config: config as any,
      },
    });
  }

  /**
   * Get all fraud rules
   */
  async getRules() {
    return this.prisma.fraudRule.findMany();
  }

  /**
   * Event listener for booking created
   */
  @OnEvent('booking.created')
  async onBookingCreated(payload: { booking: any }) {
    await this.evaluateRules(payload.booking.passengerId, 'booking.created', {
      bookingId: payload.booking.id,
      amountMinor: payload.booking.totalMinor,
    });
  }

  /**
   * Event listener for payment failed
   */
  @OnEvent('payment.failed')
  async onPaymentFailed(payload: { intentId: string; userId: string }) {
    await this.evaluateRules(payload.userId, 'payment.failed', {
      intentId: payload.intentId,
    });
  }

  /**
   * Event listener for auth login failed
   */
  @OnEvent('auth.login.failed')
  async onLoginFailed(payload: { userId: string; email: string }) {
    await this.evaluateRules(payload.userId, 'auth.login.failed', {
      email: payload.email,
    });
  }
}
