import { registerCommand } from "@open-mercato/shared/lib/commands";
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
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 { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
import { ResourcesResource, ResourcesResourceType } from "../data/entities.js";
import {
  resourcesResourceTypeCreateSchema,
  resourcesResourceTypeUpdateSchema
} from "../data/validators.js";
import { resourcesResourceTypeCrudEvents } from "../lib/crud.js";
import { makeCreateRedo } from "@open-mercato/shared/lib/commands/redo";
import { ensureOrganizationScope, ensureTenantScope, extractUndoPayload } from "./shared.js";
import { E } from "../../../generated/entities.ids.generated.js";
const resourceTypeCrudIndexer = {
  entityType: E.resources.resources_resource_type
};
async function loadResourceTypeSnapshot(em, id) {
  const resourceType = await findOneWithDecryption(
    em,
    ResourcesResourceType,
    { id },
    void 0,
    { tenantId: null, organizationId: null }
  );
  if (!resourceType) return null;
  return {
    id: resourceType.id,
    tenantId: resourceType.tenantId,
    organizationId: resourceType.organizationId,
    name: resourceType.name,
    description: resourceType.description ?? null,
    appearanceIcon: resourceType.appearanceIcon ?? null,
    appearanceColor: resourceType.appearanceColor ?? null,
    deletedAt: resourceType.deletedAt ? resourceType.deletedAt.toISOString() : null
  };
}
async function loadResourceTypeCustomSnapshot(em, snapshot) {
  return loadCustomFieldSnapshot(em, {
    entityId: E.resources.resources_resource_type,
    recordId: snapshot.id,
    tenantId: snapshot.tenantId,
    organizationId: snapshot.organizationId
  });
}
const createResourceTypeCommand = {
  id: "resources.resourceTypes.create",
  async execute(rawInput, ctx) {
    const { parsed, custom } = parseWithCustomFields(resourcesResourceTypeCreateSchema, rawInput);
    ensureTenantScope(ctx, parsed.tenantId);
    ensureOrganizationScope(ctx, parsed.organizationId);
    const em = ctx.container.resolve("em").fork();
    const now = /* @__PURE__ */ new Date();
    const record = em.create(ResourcesResourceType, {
      tenantId: parsed.tenantId,
      organizationId: parsed.organizationId,
      name: parsed.name,
      description: parsed.description ?? null,
      appearanceIcon: parsed.appearanceIcon ?? null,
      appearanceColor: parsed.appearanceColor ?? null,
      createdAt: now,
      updatedAt: now
    });
    em.persist(record);
    await em.flush();
    const dataEngine = ctx.container.resolve("dataEngine");
    await setCustomFieldsIfAny({
      dataEngine,
      entityId: E.resources.resources_resource_type,
      recordId: record.id,
      tenantId: record.tenantId,
      organizationId: record.organizationId,
      values: custom
    });
    await emitCrudSideEffects({
      dataEngine,
      action: "created",
      entity: record,
      identifiers: {
        id: record.id,
        organizationId: record.organizationId,
        tenantId: record.tenantId
      },
      events: resourcesResourceTypeCrudEvents,
      indexer: resourceTypeCrudIndexer
    });
    return { resourceTypeId: record.id };
  },
  captureAfter: async (_input, result, ctx) => {
    const em = ctx.container.resolve("em").fork();
    const snapshot = await loadResourceTypeSnapshot(em, result.resourceTypeId);
    if (!snapshot) return null;
    const custom = await loadResourceTypeCustomSnapshot(em, snapshot);
    return { snapshot, custom };
  },
  buildLog: async ({ result, ctx }) => {
    const em = ctx.container.resolve("em").fork();
    const snapshot = await loadResourceTypeSnapshot(em, result.resourceTypeId);
    if (!snapshot) return null;
    const custom = await loadResourceTypeCustomSnapshot(em, snapshot);
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("resources.audit.resourceTypes.create", "Create resource type"),
      resourceKind: "resources.resourceType",
      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 resourceType = await em.findOne(ResourcesResourceType, { id: after.id });
    if (resourceType) {
      resourceType.deletedAt = /* @__PURE__ */ new Date();
      resourceType.updatedAt = /* @__PURE__ */ new Date();
      await em.flush();
      const dataEngine = ctx.container.resolve("dataEngine");
      await emitCrudUndoSideEffects({
        dataEngine,
        action: "deleted",
        entity: resourceType,
        identifiers: {
          id: resourceType.id,
          organizationId: resourceType.organizationId,
          tenantId: resourceType.tenantId
        },
        events: resourcesResourceTypeCrudEvents,
        indexer: resourceTypeCrudIndexer
      });
    }
  },
  redo: makeCreateRedo({
    entityClass: ResourcesResourceType,
    buildResult: (entity) => ({ resourceTypeId: entity.id }),
    events: resourcesResourceTypeCrudEvents,
    indexer: resourceTypeCrudIndexer,
    afterRestore: async ({ ctx, entity, logEntry }) => {
      const payload = extractUndoPayload(logEntry);
      if (!payload?.customAfter) return;
      const dataEngine = ctx.container.resolve("dataEngine");
      const reset = buildCustomFieldResetMap(payload.customAfter, void 0);
      await setCustomFieldsIfAny({
        dataEngine,
        entityId: E.resources.resources_resource_type,
        recordId: entity.id,
        tenantId: entity.tenantId,
        organizationId: entity.organizationId,
        values: reset
      });
    }
  })
};
const updateResourceTypeCommand = {
  id: "resources.resourceTypes.update",
  async prepare(rawInput, ctx) {
    const { parsed } = parseWithCustomFields(resourcesResourceTypeUpdateSchema, rawInput);
    const em = ctx.container.resolve("em");
    const snapshot = await loadResourceTypeSnapshot(em, parsed.id);
    if (!snapshot) return {};
    const custom = await loadResourceTypeCustomSnapshot(em, snapshot);
    return { before: snapshot, customBefore: custom };
  },
  async execute(rawInput, ctx) {
    const { parsed, custom } = parseWithCustomFields(resourcesResourceTypeUpdateSchema, rawInput);
    const em = ctx.container.resolve("em").fork();
    const record = await findOneWithDecryption(
      em,
      ResourcesResourceType,
      { id: parsed.id, deletedAt: null },
      void 0,
      { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null }
    );
    if (!record) throw new CrudHttpError(404, { error: "Resources resource type not found." });
    ensureTenantScope(ctx, record.tenantId);
    ensureOrganizationScope(ctx, record.organizationId);
    if (parsed.name !== void 0) record.name = parsed.name;
    if (parsed.description !== void 0) record.description = parsed.description ?? null;
    if (parsed.appearanceIcon !== void 0) record.appearanceIcon = parsed.appearanceIcon ?? null;
    if (parsed.appearanceColor !== void 0) record.appearanceColor = parsed.appearanceColor ?? null;
    record.updatedAt = /* @__PURE__ */ new Date();
    await em.flush();
    const dataEngine = ctx.container.resolve("dataEngine");
    await setCustomFieldsIfAny({
      dataEngine,
      entityId: E.resources.resources_resource_type,
      recordId: record.id,
      tenantId: record.tenantId,
      organizationId: record.organizationId,
      values: custom
    });
    await emitCrudSideEffects({
      dataEngine,
      action: "updated",
      entity: record,
      identifiers: {
        id: record.id,
        organizationId: record.organizationId,
        tenantId: record.tenantId
      },
      events: resourcesResourceTypeCrudEvents,
      indexer: resourceTypeCrudIndexer
    });
    return { resourceTypeId: record.id };
  },
  buildLog: async ({ snapshots, ctx }) => {
    const before = snapshots.before;
    if (!before) return null;
    const em = ctx.container.resolve("em").fork();
    const after = await loadResourceTypeSnapshot(em, before.id);
    if (!after) return null;
    const customBefore = snapshots.customBefore ?? void 0;
    const customAfter = await loadResourceTypeCustomSnapshot(em, after);
    const changes = buildChanges(before, after, [
      "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("resources.audit.resourceTypes.update", "Update resource type"),
      resourceKind: "resources.resourceType",
      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 resourceType = await em.findOne(ResourcesResourceType, { id: before.id });
    if (!resourceType) return;
    resourceType.name = before.name;
    resourceType.description = before.description ?? null;
    resourceType.appearanceIcon = before.appearanceIcon ?? null;
    resourceType.appearanceColor = before.appearanceColor ?? null;
    resourceType.deletedAt = before.deletedAt ? new Date(before.deletedAt) : null;
    resourceType.updatedAt = /* @__PURE__ */ new Date();
    await em.flush();
    const dataEngine = ctx.container.resolve("dataEngine");
    if (payload.customBefore || payload.customAfter) {
      const currentCustom = await loadCustomFieldSnapshot(em, {
        entityId: E.resources.resources_resource_type,
        recordId: resourceType.id,
        tenantId: resourceType.tenantId,
        organizationId: resourceType.organizationId
      });
      const reset = buildCustomFieldResetMap(payload.customBefore ?? void 0, currentCustom ?? void 0);
      await setCustomFieldsIfAny({
        dataEngine,
        entityId: E.resources.resources_resource_type,
        recordId: resourceType.id,
        tenantId: resourceType.tenantId,
        organizationId: resourceType.organizationId,
        values: reset
      });
    }
    await emitCrudUndoSideEffects({
      dataEngine,
      action: "updated",
      entity: resourceType,
      identifiers: {
        id: resourceType.id,
        organizationId: resourceType.organizationId,
        tenantId: resourceType.tenantId
      },
      events: resourcesResourceTypeCrudEvents,
      indexer: resourceTypeCrudIndexer
    });
  }
};
const deleteResourceTypeCommand = {
  id: "resources.resourceTypes.delete",
  async prepare(input, ctx) {
    const id = input?.id;
    if (!id) throw new CrudHttpError(400, { error: "Resource type id is required." });
    const em = ctx.container.resolve("em");
    const snapshot = await loadResourceTypeSnapshot(em, id);
    if (!snapshot) return {};
    const custom = await loadResourceTypeCustomSnapshot(em, snapshot);
    return { before: snapshot, customBefore: custom };
  },
  async execute(input, ctx) {
    const id = input?.id;
    if (!id) throw new CrudHttpError(400, { error: "Resource type id is required." });
    const em = ctx.container.resolve("em").fork();
    const record = await findOneWithDecryption(
      em,
      ResourcesResourceType,
      { id, deletedAt: null },
      void 0,
      { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null }
    );
    if (!record) throw new CrudHttpError(404, { error: "Resources resource type not found." });
    ensureTenantScope(ctx, record.tenantId);
    ensureOrganizationScope(ctx, record.organizationId);
    const assignedCount = await em.count(ResourcesResource, {
      resourceTypeId: record.id,
      deletedAt: null,
      tenantId: record.tenantId,
      organizationId: record.organizationId
    });
    if (assignedCount > 0) {
      throw new CrudHttpError(400, { error: "Resource type has assigned resources." });
    }
    record.deletedAt = /* @__PURE__ */ new Date();
    record.updatedAt = /* @__PURE__ */ new Date();
    await em.flush();
    const dataEngine = ctx.container.resolve("dataEngine");
    await emitCrudSideEffects({
      dataEngine,
      action: "deleted",
      entity: record,
      identifiers: {
        id: record.id,
        organizationId: record.organizationId,
        tenantId: record.tenantId
      },
      events: resourcesResourceTypeCrudEvents,
      indexer: resourceTypeCrudIndexer
    });
    return { resourceTypeId: record.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("resources.audit.resourceTypes.delete", "Delete resource type"),
      resourceKind: "resources.resourceType",
      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 resourceType = await em.findOne(ResourcesResourceType, { id: before.id });
    if (!resourceType) {
      resourceType = em.create(ResourcesResourceType, {
        id: before.id,
        tenantId: before.tenantId,
        organizationId: before.organizationId,
        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(resourceType);
    } else {
      resourceType.name = before.name;
      resourceType.description = before.description ?? null;
      resourceType.appearanceIcon = before.appearanceIcon ?? null;
      resourceType.appearanceColor = before.appearanceColor ?? null;
      resourceType.deletedAt = null;
      resourceType.updatedAt = /* @__PURE__ */ new Date();
    }
    await em.flush();
    const dataEngine = ctx.container.resolve("dataEngine");
    if (payload.customBefore) {
      const reset = buildCustomFieldResetMap(payload.customBefore, void 0);
      await setCustomFieldsIfAny({
        dataEngine,
        entityId: E.resources.resources_resource_type,
        recordId: resourceType.id,
        tenantId: resourceType.tenantId,
        organizationId: resourceType.organizationId,
        values: reset
      });
    }
    await emitCrudUndoSideEffects({
      dataEngine,
      action: "created",
      entity: resourceType,
      identifiers: {
        id: resourceType.id,
        organizationId: resourceType.organizationId,
        tenantId: resourceType.tenantId
      },
      events: resourcesResourceTypeCrudEvents,
      indexer: resourceTypeCrudIndexer
    });
  }
};
registerCommand(createResourceTypeCommand);
registerCommand(updateResourceTypeCommand);
registerCommand(deleteResourceTypeCommand);
//# sourceMappingURL=resource-types.js.map
