import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { CommunicationChannel } from "../data/entities.js";
import { isUniqueViolation } from "./pg-errors.js";
const POLLING_ONLY_DEFAULT_INTERVAL_SECONDS = 300;
class MailboxAlreadyConnectedError extends Error {
  constructor(externalIdentifier, existingProviderKey) {
    super(`Mailbox ${externalIdentifier} is already connected via ${existingProviderKey}`);
    this.name = "MailboxAlreadyConnectedError";
    this.externalIdentifier = externalIdentifier;
    this.existingProviderKey = existingProviderKey;
  }
}
async function createConnectedChannelRow(args) {
  const { em, adapter, providerKey, displayName, externalIdentifier, credentialsRefId, userId, scope } = args;
  const credentialsAvailable = credentialsRefId !== null;
  const pollIntervalSeconds = args.pollIntervalSeconds !== void 0 ? args.pollIntervalSeconds : adapter.capabilities?.realtimePush === false ? POLLING_ONLY_DEFAULT_INTERVAL_SECONDS : null;
  const dscope = { tenantId: scope.tenantId, organizationId: scope.organizationId ?? null };
  if (externalIdentifier) {
    const normalized = externalIdentifier.toLowerCase();
    const userChannels = await findWithDecryption(
      em,
      CommunicationChannel,
      { tenantId: scope.tenantId, userId, deletedAt: null },
      void 0,
      dscope
    );
    const conflict = userChannels.find(
      (existing) => existing.providerKey !== providerKey && typeof existing.externalIdentifier === "string" && existing.externalIdentifier.toLowerCase() === normalized
    );
    if (conflict) {
      throw new MailboxAlreadyConnectedError(externalIdentifier, conflict.providerKey);
    }
  }
  const naturalKey = {
    tenantId: scope.tenantId,
    userId,
    providerKey,
    externalIdentifier,
    deletedAt: null
  };
  const applyConnectionState = (target) => {
    target.channelType = adapter.channelType;
    target.displayName = displayName;
    target.externalIdentifier = externalIdentifier ?? null;
    target.credentialsRef = credentialsRefId;
    target.capabilities = adapter.capabilities;
    target.isActive = credentialsAvailable;
    target.pollIntervalSeconds = pollIntervalSeconds;
    target.status = credentialsAvailable ? "connected" : "requires_reauth";
    target.lastError = credentialsAvailable ? null : "credentials_persist_failed";
  };
  if (externalIdentifier) {
    const existing = await findOneWithDecryption(em, CommunicationChannel, naturalKey, void 0, dscope);
    if (existing) {
      applyConnectionState(existing);
      await em.flush();
      return existing;
    }
  }
  const channel = em.create(CommunicationChannel, {
    providerKey,
    channelType: adapter.channelType,
    displayName,
    externalIdentifier: externalIdentifier ?? null,
    credentialsRef: credentialsRefId,
    capabilities: adapter.capabilities,
    isActive: credentialsAvailable,
    userId,
    isPrimary: false,
    pollIntervalSeconds,
    status: credentialsAvailable ? "connected" : "requires_reauth",
    lastError: credentialsAvailable ? null : "credentials_persist_failed",
    tenantId: scope.tenantId,
    organizationId: scope.organizationId ?? null
  });
  em.persist(channel);
  try {
    await em.flush();
    return channel;
  } catch (err) {
    if (!isUniqueViolation(err) || !externalIdentifier) throw err;
    const reEm = em.fork();
    const winner = await findOneWithDecryption(reEm, CommunicationChannel, naturalKey, void 0, dscope);
    if (!winner) throw err;
    applyConnectionState(winner);
    await reEm.flush();
    return winner;
  }
}
export {
  MailboxAlreadyConnectedError,
  POLLING_ONLY_DEFAULT_INTERVAL_SECONDS,
  createConnectedChannelRow
};
//# sourceMappingURL=connect-channel.js.map
