import { findAndCountWithDecryption, findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
import { escapeLikePattern } from "@open-mercato/shared/lib/db/escapeLikePattern";
import { SyncCursor, SyncRun } from "../data/entities.js";
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
function buildRunSearchFilter(search) {
  const trimmed = search.trim();
  if (!trimmed) return null;
  const pattern = `%${escapeLikePattern(trimmed)}%`;
  const conditions = [
    { integrationId: { $ilike: pattern } },
    { entityType: { $ilike: pattern } },
    { status: { $ilike: pattern } }
  ];
  if (UUID_PATTERN.test(trimmed)) {
    conditions.push({ id: trimmed });
  }
  return conditions;
}
function createSyncRunService(em) {
  async function resolveCursorRow(run, scope) {
    return findOneWithDecryption(
      em,
      SyncCursor,
      {
        integrationId: run.integrationId,
        entityType: run.entityType,
        direction: run.direction,
        organizationId: scope.organizationId,
        tenantId: scope.tenantId
      },
      void 0,
      scope
    );
  }
  function applyCursorMutation(run, cursorRow, cursor, scope) {
    run.cursor = cursor;
    if (cursorRow) {
      cursorRow.cursor = cursor;
    } else {
      em.create(SyncCursor, {
        integrationId: run.integrationId,
        entityType: run.entityType,
        direction: run.direction,
        cursor,
        organizationId: scope.organizationId,
        tenantId: scope.tenantId
      });
    }
  }
  return {
    async createRun(input, scope) {
      const row = em.create(SyncRun, {
        integrationId: input.integrationId,
        entityType: input.entityType,
        direction: input.direction,
        status: "pending",
        cursor: input.cursor,
        initialCursor: input.cursor,
        triggeredBy: input.triggeredBy,
        progressJobId: input.progressJobId,
        jobId: input.jobId,
        organizationId: scope.organizationId,
        tenantId: scope.tenantId
      });
      await em.persist(row).flush();
      return row;
    },
    async getRun(runId, scope) {
      return findOneWithDecryption(
        em,
        SyncRun,
        {
          id: runId,
          organizationId: scope.organizationId,
          tenantId: scope.tenantId,
          deletedAt: null
        },
        void 0,
        scope
      );
    },
    async listRuns(query, scope) {
      const where = {
        organizationId: scope.organizationId,
        tenantId: scope.tenantId,
        deletedAt: null
      };
      if (query.integrationId) where.integrationId = query.integrationId;
      if (query.entityType) where.entityType = query.entityType;
      if (query.direction) where.direction = query.direction;
      if (query.status) where.status = query.status;
      if (query.search) {
        const searchConditions = buildRunSearchFilter(query.search);
        if (searchConditions) where.$or = searchConditions;
      }
      const [items, total] = await findAndCountWithDecryption(
        em,
        SyncRun,
        where,
        {
          orderBy: { createdAt: "DESC" },
          limit: query.pageSize,
          offset: (query.page - 1) * query.pageSize
        },
        scope
      );
      return { items, total };
    },
    async markStatus(runId, status, scope, error) {
      if (status === "running") {
        const updated = await em.nativeUpdate(
          SyncRun,
          {
            id: runId,
            organizationId: scope.organizationId,
            tenantId: scope.tenantId,
            deletedAt: null,
            status: "pending"
          },
          {
            status,
            ...error !== void 0 ? { lastError: error } : {},
            updatedAt: /* @__PURE__ */ new Date()
          }
        );
        if (updated === 0) return null;
        const row2 = await this.getRun(runId, scope);
        if (row2 && typeof em.refresh === "function") {
          await em.refresh(row2);
        }
        return row2;
      }
      const row = await this.getRun(runId, scope);
      if (!row) return null;
      const isTerminal = row.status === "completed" || row.status === "failed" || row.status === "cancelled";
      if (isTerminal && row.status !== status) {
        return row;
      }
      row.status = status;
      if (error !== void 0) row.lastError = error;
      await em.flush();
      return row;
    },
    async updateCounts(runId, delta, scope) {
      const row = await this.getRun(runId, scope);
      if (!row) return null;
      row.createdCount += delta.createdCount ?? 0;
      row.updatedCount += delta.updatedCount ?? 0;
      row.skippedCount += delta.skippedCount ?? 0;
      row.failedCount += delta.failedCount ?? 0;
      row.batchesCompleted += delta.batchesCompleted ?? 0;
      await em.flush();
      return row;
    },
    async updateCursor(runId, cursor, scope) {
      const run = await this.getRun(runId, scope);
      if (!run) return;
      const cursorRow = await resolveCursorRow(run, scope);
      await withAtomicFlush(em, [
        () => applyCursorMutation(run, cursorRow, cursor, scope)
      ], { transaction: true });
    },
    async commitBatchProgress(runId, delta, cursor, scope) {
      const run = await this.getRun(runId, scope);
      if (!run) return null;
      const cursorRow = await resolveCursorRow(run, scope);
      await withAtomicFlush(em, [
        () => {
          run.createdCount += delta.createdCount ?? 0;
          run.updatedCount += delta.updatedCount ?? 0;
          run.skippedCount += delta.skippedCount ?? 0;
          run.failedCount += delta.failedCount ?? 0;
          run.batchesCompleted += delta.batchesCompleted ?? 0;
          applyCursorMutation(run, cursorRow, cursor, scope);
        }
      ], { transaction: true });
      return run;
    },
    async resolveCursor(integrationId, entityType, direction, scope) {
      const row = await findOneWithDecryption(
        em,
        SyncCursor,
        {
          integrationId,
          entityType,
          direction,
          organizationId: scope.organizationId,
          tenantId: scope.tenantId
        },
        void 0,
        scope
      );
      return row?.cursor ?? null;
    },
    async findRunningOverlap(integrationId, entityType, direction, scope) {
      const [run] = await findWithDecryption(
        em,
        SyncRun,
        {
          integrationId,
          entityType,
          direction,
          status: { $in: ["pending", "running"] },
          organizationId: scope.organizationId,
          tenantId: scope.tenantId,
          deletedAt: null
        },
        { limit: 1 },
        scope
      );
      return run ?? null;
    }
  };
}
export {
  createSyncRunService
};
//# sourceMappingURL=sync-run-service.js.map
