import { logger } from '@goodgo/logger';
import { PrismaClient, RiskLevel } from '@prisma/client';
import { Request } from 'express';

import { getPrismaClient } from '../../../config/database.config';
import { auditService } from '../../../core/events/audit.service';
import { NotFoundError } from '../../../errors/http-error';
import { RiskFiltersDto } from '../governance.dto';

/**
 * EN: Risk Management Service
 * VI: Service quản lý rủi ro
 */
export class RiskService {
  private prisma: PrismaClient;

  constructor() {
    this.prisma = getPrismaClient();
  }

  /**
   * EN: Calculate risk score for user
   * VI: Tính điểm rủi ro cho user
   */
  async calculateRiskScore(userId: string, factors?: Record<string, any>, req?: Request): Promise<any> {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
      include: {
        userRoles: true,
        userPermissions: true,
        authEvents: {
          take: 100,
          orderBy: { timestamp: 'desc' },
        },
      },
    });

    if (!user) {
      throw new NotFoundError('User');
    }

    let score = 0;
    const riskFactors: any = {};

    // Factor 1: Failed login attempts
    const failedLogins = user.authEvents.filter((e) => e.eventType === 'LOGIN_FAILED' && !e.success).length;
    if (failedLogins > 5) {
      score += 20;
      riskFactors.failedLogins = failedLogins;
    }

    // Factor 2: Number of roles/permissions
    const roleCount = user.userRoles.length;
    const permCount = user.userPermissions.length;
    if (roleCount > 5 || permCount > 20) {
      score += 15;
      riskFactors.excessiveAccess = { roles: roleCount, permissions: permCount };
    }

    // Factor 3: Last login
    if (user.lastLoginAt) {
      const daysSinceLogin = Math.floor((Date.now() - user.lastLoginAt.getTime()) / (1000 * 60 * 60 * 24));
      if (daysSinceLogin > 90) {
        score += 10;
        riskFactors.inactiveAccount = daysSinceLogin;
      }
    }

    // Factor 4: Custom factors from input
    if (factors) {
      Object.assign(riskFactors, factors);
      if (factors.additionalRisk) {
        score += factors.additionalRisk;
      }
    }

    // Determine risk level
    let level: RiskLevel = 'LOW';
    if (score >= 70) level = 'CRITICAL';
    else if (score >= 50) level = 'HIGH';
    else if (score >= 30) level = 'MEDIUM';

    // Store risk score
    const riskScore = await this.prisma.riskScore.create({
      data: {
        userId,
        score: Math.min(100, score), // Cap at 100
        level,
        factors: riskFactors,
        calculatedAt: new Date(),
      },
      include: {
        user: {
          select: {
            id: true,
            email: true,
            username: true,
          },
        },
      },
    });

    if (req) {
      await auditService.logFromRequest(req, 'RISK_SCORE_CALCULATED', {
        userId,
        score: riskScore.score,
        level,
        success: true,
      });
    }

    logger.info('Risk score calculated', { userId, score: riskScore.score, level });

    return riskScore;
  }

  /**
   * EN: Get risk score for user
   * VI: Lấy điểm rủi ro cho user
   */
  async getRiskScore(userId: string): Promise<any> {
    const riskScore = await this.prisma.riskScore.findFirst({
      where: { userId },
      orderBy: { calculatedAt: 'desc' },
      include: {
        user: {
          select: {
            id: true,
            email: true,
            username: true,
          },
        },
      },
    });

    if (!riskScore) {
      // Calculate if not exists
      return this.calculateRiskScore(userId);
    }

    return riskScore;
  }

  /**
   * EN: Get risk scores with filters
   * VI: Lấy các điểm rủi ro với filters
   */
  async getRiskScores(filters: RiskFiltersDto): Promise<{ scores: any[]; total: number }> {
    const where: any = {};

    if (filters.minScore !== undefined) {
      where.score = { ...where.score, gte: filters.minScore };
    }

    if (filters.maxScore !== undefined) {
      where.score = { ...where.score, lte: filters.maxScore };
    }

    if (filters.level) {
      where.level = filters.level;
    }

    if (filters.userId) {
      where.userId = filters.userId;
    }

    const [scores, total] = await Promise.all([
      this.prisma.riskScore.findMany({
        where,
        skip: filters.skip,
        take: filters.take,
        orderBy: { calculatedAt: 'desc' },
        include: {
          user: {
            select: {
              id: true,
              email: true,
              username: true,
            },
          },
        },
      }),
      this.prisma.riskScore.count({ where }),
    ]);

    return { scores, total };
  }

  /**
   * EN: Get high risk users
   * VI: Lấy các users có rủi ro cao
   */
  async getHighRiskUsers(threshold: number = 50): Promise<any[]> {
    return this.prisma.riskScore.findMany({
      where: {
        score: { gte: threshold },
        level: { in: ['HIGH', 'CRITICAL'] },
      },
      include: {
        user: {
          select: {
            id: true,
            email: true,
            username: true,
            isActive: true,
          },
        },
      },
      orderBy: { score: 'desc' },
      take: 100,
    });
  }

  /**
   * EN: Get risk factors for user
   * VI: Lấy các yếu tố rủi ro cho user
   */
  async getRiskFactors(userId: string): Promise<any> {
    const riskScore = await this.getRiskScore(userId);
    return riskScore.factors;
  }
}

export const riskService = new RiskService();
