import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { CommunicationChannel } from "../data/entities.js";
import {
  COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID
} from "../commands/ingest-inbound-message.js";
import { COMMUNICATION_CHANNELS_QUEUES } from "../lib/queue.js";
import { refreshCredentialsIfNeeded } from "../lib/credential-refresh.js";
import { classifyOutboundError } from "../lib/error-classification.js";
import { createLogger } from "@open-mercato/shared/lib/logger";
const logger = createLogger("communication_channels").child({ component: "channel-import-history" });
const metadata = {
  queue: COMMUNICATION_CHANNELS_QUEUES.importHistory,
  id: "communication_channels:channel-import-history",
  concurrency: 1
};
async function handle(job, ctx) {
  const payload = job.payload;
  const { progressJobId, channelId, sinceDays, contactEmails, maxMessages, scope } = payload;
  const progressService = ctx.resolve("progressService");
  const progressContext = {
    tenantId: scope.tenantId,
    organizationId: scope.organizationId
  };
  const em = ctx.resolve("em").fork();
  const adapterRegistry = ctx.resolve("channelAdapterRegistry");
  try {
    await progressService.startJob(progressJobId, progressContext);
    const channel = await findOneWithDecryption(
      em,
      CommunicationChannel,
      {
        id: channelId,
        tenantId: scope.tenantId,
        organizationId: scope.organizationId,
        deletedAt: null
      },
      void 0,
      scope
    );
    if (!channel) {
      await progressService.failJob(
        progressJobId,
        { errorMessage: "Channel not found" },
        progressContext
      );
      return;
    }
    if (!channel.isActive || channel.status !== "connected") {
      await progressService.failJob(
        progressJobId,
        { errorMessage: `Channel is not connected (status=${channel.status})` },
        progressContext
      );
      return;
    }
    const adapter = adapterRegistry?.get(channel.providerKey);
    if (!adapter || typeof adapter.importHistory !== "function") {
      await progressService.failJob(
        progressJobId,
        {
          errorMessage: `Provider "${channel.providerKey}" does not support history import`
        },
        progressContext
      );
      return;
    }
    let credentialsService = null;
    try {
      credentialsService = ctx.resolve("integrationCredentialsService");
    } catch {
      credentialsService = null;
    }
    const credentialsScope = {
      tenantId: scope.tenantId,
      organizationId: scope.organizationId,
      userId: channel.userId ?? null
    };
    let credentials = {};
    if (channel.credentialsRef && credentialsService) {
      try {
        credentials = await credentialsService.resolve(`channel_${channel.providerKey}`, credentialsScope) ?? {};
      } catch {
        credentials = {};
      }
    }
    const refreshed = await refreshCredentialsIfNeeded(
      { adapter, channelId: channel.id, credentials, scope: credentialsScope },
      { credentialsService }
    );
    credentials = refreshed.credentials;
    const commandBus = ctx.resolve("commandBus");
    const containerProxy = { resolve: ctx.resolve.bind(ctx) };
    const commandCtx = {
      container: containerProxy,
      auth: null,
      organizationScope: null,
      selectedOrganizationId: scope.organizationId,
      organizationIds: [scope.organizationId]
    };
    let cursor;
    let processedCount = 0;
    let totalCount = maxMessages;
    let firstPage = true;
    const MAX_PAGES = 100;
    for (let pageIndex = 0; pageIndex < MAX_PAGES; pageIndex += 1) {
      if (await progressService.isCancellationRequested(progressJobId, scope.tenantId, scope.organizationId)) {
        await progressService.markCancelled(progressJobId, progressContext);
        return;
      }
      const page = await adapter.importHistory({
        credentials,
        scope: { tenantId: scope.tenantId, organizationId: scope.organizationId },
        sinceDays,
        contactEmails,
        maxMessages,
        cursor
      });
      if (firstPage && typeof page.totalCandidates === "number") {
        totalCount = Math.min(maxMessages, page.totalCandidates);
        await progressService.updateProgress(
          progressJobId,
          { totalCount, processedCount: 0 },
          progressContext
        );
        firstPage = false;
      }
      for (const message of page.messages) {
        if (processedCount >= maxMessages) break;
        try {
          const input = {
            channelId: channel.id,
            providerKey: channel.providerKey,
            channelType: channel.channelType,
            scope: { tenantId: scope.tenantId, organizationId: scope.organizationId },
            message
          };
          await commandBus.execute(COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID, {
            input,
            ctx: commandCtx
          });
        } catch (err) {
          const classification = classifyOutboundError(err);
          if (classification.transient) {
            throw err;
          }
          logger.warn("permanent ingest failure; skipping message", { channelId: channel.id, externalMessageId: message.externalMessageId, reason: classification.message });
        }
        processedCount += 1;
      }
      await progressService.updateProgress(
        progressJobId,
        { processedCount, totalCount },
        progressContext
      );
      if (processedCount >= maxMessages) break;
      if (!page.hasMore || !page.nextCursor) break;
      cursor = page.nextCursor;
    }
    await progressService.completeJob(
      progressJobId,
      { resultSummary: { importedCount: processedCount, channelId } },
      progressContext
    );
  } catch (err) {
    const message = err instanceof Error ? err.message : "Import history job failed";
    const stack = err instanceof Error ? err.stack : void 0;
    try {
      await progressService.failJob(
        progressJobId,
        { errorMessage: message.slice(0, 2e3), errorStack: stack?.slice(0, 1e4) },
        progressContext
      );
    } catch (failErr) {
      logger.error("failed to mark progress job as failed", { progressJobId, err: failErr });
    }
    throw err;
  }
}
export {
  handle as default,
  metadata
};
//# sourceMappingURL=channel-import-history.js.map
