import { registerCommand } from "@open-mercato/shared/lib/commands";
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects, parseWithCustomFields, setCustomFieldsIfAny } from "@open-mercato/shared/lib/commands/helpers";
import { buildCustomFieldResetMap, diffCustomFieldChanges, loadCustomFieldSnapshot } from "@open-mercato/shared/lib/commands/customFieldSnapshots";
import { makeCreateRedo } from "@open-mercato/shared/lib/commands/redo";
import { StaffTeam, StaffTeamMember, StaffTeamRole } from "../data/entities.js";
import {
  staffTeamRoleCreateSchema,
  staffTeamRoleUpdateSchema
} from "../data/validators.js";
import { staffTeamRoleCrudEvents } from "../lib/crud.js";
import {
  applyScopeToWhere,
  commandActorScope,
  commandInputScope,
  ensureOrganizationScope,
  ensureTenantScope,
  extractUndoPayload,
  scopeForDecryption,
  scopedStaffSnapshotWhere,
  staffSnapshotDecryptionScope,
  staffSnapshotScopeFromContext,
  staffSnapshotScopeFromSnapshot
} from "./shared.js";
import { E } from "../../../generated/entities.ids.generated.js";
const teamRoleCrudIndexer = {
  entityType: E.staff.staff_team_role
};
async function loadTeamRoleSnapshot(em, id, scope) {
  const role = await findOneWithDecryption(
    em,
    StaffTeamRole,
    scopedStaffSnapshotWhere(id, scope),
    void 0,
    staffSnapshotDecryptionScope(scope)
  );
  if (!role) return null;
  return {
    id: role.id,
    tenantId: role.tenantId,
    organizationId: role.organizationId,
    teamId: role.teamId ?? null,
    name: role.name,
    description: role.description ?? null,
    appearanceIcon: role.appearanceIcon ?? null,
    appearanceColor: role.appearanceColor ?? null,
    deletedAt: role.deletedAt ? role.deletedAt.toISOString() : null
  };
}
async function loadTeamRoleCustomSnapshot(em, snapshot) {
  return loadCustomFieldSnapshot(em, {
    entityId: E.staff.staff_team_role,
    recordId: snapshot.id,
    tenantId: snapshot.tenantId,
    organizationId: snapshot.organizationId
  });
}
const redoTeamRoleCreate = makeCreateRedo({
  entityClass: StaffTeamRole,
  buildResult: (entity) => ({ roleId: entity.id }),
  events: staffTeamRoleCrudEvents,
  indexer: teamRoleCrudIndexer
});
async function ensureTeamExists(em, teamId, tenantId, organizationId) {
  const team = await findOneWithDecryption(
    em,
    StaffTeam,
    { id: teamId, tenantId, organizationId, deletedAt: null },
    void 0,
    { tenantId, organizationId }
  );
  if (!team) throw new CrudHttpError(400, { error: "Team not found." });
}
const createTeamRoleCommand = {
  id: "staff.team-roles.create",
  async execute(rawInput, ctx) {
    const { parsed, custom } = parseWithCustomFields(staffTeamRoleCreateSchema, rawInput);
    ensureTenantScope(ctx, parsed.tenantId);
    ensureOrganizationScope(ctx, parsed.organizationId);
    commandInputScope(ctx, parsed.tenantId, parsed.organizationId);
    const em = ctx.container.resolve("em").fork();
    if (parsed.teamId) {
      await ensureTeamExists(em, parsed.teamId, parsed.tenantId, parsed.organizationId);
    }
    const now = /* @__PURE__ */ new Date();
    const role = em.create(StaffTeamRole, {
      tenantId: parsed.tenantId,
      organizationId: parsed.organizationId,
      teamId: parsed.teamId ?? null,
      name: parsed.name,
      description: parsed.description ?? null,
      appearanceIcon: parsed.appearanceIcon ?? null,
      appearanceColor: parsed.appearanceColor ?? null,
      createdAt: now,
      updatedAt: now,
      deletedAt: null
    });
    em.persist(role);
    await em.flush();
    const de = ctx.container.resolve("dataEngine");
    await setCustomFieldsIfAny({
      dataEngine: de,
      entityId: E.staff.staff_team_role,
      recordId: role.id,
      tenantId: role.tenantId,
      organizationId: role.organizationId,
      values: custom
    });
    await emitCrudSideEffects({
      dataEngine: de,
      action: "created",
      entity: role,
      identifiers: {
        id: role.id,
        organizationId: role.organizationId,
        tenantId: role.tenantId
      },
      events: staffTeamRoleCrudEvents,
      indexer: teamRoleCrudIndexer
    });
    return { roleId: role.id };
  },
  captureAfter: async (_input, result, ctx) => {
    const em = ctx.container.resolve("em").fork();
    const snapshot = await loadTeamRoleSnapshot(em, result.roleId, staffSnapshotScopeFromContext(ctx));
    if (!snapshot) return null;
    const custom = await loadTeamRoleCustomSnapshot(em, snapshot);
    return { snapshot, custom };
  },
  buildLog: async ({ result, ctx }) => {
    const em = ctx.container.resolve("em").fork();
    const snapshot = await loadTeamRoleSnapshot(em, result.roleId, staffSnapshotScopeFromContext(ctx));
    if (!snapshot) return null;
    const custom = await loadTeamRoleCustomSnapshot(em, snapshot);
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("staff.audit.teamRoles.create", "Create team role"),
      resourceKind: "staff.teamRole",
      resourceId: snapshot.id,
      tenantId: snapshot.tenantId,
      organizationId: snapshot.organizationId,
      snapshotAfter: snapshot,
      payload: {
        undo: {
          after: snapshot,
          customAfter: custom
        }
      }
    };
  },
  undo: async ({ logEntry, ctx }) => {
    const payload = extractUndoPayload(logEntry);
    const after = payload?.after;
    if (!after) return;
    const em = ctx.container.resolve("em").fork();
    const role = await em.findOne(StaffTeamRole, scopedStaffSnapshotWhere(after.id, staffSnapshotScopeFromSnapshot(after)));
    if (role) {
      role.deletedAt = /* @__PURE__ */ new Date();
      await em.flush();
      const de = ctx.container.resolve("dataEngine");
      await emitCrudUndoSideEffects({
        dataEngine: de,
        action: "deleted",
        entity: role,
        identifiers: {
          id: role.id,
          organizationId: role.organizationId,
          tenantId: role.tenantId
        },
        events: staffTeamRoleCrudEvents,
        indexer: teamRoleCrudIndexer
      });
    }
  },
  redo: async ({ logEntry, ctx }) => {
    const result = await redoTeamRoleCreate({ input: void 0, ctx, logEntry });
    const payload = extractUndoPayload(logEntry);
    const customAfter = payload?.customAfter;
    if (customAfter && Object.keys(customAfter).length) {
      await setCustomFieldsIfAny({
        dataEngine: ctx.container.resolve("dataEngine"),
        entityId: E.staff.staff_team_role,
        recordId: result.roleId,
        tenantId: payload?.after?.tenantId ?? "",
        organizationId: payload?.after?.organizationId ?? "",
        values: customAfter
      });
    }
    return result;
  }
};
const updateTeamRoleCommand = {
  id: "staff.team-roles.update",
  async prepare(rawInput, ctx) {
    const { parsed } = parseWithCustomFields(staffTeamRoleUpdateSchema, rawInput);
    const em = ctx.container.resolve("em");
    const snapshot = await loadTeamRoleSnapshot(em, parsed.id, staffSnapshotScopeFromContext(ctx));
    if (!snapshot) return {};
    const custom = await loadTeamRoleCustomSnapshot(em, snapshot);
    return { before: snapshot, customBefore: custom };
  },
  async execute(rawInput, ctx) {
    const { parsed, custom } = parseWithCustomFields(staffTeamRoleUpdateSchema, rawInput);
    const em = ctx.container.resolve("em").fork();
    const scope = commandActorScope(ctx);
    const role = await findOneWithDecryption(
      em,
      StaffTeamRole,
      applyScopeToWhere({ id: parsed.id, deletedAt: null }, scope),
      void 0,
      scopeForDecryption(scope)
    );
    if (!role) throw new CrudHttpError(404, { error: "Team role not found." });
    ensureTenantScope(ctx, role.tenantId);
    ensureOrganizationScope(ctx, role.organizationId);
    if (parsed.teamId !== void 0) {
      if (parsed.teamId) {
        await ensureTeamExists(em, parsed.teamId, role.tenantId, role.organizationId);
      }
      role.teamId = parsed.teamId ?? null;
    }
    if (parsed.name !== void 0) role.name = parsed.name;
    if (parsed.description !== void 0) role.description = parsed.description ?? null;
    if (parsed.appearanceIcon !== void 0) role.appearanceIcon = parsed.appearanceIcon ?? null;
    if (parsed.appearanceColor !== void 0) role.appearanceColor = parsed.appearanceColor ?? null;
    role.updatedAt = /* @__PURE__ */ new Date();
    await em.flush();
    const de = ctx.container.resolve("dataEngine");
    await setCustomFieldsIfAny({
      dataEngine: de,
      entityId: E.staff.staff_team_role,
      recordId: role.id,
      tenantId: role.tenantId,
      organizationId: role.organizationId,
      values: custom
    });
    await emitCrudSideEffects({
      dataEngine: de,
      action: "updated",
      entity: role,
      identifiers: {
        id: role.id,
        organizationId: role.organizationId,
        tenantId: role.tenantId
      },
      events: staffTeamRoleCrudEvents,
      indexer: teamRoleCrudIndexer
    });
    return { roleId: role.id };
  },
  buildLog: async ({ snapshots, ctx }) => {
    const before = snapshots.before;
    if (!before) return null;
    const em = ctx.container.resolve("em").fork();
    const after = await loadTeamRoleSnapshot(em, before.id, staffSnapshotScopeFromSnapshot(before));
    if (!after) return null;
    const customBefore = snapshots.customBefore ?? void 0;
    const customAfter = await loadTeamRoleCustomSnapshot(em, after);
    const changes = buildChanges(before, after, [
      "teamId",
      "name",
      "description",
      "appearanceIcon",
      "appearanceColor",
      "deletedAt"
    ]);
    const customChanges = diffCustomFieldChanges(customBefore, customAfter);
    if (Object.keys(customChanges).length) {
      changes.customFields = { from: customBefore ?? null, to: customAfter ?? null };
    }
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("staff.audit.teamRoles.update", "Update team role"),
      resourceKind: "staff.teamRole",
      resourceId: before.id,
      tenantId: before.tenantId,
      organizationId: before.organizationId,
      snapshotBefore: before,
      snapshotAfter: after,
      changes,
      payload: {
        undo: {
          before,
          after,
          customBefore: customBefore ?? null,
          customAfter
        }
      }
    };
  },
  undo: async ({ logEntry, ctx }) => {
    const payload = extractUndoPayload(logEntry);
    const before = payload?.before;
    if (!before) return;
    const em = ctx.container.resolve("em").fork();
    const role = await em.findOne(StaffTeamRole, scopedStaffSnapshotWhere(before.id, staffSnapshotScopeFromSnapshot(before)));
    if (!role) return;
    role.teamId = before.teamId ?? null;
    role.name = before.name;
    role.description = before.description ?? null;
    role.appearanceIcon = before.appearanceIcon ?? null;
    role.appearanceColor = before.appearanceColor ?? null;
    role.deletedAt = before.deletedAt ? new Date(before.deletedAt) : null;
    role.updatedAt = /* @__PURE__ */ new Date();
    await em.flush();
    const de = ctx.container.resolve("dataEngine");
    if (payload.customBefore || payload.customAfter) {
      const reset = buildCustomFieldResetMap(payload.customBefore ?? void 0, payload.customAfter ?? void 0);
      await setCustomFieldsIfAny({
        dataEngine: de,
        entityId: E.staff.staff_team_role,
        recordId: role.id,
        tenantId: role.tenantId,
        organizationId: role.organizationId,
        values: reset
      });
    }
    await emitCrudUndoSideEffects({
      dataEngine: de,
      action: "updated",
      entity: role,
      identifiers: {
        id: role.id,
        organizationId: role.organizationId,
        tenantId: role.tenantId
      },
      events: staffTeamRoleCrudEvents,
      indexer: teamRoleCrudIndexer
    });
  }
};
const deleteTeamRoleCommand = {
  id: "staff.team-roles.delete",
  async prepare(input, ctx) {
    const id = input?.id;
    if (!id) throw new CrudHttpError(400, { error: "Role id is required." });
    const em = ctx.container.resolve("em");
    const snapshot = await loadTeamRoleSnapshot(em, id, staffSnapshotScopeFromContext(ctx));
    if (!snapshot) return {};
    const custom = await loadTeamRoleCustomSnapshot(em, snapshot);
    return { before: snapshot, customBefore: custom };
  },
  async execute(input, ctx) {
    const id = input?.id;
    if (!id) throw new CrudHttpError(400, { error: "Role id is required." });
    const em = ctx.container.resolve("em").fork();
    const scope = commandActorScope(ctx);
    const role = await findOneWithDecryption(
      em,
      StaffTeamRole,
      applyScopeToWhere({ id, deletedAt: null }, scope),
      void 0,
      scopeForDecryption(scope)
    );
    if (!role) throw new CrudHttpError(404, { error: "Team role not found." });
    ensureTenantScope(ctx, role.tenantId);
    ensureOrganizationScope(ctx, role.organizationId);
    const assignedMember = await findOneWithDecryption(
      em,
      StaffTeamMember,
      {
        tenantId: role.tenantId,
        organizationId: role.organizationId,
        deletedAt: null,
        roleIds: { $contains: [role.id] }
      },
      void 0,
      { tenantId: role.tenantId, organizationId: role.organizationId }
    );
    if (assignedMember) {
      const { translate } = await resolveTranslations();
      throw new CrudHttpError(409, {
        error: translate(
          "staff.teamRoles.errors.assignedMembers",
          "Team role has assigned team members. Remove them before deleting."
        )
      });
    }
    role.deletedAt = /* @__PURE__ */ new Date();
    role.updatedAt = /* @__PURE__ */ new Date();
    await em.flush();
    const de = ctx.container.resolve("dataEngine");
    await emitCrudSideEffects({
      dataEngine: de,
      action: "deleted",
      entity: role,
      identifiers: {
        id: role.id,
        organizationId: role.organizationId,
        tenantId: role.tenantId
      },
      events: staffTeamRoleCrudEvents,
      indexer: teamRoleCrudIndexer
    });
    return { roleId: role.id };
  },
  buildLog: async ({ snapshots }) => {
    const before = snapshots.before;
    if (!before) return null;
    const customBefore = snapshots.customBefore ?? void 0;
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("staff.audit.teamRoles.delete", "Delete team role"),
      resourceKind: "staff.teamRole",
      resourceId: before.id,
      tenantId: before.tenantId,
      organizationId: before.organizationId,
      snapshotBefore: before,
      payload: {
        undo: {
          before,
          customBefore: customBefore ?? null
        }
      }
    };
  },
  undo: async ({ logEntry, ctx }) => {
    const payload = extractUndoPayload(logEntry);
    const before = payload?.before;
    if (!before) return;
    const em = ctx.container.resolve("em").fork();
    let role = await em.findOne(StaffTeamRole, scopedStaffSnapshotWhere(before.id, staffSnapshotScopeFromSnapshot(before)));
    if (!role) {
      role = em.create(StaffTeamRole, {
        id: before.id,
        tenantId: before.tenantId,
        organizationId: before.organizationId,
        teamId: before.teamId ?? null,
        name: before.name,
        description: before.description ?? null,
        appearanceIcon: before.appearanceIcon ?? null,
        appearanceColor: before.appearanceColor ?? null,
        deletedAt: null,
        createdAt: /* @__PURE__ */ new Date(),
        updatedAt: /* @__PURE__ */ new Date()
      });
      em.persist(role);
    } else {
      role.teamId = before.teamId ?? null;
      role.name = before.name;
      role.description = before.description ?? null;
      role.appearanceIcon = before.appearanceIcon ?? null;
      role.appearanceColor = before.appearanceColor ?? null;
      role.deletedAt = null;
      role.updatedAt = /* @__PURE__ */ new Date();
    }
    await em.flush();
    const de = ctx.container.resolve("dataEngine");
    if (payload.customBefore) {
      const reset = buildCustomFieldResetMap(payload.customBefore, void 0);
      await setCustomFieldsIfAny({
        dataEngine: de,
        entityId: E.staff.staff_team_role,
        recordId: role.id,
        tenantId: role.tenantId,
        organizationId: role.organizationId,
        values: reset
      });
    }
    await emitCrudUndoSideEffects({
      dataEngine: de,
      action: "created",
      entity: role,
      identifiers: {
        id: role.id,
        organizationId: role.organizationId,
        tenantId: role.tenantId
      },
      events: staffTeamRoleCrudEvents,
      indexer: teamRoleCrudIndexer
    });
  }
};
registerCommand(createTeamRoleCommand);
registerCommand(updateTeamRoleCommand);
registerCommand(deleteTeamRoleCommand);
//# sourceMappingURL=team-roles.js.map
