import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { AuthCacheService } from '@modules/organization/auth/services/auth-cache.service';
import { IamAuditService } from './iam-audit.service';
import { SkipAssertAccess } from '../decorators/skip-assert-access.decorator';

/**
 * Access Review（规则 §5.3.15）
 *
 * - 每条 UserRole 90 天内必须复核一次
 * - 超期 30 天（总 120 天）自动告警
 * - 复核动作必审计
 */

const REVIEW_PERIOD_DAYS = 90;
const ALERT_GRACE_DAYS = 30;

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

  constructor(
    private readonly prisma: PrismaService,
    private readonly authCache: AuthCacheService,
    private readonly audit: IamAuditService,
  ) {}

  /**
   * 列出待复核的 UserRole（超过 reviewPeriodDays 天未复核）
   */
  async listPending(reviewPeriodDays = REVIEW_PERIOD_DAYS) {
    const cutoff = new Date();
    cutoff.setDate(cutoff.getDate() - reviewPeriodDays);

    return this.prisma.userRole.findMany({
      where: {
        OR: [
          { lastReviewedAt: null, createdAt: { lte: cutoff } },
          { lastReviewedAt: { lte: cutoff } },
        ],
      },
      include: {
        user: { select: { id: true, username: true, displayName: true } },
        role: { select: { id: true, code: true, name: true } },
      },
    });
  }

  /**
   * 模块 owner 复核某条 UserRole："保留"
   *
   * 写的只是 lastReviewedAt/lastReviewedBy 审计字段；复核操作本身需要
   * 调用层（Controller）基于 system:admin 或 模块 owner 关系做鉴权，
   * 不是常规业务 DataScope 校验。
   */
  @SkipAssertAccess('IAM 治理：复核 UserRole 的权限校验走 Controller 层（模块 owner / system:admin）')
  async approve(userRoleId: string, reviewer: string, comment?: string) {
    const before = await this.prisma.userRole.findUnique({
      where: { id: userRoleId },
    });
    if (!before) throw new NotFoundException('UserRole 不存在');

    const updated = await this.prisma.userRole.update({
      where: { id: userRoleId },
      data: {
        lastReviewedAt: new Date(),
        lastReviewedBy: reviewer,
        reviewComment: comment ?? null,
      },
    });

    await this.audit.record({
      actor: reviewer,
      action: 'UPDATE',
      resource: 'UserRole',
      targetId: userRoleId,
      before,
      after: updated,
    });

    return updated;
  }

  /**
   * 模块 owner 复核某条 UserRole："撤销"
   *
   * 权限校验同 approve，走 Controller 层。
   */
  @SkipAssertAccess('IAM 治理：撤销 UserRole 的权限校验走 Controller 层（模块 owner / system:admin）')
  async revoke(userRoleId: string, reviewer: string, comment?: string) {
    const before = await this.prisma.userRole.findUnique({
      where: { id: userRoleId },
    });
    if (!before) throw new NotFoundException('UserRole 不存在');

    await this.prisma.userRole.delete({ where: { id: userRoleId } });

    // 失效用户缓存，确保撤销立即生效
    await this.authCache.invalidate(before.userId);

    await this.audit.record({
      actor: reviewer,
      action: 'DELETE',
      resource: 'UserRole',
      targetId: userRoleId,
      before,
      after: { reviewComment: comment ?? null },
    });
  }

  /**
   * 定时任务入口：扫描超期 + 触发告警
   * （具体接入 @nestjs/schedule 由 AppModule 的 ScheduleModule 处理；此处只提供逻辑）
   */
  async scanAndAlert() {
    const alertCutoff = REVIEW_PERIOD_DAYS + ALERT_GRACE_DAYS;
    const overdue = await this.listPending(alertCutoff);
    if (overdue.length > 0) {
      this.logger.warn(
        `Access Review 超期未复核 ${overdue.length} 条（超 ${alertCutoff} 天），需 HR/模块 owner 介入`,
      );
      // TODO: 挂接告警通道（Notification / 邮件 / Slack）；当前仅日志
    }
    return overdue;
  }
}
