import {
  chunkRenameCascadeTargets
} from "./chunk-YPTJJ35S.mjs";
import {
  buildMembershipWorkspaceProjectionSk,
  buildRoleAssignmentUserProjectionSkTenantLane,
  buildRoleAssignmentUserProjectionSkWorkspaceLane,
  buildRoleAssignmentWorkspaceProjectionSk
} from "./chunk-5S6VFBLT.mjs";
import {
  buildMembershipUserProjectionSkTenantLane,
  buildMembershipUserProjectionSkWorkspaceLane,
  extractReferenceSlug
} from "./chunk-I6LUPJUY.mjs";
import "./chunk-QJDHVMKT.mjs";
import "./chunk-FYHBHHWK.mjs";
import {
  getDynamoControlService
} from "./chunk-EUIP2U5F.mjs";
import "./chunk-TRY7JGWO.mjs";
import {
  require_lib
} from "./chunk-KMEWULMX.mjs";
import {
  __toESM
} from "./chunk-LZOMFHX3.mjs";

// src/workflows/control-plane/rename-cascade/rename-list-targets.handler.ts
import { randomUUID } from "crypto";

// src/data/operations/control/rename-cascade/rename-cascade-list-targets-operation.ts
var import_workflows = __toESM(require_lib());
var DEFAULT_PAGE_SIZE = 100;
var STREAMS_FOR_ENTITY_TYPE = {
  Tenant: ["membershipUserProjection", "roleAssignmentUserProjection"],
  User: [
    "membershipUserProjection",
    "roleAssignmentUserProjection",
    "membershipWorkspaceProjection",
    "roleAssignmentWorkspaceProjection"
  ],
  Role: ["roleAssignmentUserProjection", "roleAssignmentWorkspaceProjection"]
};
async function listRenameCascadeTargetsOperation(params) {
  const {
    entityType,
    entityId,
    tenantId,
    oldName,
    newName,
    oldNormalizedName,
    newNormalizedName,
    cursors = {},
    limit = DEFAULT_PAGE_SIZE,
    tableName
  } = params;
  if (!entityId || entityId.length === 0) {
    throw new Error("listRenameCascadeTargetsOperation: entityId is required");
  }
  switch (entityType) {
    case import_workflows.RENAMABLE_ENTITY_TYPE.User:
      return pageUserRename({
        userId: entityId,
        oldNormalizedName,
        newNormalizedName,
        newName,
        cursors,
        limit,
        tableName
      });
    case import_workflows.RENAMABLE_ENTITY_TYPE.Role:
      return pageRoleRename({
        roleId: entityId,
        tenantId,
        newName,
        cursors,
        limit,
        tableName
      });
    case import_workflows.RENAMABLE_ENTITY_TYPE.Tenant:
      return pageTenantRename({
        tenantId: entityId,
        oldName,
        newName,
        cursors,
        limit,
        tableName
      });
    default: {
      const exhaustive = entityType;
      throw new Error(
        `listRenameCascadeTargetsOperation: unsupported entityType '${String(
          exhaustive
        )}'`
      );
    }
  }
}
async function pageUserRename(params) {
  const { userId, newName, cursors, limit, tableName } = params;
  const service = getDynamoControlService(tableName);
  const nextCursors = {};
  const targets = [];
  const muStream = cursors.membershipUserProjection;
  if (muStream !== null) {
    const page = await service.entities.membershipUserProjection.query.record({ userId }).begins({ sk: "MEMBERSHIP#" }).go({ cursor: muStream ?? null, limit });
    for (const row of page.data ?? []) {
      const oldKey = { userId: row.userId, sk: row.sk };
      const newSk = row.sk;
      const newKey = { userId: row.userId, sk: newSk };
      targets.push({
        entity: "membershipUserProjection",
        oldKey,
        newKey,
        newItem: {
          ...row,
          sk: newSk,
          denormalizedUserName: newName
        },
        skRewriteRequired: false
      });
    }
    nextCursors.membershipUserProjection = page.cursor ?? null;
  } else {
    nextCursors.membershipUserProjection = null;
  }
  const raUStream = cursors.roleAssignmentUserProjection;
  if (raUStream !== null) {
    const page = await service.entities.roleAssignmentUserProjection.query.record({ userId }).begins({ sk: "ROLEASSIGNMENT#" }).go({ cursor: raUStream ?? null, limit });
    for (const row of page.data ?? []) {
      const oldKey = { userId: row.userId, sk: row.sk };
      const newKey = { userId: row.userId, sk: row.sk };
      targets.push({
        entity: "roleAssignmentUserProjection",
        oldKey,
        newKey,
        newItem: {
          ...row,
          denormalizedUserName: newName
        },
        skRewriteRequired: false
      });
    }
    nextCursors.roleAssignmentUserProjection = page.cursor ?? null;
  } else {
    nextCursors.roleAssignmentUserProjection = null;
  }
  const discoveryCursor = cursors.workspaceDiscovery;
  if (discoveryCursor !== null) {
    const discovery = await service.entities.membershipUserProjection.query.record({ userId }).begins({ sk: "MEMBERSHIP#WORKSPACE#" }).go({ cursor: discoveryCursor ?? null, limit });
    for (const member of discovery.data ?? []) {
      if (!member.workspaceId || !member.tenantId) {
        continue;
      }
      await collectWorkspaceUserRenameTargets({
        service,
        tenantId: member.tenantId,
        workspaceId: member.workspaceId,
        userId,
        oldNormalizedName: params.oldNormalizedName,
        newNormalizedName: params.newNormalizedName,
        newName,
        targets
      });
    }
    nextCursors.workspaceDiscovery = discovery.cursor ?? null;
  } else {
    nextCursors.workspaceDiscovery = null;
  }
  nextCursors.membershipWorkspaceProjection = null;
  nextCursors.roleAssignmentWorkspaceProjection = null;
  const exhausted = STREAMS_FOR_ENTITY_TYPE.User.every((s) => nextCursors[s] === null) && nextCursors.workspaceDiscovery === null;
  return { targets, cursors: nextCursors, exhausted };
}
async function collectWorkspaceUserRenameTargets(params) {
  const {
    service,
    tenantId,
    workspaceId,
    userId,
    oldNormalizedName,
    newName,
    targets
  } = params;
  const mwPage = await service.entities.membershipWorkspaceProjection.query.record({ tenantId, workspaceId }).begins({ sk: `MEMBERSHIP#${oldNormalizedName}#USER#${userId}#` }).go({});
  for (const row of mwPage.data ?? []) {
    const newSk = buildMembershipWorkspaceProjectionSk({
      userId: row.userId,
      membershipId: row.membershipId,
      denormalizedUserName: newName
    });
    targets.push({
      entity: "membershipWorkspaceProjection",
      oldKey: {
        tenantId: row.tenantId,
        workspaceId: row.workspaceId,
        sk: row.sk
      },
      newKey: {
        tenantId: row.tenantId,
        workspaceId: row.workspaceId,
        sk: newSk
      },
      newItem: {
        ...row,
        sk: newSk,
        denormalizedUserName: newName
      },
      skRewriteRequired: row.sk !== newSk
    });
  }
  const raPage = await service.entities.roleAssignmentWorkspaceProjection.query.record({ tenantId, workspaceId }).begins({ sk: "ROLEASSIGNMENT#" }).where((attr, op) => op.eq(attr.userId, userId)).go({});
  for (const row of raPage.data ?? []) {
    const newSk = buildRoleAssignmentWorkspaceProjectionSk({
      roleId: row.roleId,
      userId: row.userId,
      roleAssignmentId: row.roleAssignmentId,
      denormalizedUserName: newName
    });
    targets.push({
      entity: "roleAssignmentWorkspaceProjection",
      oldKey: {
        tenantId: row.tenantId,
        workspaceId: row.workspaceId,
        sk: row.sk
      },
      newKey: {
        tenantId: row.tenantId,
        workspaceId: row.workspaceId,
        sk: newSk
      },
      newItem: {
        ...row,
        sk: newSk,
        denormalizedUserName: newName
      },
      skRewriteRequired: row.sk !== newSk
    });
  }
}
async function pageRoleRename(params) {
  const { roleId, tenantId, newName, cursors, limit, tableName } = params;
  if (!tenantId) {
    throw new Error(
      "listRenameCascadeTargetsOperation: tenantId is required for Role rename"
    );
  }
  const service = getDynamoControlService(tableName);
  const nextCursors = {};
  const targets = [];
  const discoveryCursor = cursors.roleDiscovery;
  if (discoveryCursor !== null) {
    const page = await service.entities.roleAssignment.query.gsi1({ tenantId, gsi1Shard: "0" }).begins({ gsi1sk: `${roleId}#` }).go({ cursor: discoveryCursor ?? null, limit });
    for (const row of page.data ?? []) {
      const userId = extractUserIdFromResource(row.resource);
      if (userId === void 0) {
        continue;
      }
      await collectUserRoleRenameTargets({
        service,
        userId,
        roleId,
        newName,
        targets
      });
    }
    nextCursors.roleDiscovery = page.cursor ?? null;
  } else {
    nextCursors.roleDiscovery = null;
  }
  nextCursors.roleAssignmentUserProjection = null;
  nextCursors.roleAssignmentWorkspaceProjection = null;
  const exhausted = nextCursors.roleDiscovery === null;
  return { targets, cursors: nextCursors, exhausted };
}
async function collectUserRoleRenameTargets(params) {
  const { service, userId, roleId, newName, targets } = params;
  const userProjPage = await service.entities.roleAssignmentUserProjection.query.record({ userId }).begins({ sk: "ROLEASSIGNMENT#" }).where((attr, op) => op.eq(attr.roleId, roleId)).go({});
  for (const row of userProjPage.data ?? []) {
    const isWorkspaceLane = typeof row.workspaceId === "string" && row.workspaceId.length > 0;
    const newSk = isWorkspaceLane ? buildRoleAssignmentUserProjectionSkWorkspaceLane({
      tenantId: row.tenantId,
      workspaceId: row.workspaceId,
      roleId: row.roleId,
      roleAssignmentId: row.roleAssignmentId,
      denormalizedRoleName: newName
    }) : buildRoleAssignmentUserProjectionSkTenantLane({
      tenantId: row.tenantId,
      roleId: row.roleId,
      roleAssignmentId: row.roleAssignmentId,
      denormalizedRoleName: newName
    });
    targets.push({
      entity: "roleAssignmentUserProjection",
      oldKey: { userId: row.userId, sk: row.sk },
      newKey: { userId: row.userId, sk: newSk },
      newItem: {
        ...row,
        sk: newSk,
        denormalizedRoleName: newName
      },
      skRewriteRequired: row.sk !== newSk
    });
  }
}
async function pageTenantRename(params) {
  const { tenantId, newName, cursors, limit, tableName } = params;
  const service = getDynamoControlService(tableName);
  const nextCursors = {};
  const targets = [];
  const discoveryCursor = cursors.tenantDiscovery;
  if (discoveryCursor !== null) {
    const page = await service.entities.membership.query.gsi1({ tenantId, gsi1Shard: "0" }).go({ cursor: discoveryCursor ?? null, limit });
    for (const row of page.data ?? []) {
      const userId = extractUserIdFromResource(row.resource);
      if (userId === void 0) {
        continue;
      }
      const userPage = await service.entities.membershipUserProjection.query.record({ userId }).begins({ sk: `MEMBERSHIP#TENANT#` }).where((attr, op) => op.eq(attr.tenantId, tenantId)).go({});
      for (const userRow of userPage.data ?? []) {
        const newSk = buildMembershipUserProjectionSkTenantLane({
          tenantId: userRow.tenantId,
          membershipId: userRow.membershipId,
          denormalizedTenantName: newName
        });
        targets.push({
          entity: "membershipUserProjection",
          oldKey: { userId: userRow.userId, sk: userRow.sk },
          newKey: { userId: userRow.userId, sk: newSk },
          newItem: {
            ...userRow,
            sk: newSk,
            denormalizedTenantName: newName
          },
          skRewriteRequired: userRow.sk !== newSk
        });
      }
      const wsPage = await service.entities.membershipUserProjection.query.record({ userId }).begins({ sk: `MEMBERSHIP#WORKSPACE#TID#${tenantId}#` }).go({});
      for (const wsRow of wsPage.data ?? []) {
        const newSk = buildMembershipUserProjectionSkWorkspaceLane({
          tenantId: wsRow.tenantId,
          workspaceId: wsRow.workspaceId,
          membershipId: wsRow.membershipId,
          denormalizedWorkspaceName: wsRow.denormalizedWorkspaceName
        });
        targets.push({
          entity: "membershipUserProjection",
          oldKey: { userId: wsRow.userId, sk: wsRow.sk },
          newKey: { userId: wsRow.userId, sk: newSk },
          newItem: {
            ...wsRow,
            sk: newSk,
            denormalizedTenantName: newName
          },
          skRewriteRequired: wsRow.sk !== newSk
        });
      }
    }
    nextCursors.tenantDiscovery = page.cursor ?? null;
  } else {
    nextCursors.tenantDiscovery = null;
  }
  nextCursors.membershipUserProjection = null;
  nextCursors.roleAssignmentUserProjection = null;
  const exhausted = nextCursors.tenantDiscovery === null;
  return { targets, cursors: nextCursors, exhausted };
}
function extractUserIdFromResource(resource) {
  if (typeof resource !== "string" || resource.length === 0) {
    return void 0;
  }
  let parsed;
  try {
    parsed = JSON.parse(resource);
  } catch {
    return void 0;
  }
  if (!parsed || typeof parsed !== "object") {
    return void 0;
  }
  return extractReferenceSlug(parsed, "user");
}

// src/workflows/control-plane/rename-cascade/rename-list-targets.handler.ts
var handler = async (input) => {
  const cursors = {};
  if (input.cursors) {
    for (const [key, value] of Object.entries(input.cursors)) {
      cursors[key] = value;
    }
  }
  const page = await listRenameCascadeTargetsOperation({
    entityType: input.entityType,
    entityId: input.entityId,
    tenantId: input.tenantId,
    oldName: input.oldName,
    newName: input.newName,
    oldNormalizedName: input.oldNormalizedName,
    newNormalizedName: input.newNormalizedName,
    cursors
  });
  const chunks = chunkRenameCascadeTargets(
    page.targets
  ).map((targets) => ({
    entityType: input.entityType,
    entityId: input.entityId,
    tenantId: input.tenantId,
    targets,
    chunkToken: randomUUID()
  }));
  const priorRewritten = input.itemsRewritten ?? 0;
  const priorChunks = input.chunkCount ?? 0;
  const itemsRewritten = priorRewritten + page.targets.length;
  const chunkCount = priorChunks + chunks.length;
  return {
    entityType: input.entityType,
    entityId: input.entityId,
    tenantId: input.tenantId,
    oldName: input.oldName,
    newName: input.newName,
    oldNormalizedName: input.oldNormalizedName,
    newNormalizedName: input.newNormalizedName,
    cursors: page.cursors,
    chunks,
    exhausted: page.exhausted,
    itemsRewritten,
    chunkCount
  };
};
export {
  handler
};
//# sourceMappingURL=rename-list-targets.handler.mjs.map