import { z } from "zod";
import { registerCommand } from "@open-mercato/shared/lib/commands";
import { extractUndoPayload as extractSharedUndoPayload } from "@open-mercato/shared/lib/commands/undo";
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { CommunicationChannel } from "../data/entities.js";
import { emitCommunicationChannelsEvent } from "../events.js";
import { pushUnregister } from "./push-unregister.js";
import { createLogger } from "@open-mercato/shared/lib/logger";
const logger = createLogger("communication_channels").child({ component: "disconnect-channel" });
const disconnectChannelSchema = z.object({
  channelId: z.string().uuid(),
  userId: z.string().uuid(),
  scope: z.object({
    tenantId: z.string().uuid(),
    organizationId: z.string().uuid().nullable()
  })
});
const COMMUNICATION_CHANNELS_DISCONNECT_CHANNEL_COMMAND_ID = "communication_channels.channel.disconnect";
const disconnectChannelCommand = {
  id: COMMUNICATION_CHANNELS_DISCONNECT_CHANNEL_COMMAND_ID,
  // Explicitly undoable (the bus also infers this from `undo` below, but
  // declaring it keeps undoability from silently dropping under a refactor).
  isUndoable: true,
  async execute(rawInput, ctx) {
    const input = disconnectChannelSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const dscope = {
      tenantId: input.scope.tenantId,
      organizationId: input.scope.organizationId ?? null
    };
    const channel = await findOneWithDecryption(
      em,
      CommunicationChannel,
      {
        id: input.channelId,
        tenantId: input.scope.tenantId,
        organizationId: input.scope.organizationId ?? null,
        deletedAt: null
      },
      void 0,
      dscope
    );
    if (!channel) {
      return { status: "noop", reason: "channel not found" };
    }
    if (channel.userId !== input.userId) {
      return { status: "not_owner", reason: "channel is not owned by the current user" };
    }
    if (channel.status === "disconnected" && !channel.isActive) {
      return { status: "noop", reason: "channel is already disconnected" };
    }
    const undo = {
      channelId: channel.id,
      tenantId: channel.tenantId,
      previousStatus: channel.status,
      previousIsActive: channel.isActive,
      previousIsPrimary: channel.isPrimary,
      previousCredentialsRef: channel.credentialsRef ?? null,
      previousLastError: channel.lastError ?? null
    };
    if (input.scope.organizationId) {
      try {
        await pushUnregister({
          container: ctx.container,
          scope: {
            tenantId: input.scope.tenantId,
            organizationId: input.scope.organizationId,
            userId: input.userId
          },
          input: { channelId: channel.id }
        });
      } catch (err) {
        logger.warn("push unregister failed for channel", { channelId: channel.id, err });
      }
    }
    channel.status = "disconnected";
    channel.isActive = false;
    channel.isPrimary = false;
    channel.credentialsRef = null;
    channel.lastError = "user-disconnected";
    channel.lastPolledAt = /* @__PURE__ */ new Date();
    await em.flush();
    await emitCommunicationChannelsEvent(
      "communication_channels.channel.disconnected",
      {
        channelId: channel.id,
        userId: input.userId,
        providerKey: channel.providerKey,
        channelType: channel.channelType,
        tenantId: input.scope.tenantId,
        organizationId: input.scope.organizationId ?? null
      },
      { persistent: true }
    );
    return { status: "disconnected", channelId: channel.id, undo };
  },
  // Persist the undo snapshot into the action log. Without this, the command bus
  // mints an undo token (so the UI offers "Undo") but the snapshot returned from
  // execute() is never stored, and undo() would silently no-op.
  async buildLog({ input, result }) {
    if (result.status !== "disconnected") return null;
    return {
      resourceKind: "communication_channels.channel",
      resourceId: result.channelId,
      tenantId: result.undo.tenantId ?? input.scope.tenantId,
      organizationId: input.scope.organizationId ?? null,
      payload: { undo: result.undo },
      snapshotBefore: result.undo
    };
  },
  async undo({ ctx, logEntry }) {
    const snapshot = extractSnapshotFromLog(logEntry);
    if (!snapshot) return;
    const em = ctx.container.resolve("em").fork();
    if (!snapshot.tenantId) return;
    const channel = await findOneWithDecryption(
      em,
      CommunicationChannel,
      { id: snapshot.channelId, tenantId: snapshot.tenantId },
      void 0,
      { tenantId: snapshot.tenantId, organizationId: null }
    );
    if (!channel) return;
    channel.status = snapshot.previousStatus;
    channel.isActive = snapshot.previousIsActive;
    channel.isPrimary = snapshot.previousIsPrimary;
    channel.credentialsRef = snapshot.previousCredentialsRef;
    channel.lastError = snapshot.previousLastError;
    await em.flush();
  }
};
function extractUndoPayload(value) {
  if (!value || typeof value !== "object") return null;
  const candidate = value.undo ?? value;
  if (!candidate || typeof candidate !== "object") return null;
  const obj = candidate;
  if (typeof obj.channelId !== "string") return null;
  return {
    channelId: obj.channelId,
    tenantId: typeof obj.tenantId === "string" ? obj.tenantId : void 0,
    previousStatus: typeof obj.previousStatus === "string" ? obj.previousStatus : "connected",
    previousIsActive: typeof obj.previousIsActive === "boolean" ? obj.previousIsActive : true,
    previousIsPrimary: typeof obj.previousIsPrimary === "boolean" ? obj.previousIsPrimary : false,
    previousCredentialsRef: typeof obj.previousCredentialsRef === "string" ? obj.previousCredentialsRef : null,
    previousLastError: typeof obj.previousLastError === "string" ? obj.previousLastError : null
  };
}
function extractSnapshotFromLog(logEntry) {
  const undo = extractSharedUndoPayload(
    logEntry ?? null
  );
  if (undo) return extractUndoPayload(undo);
  return extractUndoPayload(logEntry);
}
registerCommand(disconnectChannelCommand);
var disconnect_channel_default = disconnectChannelCommand;
export {
  COMMUNICATION_CHANNELS_DISCONNECT_CHANNEL_COMMAND_ID,
  disconnect_channel_default as default,
  extractSnapshotFromLog,
  extractUndoPayload
};
//# sourceMappingURL=disconnect-channel.js.map
