import {
  assertTenantId
} from "./chunk-MZ6GV4YF.js";
import {
  INTEGRATION_CHANGE_SOURCE,
  INTEGRATION_CURSOR_STORE,
  INTEGRATION_FIELD_DIFFER,
  INTEGRATION_MULTI_TENANT,
  INTEGRATION_RUN_RECORDER,
  INTEGRATION_SINK
} from "./chunk-S7C6TIIF.js";
import {
  __decorateClass,
  __decorateParam
} from "./chunk-2E224ZSN.js";

// runtime/subsystems/integration/execute-integration.use-case.ts
import { Inject, Injectable, Logger, Optional } from "@nestjs/common";
var ExecuteIntegrationUseCase = class {
  constructor(source, cursors, differ, sink, recorder, multiTenant = false) {
    this.source = source;
    this.cursors = cursors;
    this.differ = differ;
    this.sink = sink;
    this.recorder = recorder;
    this.multiTenant = multiTenant;
  }
  source;
  cursors;
  differ;
  sink;
  recorder;
  multiTenant;
  logger = new Logger(ExecuteIntegrationUseCase.name);
  async execute(input) {
    assertTenantId(input.tenantId, {
      multiTenant: this.multiTenant,
      operation: "execute"
    });
    const source = input.sourceOverride ?? this.source;
    const startedAt = Date.now();
    const cursorBefore = await this.cursors.get(input.subscription.id, input.tenantId);
    const { id: runId } = await this.recorder.startRun({
      subscriptionId: input.subscription.id,
      direction: input.direction,
      action: input.action,
      cursorBefore,
      tenantId: input.tenantId
    });
    let recordsFound = 0;
    let recordsProcessed = 0;
    let recordsFailed = 0;
    let latestCursor = cursorBefore;
    let cursorAdvanced = false;
    let runError = null;
    let status = "no_changes";
    try {
      for await (const change of source.listChanges(input.subscription, cursorBefore)) {
        recordsFound++;
        latestCursor = change.cursor;
        cursorAdvanced = true;
        try {
          await this.processChange(runId, input, change);
          recordsProcessed++;
        } catch (err) {
          recordsFailed++;
          const message = err instanceof Error ? err.message : String(err);
          this.logger.warn(
            `integration item failed: subscription=${input.subscription.id} externalId=${change.externalId}: ${message}`
          );
          await this.recorder.recordItem({
            integrationRunId: runId,
            entityType: input.subscription.domain,
            externalId: change.externalId,
            operation: change.operation === "deleted" ? "deleted" : "updated",
            status: "failed",
            changedFields: {},
            error: message,
            tenantId: input.tenantId
          });
        }
      }
      if (recordsFailed > 0 && recordsProcessed === 0 && recordsFound > 0) {
        status = "failed";
        runError = `all ${recordsFailed} records failed`;
      } else if (recordsFound === 0) {
        status = "no_changes";
      } else {
        status = "success";
      }
    } catch (err) {
      status = "failed";
      runError = err instanceof Error ? err.message : String(err);
      this.logger.error(
        `integration source failed: subscription=${input.subscription.id}: ${runError}`
      );
    }
    if (cursorAdvanced && latestCursor !== null && latestCursor !== void 0) {
      try {
        await this.cursors.put(input.subscription.id, latestCursor, input.tenantId);
      } catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        this.logger.error(
          `cursor put failed: subscription=${input.subscription.id}: ${message}`
        );
        if (status !== "failed") {
          status = "failed";
          runError = `cursor put failed: ${message}`;
        }
      }
    }
    const durationMs = Date.now() - startedAt;
    await this.recorder.completeRun(runId, {
      status,
      recordsFound,
      recordsProcessed,
      cursorAfter: cursorAdvanced ? latestCursor : cursorBefore,
      durationMs,
      error: runError
    });
    return {
      runId,
      status,
      recordsFound,
      recordsProcessed,
      recordsFailed,
      cursorBefore,
      cursorAfter: cursorAdvanced ? latestCursor : cursorBefore,
      durationMs,
      error: runError
    };
  }
  async processChange(runId, input, change) {
    if (change.operation === "deleted") {
      const result = await this.sink.softDeleteByExternalId(
        input.userId,
        change.externalId
      );
      await this.recorder.recordItem({
        integrationRunId: runId,
        entityType: input.subscription.domain,
        externalId: change.externalId,
        localId: result?.id ?? null,
        operation: result ? "deleted" : "noop",
        status: "success",
        changedFields: {},
        tenantId: input.tenantId
      });
      return;
    }
    const existing = await this.sink.findByExternalId(
      input.userId,
      change.externalId
    );
    const diff = this.differ.diff(
      existing,
      change.record,
      change.providerChangedFields
    );
    if (diff === "noop") {
      if (!this.sink.reprojectsOnNoop) {
        await this.recorder.recordItem({
          integrationRunId: runId,
          entityType: input.subscription.domain,
          externalId: change.externalId,
          localId: null,
          operation: "noop",
          status: "success",
          changedFields: {},
          tenantId: input.tenantId
        });
        return;
      }
      const { id: noopLocalId } = await this.sink.upsertByExternalId(
        input.userId,
        change.record,
        input.provider
      );
      await this.recorder.recordItem({
        integrationRunId: runId,
        entityType: input.subscription.domain,
        externalId: change.externalId,
        localId: noopLocalId,
        operation: "noop",
        status: "success",
        changedFields: {},
        tenantId: input.tenantId
      });
      return;
    }
    const { id: localId } = await this.sink.upsertByExternalId(
      input.userId,
      change.record,
      input.provider
    );
    await this.recorder.recordItem({
      integrationRunId: runId,
      entityType: input.subscription.domain,
      externalId: change.externalId,
      localId,
      operation: existing === null ? "created" : "updated",
      status: "success",
      changedFields: diff,
      tenantId: input.tenantId
    });
  }
};
ExecuteIntegrationUseCase = __decorateClass([
  Injectable(),
  __decorateParam(0, Inject(INTEGRATION_CHANGE_SOURCE)),
  __decorateParam(1, Inject(INTEGRATION_CURSOR_STORE)),
  __decorateParam(2, Inject(INTEGRATION_FIELD_DIFFER)),
  __decorateParam(3, Inject(INTEGRATION_SINK)),
  __decorateParam(4, Inject(INTEGRATION_RUN_RECORDER)),
  __decorateParam(5, Optional()),
  __decorateParam(5, Inject(INTEGRATION_MULTI_TENANT))
], ExecuteIntegrationUseCase);

export {
  ExecuteIntegrationUseCase
};
//# sourceMappingURL=chunk-OGIZXGPY.js.map