import { Injectable, Logger } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '@core/database/prisma/prisma.service';

/**
 * IAM 配置变更审计（规则 §5.3.3.2）
 *
 * 对以下表的增删改必须调用 record()：
 * - platform_iam.data_scopes
 * - platform_iam.role_data_scope_rel
 * - platform_iam.role_permission_rel
 * - platform_iam.user_role_rel
 *
 * 另规则 §4.5 + §8 禁止事项 #13：Administrator 权限跳过必记审计（走 adminBypass）。
 */

export type IamAuditAction = 'CREATE' | 'UPDATE' | 'DELETE' | 'ADMIN_BYPASS';

export interface IamAuditContext {
  /**
   * 审计主体：通常是 userId（UUID）；
   * 异步任务 / system principal 场景允许 'system'（来源附加在 after.source）。
   */
  actor: string;
  action: IamAuditAction;
  resource: string; // e.g. 'RoleDataScope' / 'UserRole'
  targetId?: string;
  before?: any;
  after?: any;
  ip?: string;
  userAgent?: string;
}

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

  constructor(private readonly prisma: PrismaService) {}

  async record(ctx: IamAuditContext): Promise<void> {
    try {
      await this.prisma.iamAuditLog.create({
        data: {
          actor: ctx.actor,
          action: ctx.action,
          resource: ctx.resource,
          targetId: ctx.targetId,
          before: ctx.before ?? undefined,
          after: ctx.after ?? undefined,
          ip: ctx.ip,
          userAgent: ctx.userAgent,
        },
      });
    } catch (err: unknown) {
      // 审计失败不应阻塞业务，但必须日志告警
      const msg = err instanceof Error ? err.message : String(err);
      this.logger.error(
        `IAM 审计写入失败 actor=${ctx.actor} action=${ctx.action} resource=${ctx.resource}: ${msg}`,
      );
    }
  }

  /**
   * 查询审计日志（IAM 后台用）。
   *
   * 支持按 actor / action / resource / 时间范围过滤，分页返回。
   */
  async query(filters: {
    actor?: string;
    action?: IamAuditAction;
    resource?: string;
    from?: Date;
    to?: Date;
    page?: number;
    pageSize?: number;
  }) {
    const page = filters.page && filters.page > 0 ? filters.page : 1;
    const pageSize =
      filters.pageSize && filters.pageSize > 0 && filters.pageSize <= 200
        ? filters.pageSize
        : 50;

    const where: Prisma.IamAuditLogWhereInput = {};
    if (filters.actor) where.actor = filters.actor;
    if (filters.action) where.action = filters.action;
    if (filters.resource) where.resource = filters.resource;
    if (filters.from || filters.to) {
      where.timestamp = {};
      if (filters.from) where.timestamp.gte = filters.from;
      if (filters.to) where.timestamp.lte = filters.to;
    }

    const [items, total] = await Promise.all([
      this.prisma.iamAuditLog.findMany({
        where,
        orderBy: { timestamp: 'desc' },
        skip: (page - 1) * pageSize,
        take: pageSize,
      }),
      this.prisma.iamAuditLog.count({ where }),
    ]);

    return { items, total, page, pageSize };
  }

  /**
   * Administrator 权限跳过的审计（规则 §5.3.3.2 + §4.5）
   */
  async recordAdminBypass(
    actor: string,
    endpoint: string,
    requiredPermissions: string[],
  ): Promise<void> {
    await this.record({
      actor,
      action: 'ADMIN_BYPASS',
      resource: 'PermissionsGuard',
      after: { endpoint, requiredPermissions },
    });
  }
}
