import { registerCommand } from "@open-mercato/shared/lib/commands";
import { emitCrudSideEffects, emitCrudUndoSideEffects, buildChanges, requireId } from "@open-mercato/shared/lib/commands/helpers";
import { StaffTeamMemberAddress } from "../data/entities.js";
import {
  staffTeamMemberAddressCreateSchema,
  staffTeamMemberAddressUpdateSchema
} from "../data/validators.js";
import { staffTeamMemberAddressCrudEvents } from "../lib/crud.js";
import {
  applyScopeToWhere,
  commandActorScope,
  commandInputScope,
  ensureOrganizationScope,
  ensureTenantScope,
  explicitStaffCommandScope,
  extractUndoPayload,
  requireTeamMember,
  scopedStaffSnapshotWhere,
  staffSnapshotScopeFromContext,
  staffSnapshotScopeFromSnapshot
} from "./shared.js";
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
import { resolveRedoSnapshot } from "@open-mercato/shared/lib/commands/redo";
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import { E } from "../../../generated/entities.ids.generated.js";
const addressCrudIndexer = {
  entityType: E.staff.staff_team_member_address
};
async function loadAddressSnapshot(em, id, scope) {
  const address = await em.findOne(StaffTeamMemberAddress, scopedStaffSnapshotWhere(id, scope));
  if (!address) return null;
  return {
    id: address.id,
    organizationId: address.organizationId,
    tenantId: address.tenantId,
    memberId: typeof address.member === "string" ? address.member : address.member.id,
    name: address.name ?? null,
    purpose: address.purpose ?? null,
    companyName: address.companyName ?? null,
    addressLine1: address.addressLine1,
    addressLine2: address.addressLine2 ?? null,
    buildingNumber: address.buildingNumber ?? null,
    flatNumber: address.flatNumber ?? null,
    city: address.city ?? null,
    region: address.region ?? null,
    postalCode: address.postalCode ?? null,
    country: address.country ?? null,
    latitude: address.latitude ?? null,
    longitude: address.longitude ?? null,
    isPrimary: address.isPrimary
  };
}
async function enforcePrimaryAddress(em, memberId, addressId) {
  await em.nativeUpdate(
    StaffTeamMemberAddress,
    { member: memberId, id: { $ne: addressId }, isPrimary: true },
    { isPrimary: false }
  );
}
const createAddressCommand = {
  id: "staff.team-member-addresses.create",
  async execute(rawInput, ctx) {
    const parsed = staffTeamMemberAddressCreateSchema.parse(rawInput);
    ensureTenantScope(ctx, parsed.tenantId);
    ensureOrganizationScope(ctx, parsed.organizationId);
    const scope = commandInputScope(ctx, parsed.tenantId, parsed.organizationId);
    const em = ctx.container.resolve("em").fork();
    const member = await requireTeamMember(
      em,
      parsed.entityId,
      scope,
      "Team member not found"
    );
    ensureTenantScope(ctx, member.tenantId);
    ensureOrganizationScope(ctx, member.organizationId);
    const address = em.create(StaffTeamMemberAddress, {
      organizationId: parsed.organizationId,
      tenantId: parsed.tenantId,
      member,
      name: parsed.name ?? null,
      purpose: parsed.purpose ?? null,
      companyName: parsed.companyName ?? null,
      addressLine1: parsed.addressLine1,
      addressLine2: parsed.addressLine2 ?? null,
      buildingNumber: parsed.buildingNumber ?? null,
      flatNumber: parsed.flatNumber ?? null,
      city: parsed.city ?? null,
      region: parsed.region ?? null,
      postalCode: parsed.postalCode ?? null,
      country: parsed.country ?? null,
      latitude: parsed.latitude ?? null,
      longitude: parsed.longitude ?? null,
      isPrimary: parsed.isPrimary ?? false,
      createdAt: /* @__PURE__ */ new Date(),
      updatedAt: /* @__PURE__ */ new Date()
    });
    em.persist(address);
    await em.flush();
    if (address.isPrimary) {
      await enforcePrimaryAddress(em, member.id, address.id);
      await em.flush();
    }
    const de = ctx.container.resolve("dataEngine");
    await emitCrudSideEffects({
      dataEngine: de,
      action: "created",
      entity: address,
      identifiers: {
        id: address.id,
        organizationId: address.organizationId,
        tenantId: address.tenantId
      },
      events: staffTeamMemberAddressCrudEvents,
      indexer: addressCrudIndexer
    });
    return { addressId: address.id };
  },
  captureAfter: async (_input, result, ctx) => {
    const em = ctx.container.resolve("em").fork();
    return await loadAddressSnapshot(em, result.addressId, staffSnapshotScopeFromContext(ctx));
  },
  buildLog: async ({ result, snapshots }) => {
    const { translate } = await resolveTranslations();
    const snapshot = snapshots.after;
    return {
      actionLabel: translate("staff.audit.teamMemberAddresses.create", "Create address"),
      resourceKind: "staff.team_member_address",
      resourceId: result.addressId,
      parentResourceKind: "staff.teamMember",
      parentResourceId: snapshot?.memberId ?? null,
      tenantId: snapshot?.tenantId ?? null,
      organizationId: snapshot?.organizationId ?? null,
      snapshotAfter: snapshot ?? null,
      payload: {
        undo: {
          after: snapshot ?? null
        }
      }
    };
  },
  undo: async ({ logEntry, ctx }) => {
    const payload = extractUndoPayload(logEntry);
    const after = payload?.after;
    const addressId = after?.id ?? logEntry?.resourceId ?? null;
    if (!addressId) return;
    const em = ctx.container.resolve("em").fork();
    const address = await em.findOne(StaffTeamMemberAddress, scopedStaffSnapshotWhere(addressId, staffSnapshotScopeFromSnapshot(after)));
    if (address) {
      em.remove(address);
      await em.flush();
    }
  },
  redo: async ({ logEntry, ctx }) => {
    const after = resolveRedoSnapshot(logEntry);
    if (!after) {
      throw new CrudHttpError(400, { error: "[internal] redo snapshot unavailable for address create" });
    }
    const em = ctx.container.resolve("em").fork();
    const snapshotScope = staffSnapshotScopeFromSnapshot(after);
    const member = await requireTeamMember(
      em,
      after.memberId,
      explicitStaffCommandScope(after.tenantId, after.organizationId),
      "Team member not found"
    );
    let address = await em.findOne(StaffTeamMemberAddress, scopedStaffSnapshotWhere(after.id, snapshotScope));
    if (!address) {
      address = em.create(StaffTeamMemberAddress, {
        id: after.id,
        organizationId: after.organizationId,
        tenantId: after.tenantId,
        member,
        name: after.name,
        purpose: after.purpose,
        companyName: after.companyName,
        addressLine1: after.addressLine1,
        addressLine2: after.addressLine2,
        buildingNumber: after.buildingNumber,
        flatNumber: after.flatNumber,
        city: after.city,
        region: after.region,
        postalCode: after.postalCode,
        country: after.country,
        latitude: after.latitude,
        longitude: after.longitude,
        isPrimary: after.isPrimary,
        createdAt: /* @__PURE__ */ new Date(),
        updatedAt: /* @__PURE__ */ new Date()
      });
      em.persist(address);
    } else {
      address.member = member;
      address.name = after.name;
      address.purpose = after.purpose;
      address.companyName = after.companyName;
      address.addressLine1 = after.addressLine1;
      address.addressLine2 = after.addressLine2;
      address.buildingNumber = after.buildingNumber;
      address.flatNumber = after.flatNumber;
      address.city = after.city;
      address.region = after.region;
      address.postalCode = after.postalCode;
      address.country = after.country;
      address.latitude = after.latitude;
      address.longitude = after.longitude;
      address.isPrimary = after.isPrimary;
    }
    await em.flush();
    if (after.isPrimary) {
      await enforcePrimaryAddress(em, after.memberId, after.id);
      await em.flush();
    }
    const de = ctx.container.resolve("dataEngine");
    await emitCrudSideEffects({
      dataEngine: de,
      action: "created",
      entity: address,
      identifiers: {
        id: address.id,
        organizationId: address.organizationId,
        tenantId: address.tenantId
      },
      events: staffTeamMemberAddressCrudEvents,
      indexer: addressCrudIndexer
    });
    return { addressId: address.id };
  }
};
const updateAddressCommand = {
  id: "staff.team-member-addresses.update",
  async prepare(rawInput, ctx) {
    const parsed = staffTeamMemberAddressUpdateSchema.parse(rawInput);
    const em = ctx.container.resolve("em");
    const snapshot = await loadAddressSnapshot(em, parsed.id, staffSnapshotScopeFromContext(ctx));
    return snapshot ? { before: snapshot } : {};
  },
  async execute(rawInput, ctx) {
    const parsed = staffTeamMemberAddressUpdateSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const scope = commandActorScope(ctx);
    const address = await em.findOne(
      StaffTeamMemberAddress,
      applyScopeToWhere({ id: parsed.id }, scope)
    );
    if (!address) throw new CrudHttpError(404, { error: "Address not found" });
    ensureTenantScope(ctx, address.tenantId);
    ensureOrganizationScope(ctx, address.organizationId);
    if (parsed.entityId !== void 0) {
      const member = await requireTeamMember(em, parsed.entityId, scope, "Team member not found");
      ensureTenantScope(ctx, member.tenantId);
      ensureOrganizationScope(ctx, member.organizationId);
      address.member = member;
    }
    if (parsed.name !== void 0) address.name = parsed.name ?? null;
    if (parsed.purpose !== void 0) address.purpose = parsed.purpose ?? null;
    if (parsed.companyName !== void 0) address.companyName = parsed.companyName ?? null;
    if (parsed.addressLine1 !== void 0) address.addressLine1 = parsed.addressLine1;
    if (parsed.addressLine2 !== void 0) address.addressLine2 = parsed.addressLine2 ?? null;
    if (parsed.buildingNumber !== void 0) address.buildingNumber = parsed.buildingNumber ?? null;
    if (parsed.flatNumber !== void 0) address.flatNumber = parsed.flatNumber ?? null;
    if (parsed.city !== void 0) address.city = parsed.city ?? null;
    if (parsed.region !== void 0) address.region = parsed.region ?? null;
    if (parsed.postalCode !== void 0) address.postalCode = parsed.postalCode ?? null;
    if (parsed.country !== void 0) address.country = parsed.country ?? null;
    if (parsed.latitude !== void 0) address.latitude = parsed.latitude ?? null;
    if (parsed.longitude !== void 0) address.longitude = parsed.longitude ?? null;
    if (parsed.isPrimary !== void 0) address.isPrimary = parsed.isPrimary;
    await em.flush();
    if (address.isPrimary) {
      await enforcePrimaryAddress(em, typeof address.member === "string" ? address.member : address.member.id, address.id);
      await em.flush();
    }
    const de = ctx.container.resolve("dataEngine");
    await emitCrudSideEffects({
      dataEngine: de,
      action: "updated",
      entity: address,
      identifiers: {
        id: address.id,
        organizationId: address.organizationId,
        tenantId: address.tenantId
      },
      events: staffTeamMemberAddressCrudEvents,
      indexer: addressCrudIndexer
    });
    return { addressId: address.id };
  },
  captureAfter: async (_input, result, ctx) => {
    const em = ctx.container.resolve("em").fork();
    return await loadAddressSnapshot(em, result.addressId, staffSnapshotScopeFromContext(ctx));
  },
  buildLog: async ({ snapshots }) => {
    const { translate } = await resolveTranslations();
    const before = snapshots.before;
    if (!before) return null;
    const afterSnapshot = snapshots.after;
    const changes = afterSnapshot && before ? buildChanges(
      before,
      afterSnapshot,
      [
        "memberId",
        "name",
        "purpose",
        "companyName",
        "addressLine1",
        "addressLine2",
        "buildingNumber",
        "flatNumber",
        "city",
        "region",
        "postalCode",
        "country",
        "latitude",
        "longitude",
        "isPrimary"
      ]
    ) : {};
    return {
      actionLabel: translate("staff.audit.teamMemberAddresses.update", "Update address"),
      resourceKind: "staff.team_member_address",
      resourceId: before.id,
      parentResourceKind: "staff.teamMember",
      parentResourceId: before.memberId ?? null,
      tenantId: before.tenantId,
      organizationId: before.organizationId,
      snapshotBefore: before,
      snapshotAfter: afterSnapshot ?? null,
      changes,
      payload: {
        undo: {
          before,
          after: afterSnapshot ?? null
        }
      }
    };
  },
  undo: async ({ logEntry, ctx }) => {
    const payload = extractUndoPayload(logEntry);
    const before = payload?.before;
    if (!before) return;
    const em = ctx.container.resolve("em").fork();
    const snapshotScope = staffSnapshotScopeFromSnapshot(before);
    let address = await em.findOne(StaffTeamMemberAddress, scopedStaffSnapshotWhere(before.id, snapshotScope));
    const member = await requireTeamMember(
      em,
      before.memberId,
      explicitStaffCommandScope(before.tenantId, before.organizationId),
      "Team member not found"
    );
    if (!address) {
      address = em.create(StaffTeamMemberAddress, {
        id: before.id,
        organizationId: before.organizationId,
        tenantId: before.tenantId,
        member,
        name: before.name,
        purpose: before.purpose,
        companyName: before.companyName,
        addressLine1: before.addressLine1,
        addressLine2: before.addressLine2,
        buildingNumber: before.buildingNumber,
        flatNumber: before.flatNumber,
        city: before.city,
        region: before.region,
        postalCode: before.postalCode,
        country: before.country,
        latitude: before.latitude,
        longitude: before.longitude,
        isPrimary: before.isPrimary,
        createdAt: /* @__PURE__ */ new Date(),
        updatedAt: /* @__PURE__ */ new Date()
      });
      em.persist(address);
    } else {
      address.member = member;
      address.name = before.name;
      address.purpose = before.purpose;
      address.companyName = before.companyName;
      address.addressLine1 = before.addressLine1;
      address.addressLine2 = before.addressLine2;
      address.buildingNumber = before.buildingNumber;
      address.flatNumber = before.flatNumber;
      address.city = before.city;
      address.region = before.region;
      address.postalCode = before.postalCode;
      address.country = before.country;
      address.latitude = before.latitude;
      address.longitude = before.longitude;
      address.isPrimary = before.isPrimary;
    }
    await em.flush();
    if (before.isPrimary) {
      await enforcePrimaryAddress(em, before.memberId, before.id);
      await em.flush();
    }
    const de = ctx.container.resolve("dataEngine");
    await emitCrudUndoSideEffects({
      dataEngine: de,
      action: "updated",
      entity: address,
      identifiers: {
        id: address.id,
        organizationId: address.organizationId,
        tenantId: address.tenantId
      },
      events: staffTeamMemberAddressCrudEvents,
      indexer: addressCrudIndexer
    });
  }
};
const deleteAddressCommand = {
  id: "staff.team-member-addresses.delete",
  async prepare(input, ctx) {
    const id = requireId(input, "Address id required");
    const em = ctx.container.resolve("em");
    const snapshot = await loadAddressSnapshot(em, id, staffSnapshotScopeFromContext(ctx));
    return snapshot ? { before: snapshot } : {};
  },
  async execute(input, ctx) {
    const id = requireId(input, "Address id required");
    const em = ctx.container.resolve("em").fork();
    const scope = commandActorScope(ctx);
    const address = await em.findOne(
      StaffTeamMemberAddress,
      applyScopeToWhere({ id }, scope)
    );
    if (!address) throw new CrudHttpError(404, { error: "Address not found" });
    ensureTenantScope(ctx, address.tenantId);
    ensureOrganizationScope(ctx, address.organizationId);
    em.remove(address);
    await em.flush();
    const de = ctx.container.resolve("dataEngine");
    await emitCrudSideEffects({
      dataEngine: de,
      action: "deleted",
      entity: address,
      identifiers: {
        id: address.id,
        organizationId: address.organizationId,
        tenantId: address.tenantId
      },
      events: staffTeamMemberAddressCrudEvents,
      indexer: addressCrudIndexer
    });
    return { addressId: address.id };
  },
  buildLog: async ({ snapshots }) => {
    const before = snapshots.before;
    if (!before) return null;
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("staff.audit.teamMemberAddresses.delete", "Delete address"),
      resourceKind: "staff.team_member_address",
      resourceId: before.id,
      parentResourceKind: "staff.teamMember",
      parentResourceId: before.memberId ?? null,
      tenantId: before.tenantId,
      organizationId: before.organizationId,
      snapshotBefore: before,
      payload: {
        undo: {
          before
        }
      }
    };
  },
  undo: async ({ logEntry, ctx }) => {
    const payload = extractUndoPayload(logEntry);
    const before = payload?.before;
    if (!before) return;
    const em = ctx.container.resolve("em").fork();
    const snapshotScope = staffSnapshotScopeFromSnapshot(before);
    const member = await requireTeamMember(
      em,
      before.memberId,
      explicitStaffCommandScope(before.tenantId, before.organizationId),
      "Team member not found"
    );
    let address = await em.findOne(StaffTeamMemberAddress, scopedStaffSnapshotWhere(before.id, snapshotScope));
    if (!address) {
      address = em.create(StaffTeamMemberAddress, {
        id: before.id,
        organizationId: before.organizationId,
        tenantId: before.tenantId,
        member,
        name: before.name,
        purpose: before.purpose,
        companyName: before.companyName,
        addressLine1: before.addressLine1,
        addressLine2: before.addressLine2,
        buildingNumber: before.buildingNumber,
        flatNumber: before.flatNumber,
        city: before.city,
        region: before.region,
        postalCode: before.postalCode,
        country: before.country,
        latitude: before.latitude,
        longitude: before.longitude,
        isPrimary: before.isPrimary,
        createdAt: /* @__PURE__ */ new Date(),
        updatedAt: /* @__PURE__ */ new Date()
      });
      em.persist(address);
    } else {
      address.member = member;
      address.name = before.name;
      address.purpose = before.purpose;
      address.companyName = before.companyName;
      address.addressLine1 = before.addressLine1;
      address.addressLine2 = before.addressLine2;
      address.buildingNumber = before.buildingNumber;
      address.flatNumber = before.flatNumber;
      address.city = before.city;
      address.region = before.region;
      address.postalCode = before.postalCode;
      address.country = before.country;
      address.latitude = before.latitude;
      address.longitude = before.longitude;
      address.isPrimary = before.isPrimary;
    }
    await em.flush();
    if (before.isPrimary) {
      await enforcePrimaryAddress(em, before.memberId, before.id);
      await em.flush();
    }
    const de = ctx.container.resolve("dataEngine");
    await emitCrudUndoSideEffects({
      dataEngine: de,
      action: "created",
      entity: address,
      identifiers: {
        id: address.id,
        organizationId: address.organizationId,
        tenantId: address.tenantId
      },
      events: staffTeamMemberAddressCrudEvents,
      indexer: addressCrudIndexer
    });
  }
};
registerCommand(createAddressCommand);
registerCommand(updateAddressCommand);
registerCommand(deleteAddressCommand);
//# sourceMappingURL=addresses.js.map
