import {
  BadRequestException,
  ConflictException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { DataScopeType, Prisma } from '@prisma/client';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { IamAuditService } from '@common/services/iam-audit.service';
import { SkipAssertAccess } from '@common/decorators/skip-assert-access.decorator';

interface CreateDataScopeInput {
  code: string;
  name: string;
  scopeType: DataScopeType;
  rules?: Prisma.InputJsonValue | null | undefined;
}

interface UpdateDataScopeInput {
  name?: string;
  scopeType?: DataScopeType;
  rules?: Prisma.InputJsonValue | null | undefined;
}

interface BindRoleDataScopeInput {
  roleId: string;
  dataScopeId: string;
  resource: string;
}

/**
 * IAM 后台：DataScope 定义 + RoleDataScope 绑定的管理操作。
 *
 * - CUSTOM scopeType 永久禁用（规则 §5.3.1 / PRD §3）
 * - 内置 DataScope（isBuiltIn=true）禁止删除
 * - 所有写操作通过 IamAuditService 落审计（规则 §5.3.3.2）
 */
@Injectable()
export class DataScopeAdminService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly audit: IamAuditService,
  ) {}

  // ---------------- DataScope CRUD ----------------

  list() {
    return this.prisma.dataScope.findMany({ orderBy: { code: 'asc' } });
  }

  async listByRole(roleId: string) {
    return this.prisma.roleDataScope.findMany({
      where: { roleId },
      include: { dataScope: true },
      orderBy: { resource: 'asc' },
    });
  }

  async create(input: CreateDataScopeInput, actor: string) {
    this.assertNotCustom(input.scopeType);

    const exists = await this.prisma.dataScope.findUnique({
      where: { code: input.code },
    });
    if (exists) {
      throw new ConflictException(`DataScope code ${input.code} 已存在`);
    }

    const created = await this.prisma.dataScope.create({
      data: {
        code: input.code,
        name: input.name,
        scopeType: input.scopeType,
        rules: input.rules ?? Prisma.JsonNull,
        isBuiltIn: false,
      },
    });

    await this.audit.record({
      actor,
      action: 'CREATE',
      resource: 'DataScope',
      targetId: created.id,
      after: created,
    });

    return created;
  }

  @SkipAssertAccess('IAM 配置表：DataScope 是全局 IAM 元数据，无业务归属，鉴权走 iam_admin:manage')
  async update(id: string, input: UpdateDataScopeInput, actor: string) {
    if (input.scopeType !== undefined) this.assertNotCustom(input.scopeType);

    const before = await this.prisma.dataScope.findUnique({ where: { id } });
    if (!before) throw new NotFoundException('DataScope 不存在');

    const updated = await this.prisma.dataScope.update({
      where: { id },
      data: {
        name: input.name ?? undefined,
        scopeType: input.scopeType ?? undefined,
        rules:
          input.rules === undefined
            ? undefined
            : input.rules === null
              ? Prisma.JsonNull
              : input.rules,
      },
    });

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

    return updated;
  }

  @SkipAssertAccess('IAM 配置表：DataScope 是全局 IAM 元数据，无业务归属，鉴权走 iam_admin:manage')
  async remove(id: string, actor: string) {
    const [before, usedBy] = await Promise.all([
      this.prisma.dataScope.findUnique({ where: { id } }),
      this.prisma.roleDataScope.count({ where: { dataScopeId: id } }),
    ]);
    if (!before) throw new NotFoundException('DataScope 不存在');
    if (before.isBuiltIn) {
      throw new BadRequestException('内置 DataScope 不允许删除');
    }

    if (usedBy > 0) {
      throw new ConflictException(
        `DataScope 已绑定 ${usedBy} 个角色，请先解绑后再删除`,
      );
    }

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

    await this.audit.record({
      actor,
      action: 'DELETE',
      resource: 'DataScope',
      targetId: id,
      before,
    });

    return { ok: true };
  }

  // ---------------- RoleDataScope 绑定 ----------------

  async listMatrix(roleId?: string) {
    return this.prisma.roleDataScope.findMany({
      where: roleId ? { roleId } : undefined,
      include: { dataScope: true, role: true },
      orderBy: [{ roleId: 'asc' }, { resource: 'asc' }],
    });
  }

  async bind(input: BindRoleDataScopeInput, actor: string) {
    const [role, scope] = await Promise.all([
      this.prisma.role.findUnique({ where: { id: input.roleId } }),
      this.prisma.dataScope.findUnique({ where: { id: input.dataScopeId } }),
    ]);
    if (!role) throw new NotFoundException('Role 不存在');
    if (!scope) throw new NotFoundException('DataScope 不存在');

    const existing = await this.prisma.roleDataScope.findUnique({
      where: {
        roleId_dataScopeId_resource: {
          roleId: input.roleId,
          dataScopeId: input.dataScopeId,
          resource: input.resource,
        },
      },
    });
    if (existing) {
      throw new ConflictException('该角色已绑定相同 DataScope 与 resource');
    }

    const created = await this.prisma.roleDataScope.create({
      data: {
        roleId: input.roleId,
        dataScopeId: input.dataScopeId,
        resource: input.resource,
      },
    });

    await this.audit.record({
      actor,
      action: 'CREATE',
      resource: 'RoleDataScope',
      targetId: created.id,
      after: created,
    });

    return created;
  }

  @SkipAssertAccess('IAM 配置表：RoleDataScope 是角色绑定关系，无业务归属，鉴权走 iam_admin:manage')
  async unbind(id: string, actor: string) {
    const before = await this.prisma.roleDataScope.findUnique({ where: { id } });
    if (!before) throw new NotFoundException('绑定关系不存在');

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

    await this.audit.record({
      actor,
      action: 'DELETE',
      resource: 'RoleDataScope',
      targetId: id,
      before,
    });

    return { ok: true };
  }

  private assertNotCustom(scopeType: DataScopeType) {
    if (scopeType === DataScopeType.CUSTOM) {
      throw new BadRequestException(
        'CUSTOM DataScope 被规则永久禁用（§5.3.1）',
      );
    }
  }
}
