import { registerCommand } from "@open-mercato/shared/lib/commands";
import { tenantCreateSchema, tenantUpdateSchema } from "@open-mercato/core/modules/directory/data/validators";
import { Tenant } from "@open-mercato/core/modules/directory/data/entities";
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import { E } from "../../../generated/entities.ids.generated.js";
import {
  parseWithCustomFields,
  setCustomFieldsIfAny,
  emitCrudSideEffects,
  buildChanges,
  requireId
} from "@open-mercato/shared/lib/commands/helpers";
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
import { isTenantDataEncryptionEnabled } from "@open-mercato/shared/lib/encryption/toggles";
import { makeCreateRedo } from "@open-mercato/shared/lib/commands/redo";
import { createLogger } from "@open-mercato/shared/lib/logger";
const logger = createLogger("directory").child({ component: "tenants" });
const tenantCrudEvents = {
  module: "directory",
  entity: "tenant",
  persistent: true
};
const tenantCrudIndexer = {
  entityType: E.directory.tenant
};
function serializeTenant(entity) {
  return {
    id: String(entity.id),
    name: entity.name,
    isActive: !!entity.isActive,
    createdAt: entity.createdAt ? entity.createdAt.toISOString() : null,
    updatedAt: entity.updatedAt ? entity.updatedAt.toISOString() : null,
    deletedAt: entity.deletedAt ? entity.deletedAt.toISOString() : null
  };
}
const createTenantCommand = {
  id: "directory.tenants.create",
  async execute(rawInput, ctx) {
    const { parsed, custom } = parseWithCustomFields(tenantCreateSchema, rawInput);
    const de = ctx.container.resolve("dataEngine");
    const tenant = await de.createOrmEntity({
      entity: Tenant,
      data: {
        name: parsed.name,
        isActive: parsed.isActive ?? true
      }
    });
    await setCustomFieldsIfAny({
      dataEngine: de,
      entityId: E.directory.tenant,
      recordId: String(tenant.id),
      organizationId: null,
      tenantId: ctx.auth?.tenantId ?? null,
      values: custom
    });
    if (isTenantDataEncryptionEnabled()) {
      try {
        const kms = ctx.container.resolve("kmsService");
        if (kms?.isHealthy?.()) {
          await kms?.createTenantDek?.(String(tenant.id));
          logger.info("Created tenant DEK", { tenantId: String(tenant.id) });
        } else {
          logger.warn("KMS not healthy, skipping tenant DEK provisioning", { tenantId: String(tenant.id) });
        }
      } catch (err) {
        logger.warn("Failed to provision tenant key", { err });
      }
    }
    const identifiers = {
      id: String(tenant.id),
      organizationId: null,
      tenantId: String(tenant.id)
    };
    await emitCrudSideEffects({
      dataEngine: de,
      action: "created",
      entity: tenant,
      identifiers,
      events: tenantCrudEvents,
      indexer: tenantCrudIndexer
    });
    return tenant;
  },
  captureAfter: (_input, result) => serializeTenant(result),
  buildLog: async ({ snapshots, result, ctx }) => {
    const { translate } = await resolveTranslations();
    const after = snapshots.after ?? serializeTenant(result);
    return {
      actionLabel: translate("directory.audit.tenants.create", "Create tenant"),
      resourceKind: "directory.tenant",
      resourceId: String(result.id),
      tenantId: ctx.auth?.tenantId ?? null,
      snapshotAfter: after,
      payload: { undo: { after } }
    };
  },
  redo: makeCreateRedo({
    entityClass: Tenant,
    buildResult: (entity) => entity,
    events: tenantCrudEvents,
    indexer: tenantCrudIndexer,
    afterRestore: ({ entity }) => {
      Reflect.set(entity, "tenantId", String(entity.id));
    }
  })
};
const updateTenantCommand = {
  id: "directory.tenants.update",
  async prepare(rawInput, ctx) {
    const { parsed } = parseWithCustomFields(tenantUpdateSchema, rawInput);
    const em = ctx.container.resolve("em");
    const current = await em.findOne(Tenant, { id: parsed.id, deletedAt: null });
    if (!current) throw new CrudHttpError(404, { error: "Tenant not found" });
    return { before: serializeTenant(current) };
  },
  async execute(rawInput, ctx) {
    const { parsed, custom } = parseWithCustomFields(tenantUpdateSchema, rawInput);
    const de = ctx.container.resolve("dataEngine");
    const tenant = await de.updateOrmEntity({
      entity: Tenant,
      where: { id: parsed.id, deletedAt: null },
      apply: (entity) => {
        if (parsed.name !== void 0) entity.name = parsed.name;
        if (parsed.isActive !== void 0) entity.isActive = parsed.isActive;
        entity.updatedAt = /* @__PURE__ */ new Date();
      }
    });
    if (!tenant) throw new CrudHttpError(404, { error: "Tenant not found" });
    await setCustomFieldsIfAny({
      dataEngine: de,
      entityId: E.directory.tenant,
      recordId: String(tenant.id),
      organizationId: null,
      tenantId: ctx.auth?.tenantId ?? null,
      values: custom
    });
    const identifiers = {
      id: String(tenant.id),
      organizationId: null,
      tenantId: String(tenant.id)
    };
    await emitCrudSideEffects({
      dataEngine: de,
      action: "updated",
      entity: tenant,
      identifiers,
      events: tenantCrudEvents,
      indexer: tenantCrudIndexer
    });
    return tenant;
  },
  captureAfter: (_input, result) => serializeTenant(result),
  buildLog: async ({ result, snapshots, ctx }) => {
    const { translate } = await resolveTranslations();
    const beforeRecord = snapshots.before ?? null;
    const after = serializeTenant(result);
    const changes = buildChanges(beforeRecord, after, ["name", "isActive"]);
    return {
      actionLabel: translate("directory.audit.tenants.update", "Update tenant"),
      resourceKind: "directory.tenant",
      resourceId: String(result.id),
      changes,
      tenantId: ctx.auth?.tenantId ?? null
    };
  }
};
const deleteTenantCommand = {
  id: "directory.tenants.delete",
  async prepare(input, ctx) {
    const id = requireId(input, "Tenant id required");
    const em = ctx.container.resolve("em");
    const existing = await em.findOne(Tenant, { id, deletedAt: null });
    return existing ? { before: serializeTenant(existing) } : {};
  },
  async execute(rawInput, ctx) {
    const id = requireId(rawInput, "Tenant id required");
    const de = ctx.container.resolve("dataEngine");
    const tenant = await de.deleteOrmEntity({
      entity: Tenant,
      where: { id, deletedAt: null },
      soft: true,
      softDeleteField: "deletedAt"
    });
    if (!tenant) throw new CrudHttpError(404, { error: "Tenant not found" });
    const identifiers = {
      id: String(id),
      organizationId: null,
      tenantId: String(id)
    };
    await emitCrudSideEffects({
      dataEngine: de,
      action: "deleted",
      entity: tenant,
      identifiers,
      events: tenantCrudEvents,
      indexer: tenantCrudIndexer
    });
    return tenant;
  },
  buildLog: async ({ snapshots, input, ctx }) => {
    const { translate } = await resolveTranslations();
    const beforeSnapshot = snapshots.before ?? null;
    const id = String(input?.body?.id ?? input?.query?.id ?? "");
    const fallbackId = beforeSnapshot?.id ?? null;
    return {
      actionLabel: translate("directory.audit.tenants.delete", "Delete tenant"),
      resourceKind: "directory.tenant",
      resourceId: id || fallbackId || null,
      snapshotBefore: beforeSnapshot ?? null,
      tenantId: ctx.auth?.tenantId ?? null
    };
  }
};
registerCommand(createTenantCommand);
registerCommand(updateTenantCommand);
registerCommand(deleteTenantCommand);
export {
  tenantCrudEvents,
  tenantCrudIndexer
};
//# sourceMappingURL=tenants.js.map
