import { logger } from '@goodgo/logger';
import { PrismaClient, RequestStatus } 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 { AccessRequestRepository } from '../../../repositories/access-request.repository';
import { CreateAccessRequestDto } from '../access.dto';

/**
 * EN: Access Request Service
 * VI: Service yêu cầu truy cập
 */
export class AccessRequestService {
  private prisma: PrismaClient;
  private requestRepo: AccessRequestRepository;

  constructor() {
    this.prisma = getPrismaClient();
    this.requestRepo = new AccessRequestRepository(this.prisma);
  }

  /**
   * EN: Create access request
   * VI: Tạo yêu cầu truy cập
   */
  async createRequest(userId: string, data: CreateAccessRequestDto, req?: Request): Promise<any> {
    const expiresAt = data.expiresAt ? new Date(data.expiresAt) : undefined;

    const accessRequest = await this.requestRepo.createRequest({
      userId,
      resource: data.resource,
      action: data.action,
      reason: data.reason,
      expiresAt,
      metadata: data.metadata,
    });

    // TODO: Determine approvers based on resource/action and add them
    // This would typically involve finding users with appropriate permissions
    // For now, we'll skip automatic approver assignment

    if (req) {
      await auditService.logFromRequest(req, 'ACCESS_REQUEST_CREATED', {
        requestId: accessRequest.id,
        userId,
        resource: data.resource,
        action: data.action,
        success: true,
      });
    }

    logger.info('Access request created', { requestId: accessRequest.id, userId });

    return accessRequest;
  }

  /**
   * EN: Get request by ID
   * VI: Lấy yêu cầu theo ID
   */
  async getRequest(id: string): Promise<any> {
    const request = await this.requestRepo.findWithDetails(id);
    if (!request) {
      throw new NotFoundError('Access Request');
    }
    return request;
  }

  /**
   * EN: Get user requests
   * VI: Lấy các yêu cầu của user
   */
  async getUserRequests(userId: string): Promise<any[]> {
    return this.requestRepo.findByUser(userId);
  }

  /**
   * EN: Approve access request
   * VI: Phê duyệt yêu cầu truy cập
   */
  async approveRequest(
    requestId: string,
    approverId: string,
    comments?: string,
    req?: Request
  ): Promise<any> {
    const request = await this.requestRepo.findById(requestId);
    if (!request) {
      throw new NotFoundError('Access Request');
    }

    if (request.status !== RequestStatus.PENDING) {
      throw new BadRequestError('Request is not pending / Yêu cầu không ở trạng thái chờ');
    }

    // Update approver status
    await this.requestRepo.updateApproverStatus(requestId, approverId, 'APPROVED', comments);

    // Check if all approvers have approved (for multi-approver scenarios)
    // For simplicity, we'll approve the request if at least one approver approves
    // In a real system, you'd check if all required approvers have approved

    // Update request status
    const updated = await this.requestRepo.updateStatus(requestId, RequestStatus.APPROVED, approverId);

    // Grant the requested access (this would typically call RBAC service)
    // For now, we'll just log it - actual permission granting would be implementation-specific
    // await rbacService.grantPermission(request.userId, { resource: request.resource, action: request.action });

    if (req) {
      await auditService.logFromRequest(req, 'ACCESS_REQUEST_APPROVED', {
        requestId,
        approverId,
        userId: request.userId,
        success: true,
      });
    }

    logger.info('Access request approved', { requestId, approverId });

    return updated;
  }

  /**
   * EN: Reject access request
   * VI: Từ chối yêu cầu truy cập
   */
  async rejectRequest(
    requestId: string,
    approverId: string,
    comments: string,
    req?: Request
  ): Promise<any> {
    const request = await this.requestRepo.findById(requestId);
    if (!request) {
      throw new NotFoundError('Access Request');
    }

    if (request.status !== RequestStatus.PENDING) {
      throw new BadRequestError('Request is not pending / Yêu cầu không ở trạng thái chờ');
    }

    // Update approver status
    await this.requestRepo.updateApproverStatus(requestId, approverId, 'REJECTED', comments);

    // Update request status
    const updated = await this.requestRepo.updateStatus(requestId, RequestStatus.REJECTED, approverId);

    if (req) {
      await auditService.logFromRequest(req, 'ACCESS_REQUEST_REJECTED', {
        requestId,
        approverId,
        userId: request.userId,
        success: true,
      });
    }

    logger.info('Access request rejected', { requestId, approverId });

    return updated;
  }

  /**
   * EN: Cancel access request
   * VI: Hủy yêu cầu truy cập
   */
  async cancelRequest(requestId: string, userId: string, req?: Request): Promise<boolean> {
    const request = await this.requestRepo.findById(requestId);
    if (!request) {
      throw new NotFoundError('Access Request');
    }

    if (request.userId !== userId) {
      throw new BadRequestError('Cannot cancel another user\'s request / Không thể hủy yêu cầu của user khác');
    }

    if (request.status !== RequestStatus.PENDING) {
      throw new BadRequestError('Only pending requests can be cancelled / Chỉ yêu cầu đang chờ mới có thể hủy');
    }

    await this.requestRepo.updateStatus(requestId, RequestStatus.CANCELLED, userId);

    if (req) {
      await auditService.logFromRequest(req, 'ACCESS_REQUEST_CANCELLED', {
        requestId,
        userId,
        success: true,
      });
    }

    logger.info('Access request cancelled', { requestId, userId });

    return true;
  }

  /**
   * EN: Get pending approvals for approver
   * VI: Lấy các yêu cầu đang chờ phê duyệt cho người phê duyệt
   */
  async getPendingApprovals(approverId: string): Promise<any[]> {
    return this.requestRepo.findPendingApprovals(approverId);
  }
}

export const accessRequestService = new AccessRequestService();
