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

import { getPrismaClient } from '../../../config/database.config';
import { auditService } from '../../../core/events/audit.service';
import { NotFoundError, BadRequestError } from '../../../errors/http-error';
import { AccessReviewRepository } from '../../../repositories/access-review.repository';
import { rbacService } from '../../rbac/rbac.service';
import { CreateAccessReviewDto } from '../access.dto';

/**
 * EN: Access Review Service
 * VI: Service đánh giá truy cập
 */
export class AccessReviewService {
  private prisma: PrismaClient;
  private reviewRepo: AccessReviewRepository;

  constructor() {
    this.prisma = getPrismaClient();
    this.reviewRepo = new AccessReviewRepository(this.prisma);
  }

  /**
   * EN: Create access review
   * VI: Tạo đánh giá truy cập
   */
  async createReview(data: CreateAccessReviewDto, createdBy: string, req?: Request): Promise<any> {
    const startDate = new Date(data.startDate);
    const endDate = new Date(data.endDate);

    if (endDate <= startDate) {
      throw new BadRequestError('End date must be after start date / Ngày kết thúc phải sau ngày bắt đầu');
    }

    const review = await this.reviewRepo.createReview({
      name: data.name,
      description: data.description,
      type: data.type,
      startDate,
      endDate,
      createdBy,
    });

    if (req) {
      await auditService.logFromRequest(req, 'ACCESS_REVIEW_CREATED', {
        reviewId: review.id,
        createdBy,
        success: true,
      });
    }

    logger.info('Access review created', { reviewId: review.id });

    return review;
  }

  /**
   * EN: Get review by ID
   * VI: Lấy đánh giá theo ID
   */
  async getReview(id: string): Promise<any> {
    const review = await this.reviewRepo.findWithItems(id);
    if (!review) {
      throw new NotFoundError('Access Review');
    }
    return review;
  }

  /**
   * EN: Start review
   * VI: Bắt đầu đánh giá
   */
  async startReview(id: string, req?: Request): Promise<any> {
    const review = await this.reviewRepo.findById(id);
    if (!review) {
      throw new NotFoundError('Access Review');
    }

    if (review.status !== ReviewStatus.DRAFT) {
      throw new BadRequestError('Only draft reviews can be started / Chỉ đánh giá ở trạng thái draft mới có thể bắt đầu');
    }

    const updated = await this.reviewRepo.updateStatus(id, ReviewStatus.ACTIVE);

    // Generate review items if not already generated
    const items = await this.reviewRepo.getItems(id);
    if (items.length === 0) {
      await this.generateReviewItems(id);
    }

    if (req) {
      await auditService.logFromRequest(req, 'ACCESS_REVIEW_STARTED', {
        reviewId: id,
        success: true,
      });
    }

    logger.info('Access review started', { reviewId: id });

    return updated;
  }

  /**
   * EN: Complete review
   * VI: Hoàn thành đánh giá
   */
  async completeReview(id: string, req?: Request): Promise<any> {
    const review = await this.reviewRepo.findById(id);
    if (!review) {
      throw new NotFoundError('Access Review');
    }

    if (review.status !== ReviewStatus.ACTIVE) {
      throw new BadRequestError('Only active reviews can be completed / Chỉ đánh giá đang hoạt động mới có thể hoàn thành');
    }

    const updated = await this.reviewRepo.updateStatus(id, ReviewStatus.COMPLETED, new Date());

    if (req) {
      await auditService.logFromRequest(req, 'ACCESS_REVIEW_COMPLETED', {
        reviewId: id,
        success: true,
      });
    }

    logger.info('Access review completed', { reviewId: id });

    return updated;
  }

  /**
   * EN: Generate review items (auto-generate based on current access)
   * VI: Tạo các items đánh giá (tự động tạo dựa trên truy cập hiện tại)
   */
  async generateReviewItems(reviewId: string): Promise<number> {
    const review = await this.reviewRepo.findById(reviewId);
    if (!review) {
      throw new NotFoundError('Access Review');
    }

    // Get all users with their current permissions
    const users = await this.prisma.user.findMany({
      where: { isActive: true },
      include: {
        userRoles: {
          include: {
            role: {
              include: {
                permissions: {
                  include: { permission: true },
                },
              },
            },
          },
        },
        userPermissions: {
          include: { permission: true },
        },
      },
    });

    const items: Array<{
      reviewId: string;
      userId: string;
      resource: string;
      access: any;
    }> = [];

    // Generate items for each user's access
    for (const user of users) {
      const permissions = await rbacService.getUserPermissions(user.id);
      
      // Group permissions by resource
      // EN: Permissions are in format "resource:action:scope"
      // VI: Permissions có định dạng "resource:action:scope"
      const resourceMap = new Map<string, any>();
      for (const permStr of permissions) {
        const [resource, action, scope] = permStr.split(':');
        if (!resource) continue;
        
        const key = resource;
        if (!resourceMap.has(key)) {
          resourceMap.set(key, {
            resource,
            actions: [],
          });
        }
        resourceMap.get(key).actions.push({
          action: action || 'read',
          scope: scope || 'all',
        });
      }

      // Create items for each resource
      for (const [resource, access] of resourceMap.entries()) {
        items.push({
          reviewId,
          userId: user.id,
          resource,
          access,
        });
      }
    }

    const count = await this.reviewRepo.createItems(items);

    logger.info('Review items generated', { reviewId, count });

    return count;
  }

  /**
   * EN: Get review items
   * VI: Lấy các items đánh giá
   */
  async getReviewItems(reviewId: string): Promise<any[]> {
    return this.reviewRepo.getItems(reviewId);
  }

  /**
   * EN: Review item
   * VI: Đánh giá item
   */
  async reviewItem(
    itemId: string,
    reviewerId: string,
    status: ReviewItemStatus,
    comments?: string,
    req?: Request
  ): Promise<any> {
    const item = await this.prisma.accessReviewItem.findUnique({ where: { id: itemId } });
    if (!item) {
      throw new NotFoundError('Review Item');
    }

    const review = await this.reviewRepo.findById(item.reviewId);
    if (!review) {
      throw new NotFoundError('Access Review');
    }

    if (review.status !== ReviewStatus.ACTIVE) {
      throw new BadRequestError('Review is not active / Đánh giá không đang hoạt động');
    }

    const updated = await this.reviewRepo.updateItemStatus(itemId, status, reviewerId, comments);

    // If status is REVOKED, revoke the access (implementation-specific)
    // This would typically involve calling RBAC service to remove permissions
    // await rbacService.revokeAccess(item.userId, item.resource, item.access);

    if (req) {
      await auditService.logFromRequest(req, 'REVIEW_ITEM_REVIEWED', {
        itemId,
        reviewId: item.reviewId,
        status,
        success: true,
      });
    }

    logger.info('Review item reviewed', { itemId, status });

    return updated;
  }

  /**
   * EN: Get user review items
   * VI: Lấy các items đánh giá của user
   */
  async getUserReviewItems(userId: string, reviewId: string): Promise<any[]> {
    return this.reviewRepo.getUserItems(userId, reviewId);
  }

  /**
   * EN: List reviews
   * VI: Liệt kê các đánh giá
   */
  async list(status?: ReviewStatus): Promise<any[]> {
    if (status) {
      return this.reviewRepo.findByStatus(status);
    }
    return this.reviewRepo.findAll({ orderBy: { createdAt: 'desc' } });
  }

  /**
   * EN: Get review statistics
   * VI: Lấy thống kê đánh giá
   */
  async getReviewStats(reviewId: string): Promise<any> {
    return this.reviewRepo.getReviewStats(reviewId);
  }
}

export const accessReviewService = new AccessReviewService();
