import {
  findOneWithDecryption,
  findWithDecryption
} from "@open-mercato/shared/lib/encryption/find";
import { AiPendingAction } from "../entities.js";
import {
  AI_PENDING_ACTION_ALLOWED_TRANSITIONS,
  AiPendingActionStateError,
  resolveAiPendingActionTtlSeconds
} from "../../lib/pending-action-types.js";
class AiPendingActionRepository {
  constructor(em) {
    this.em = em;
  }
  async create(input, ctx) {
    if (!ctx?.tenantId) {
      throw new Error("AiPendingActionRepository.create requires tenantId");
    }
    if (!input?.agentId) {
      throw new Error("AiPendingActionRepository.create requires agentId");
    }
    if (!input?.toolName) {
      throw new Error("AiPendingActionRepository.create requires toolName");
    }
    if (!input?.idempotencyKey) {
      throw new Error(
        "AiPendingActionRepository.create requires idempotencyKey"
      );
    }
    if (!input?.createdByUserId) {
      throw new Error(
        "AiPendingActionRepository.create requires createdByUserId"
      );
    }
    const now = input.now ?? /* @__PURE__ */ new Date();
    const ttlSeconds = Math.max(
      1,
      Math.floor(
        typeof input.ttlSeconds === "number" && Number.isFinite(input.ttlSeconds) ? input.ttlSeconds : resolveAiPendingActionTtlSeconds()
      )
    );
    const expiresAt = new Date(now.getTime() + ttlSeconds * 1e3);
    return this.em.transactional(async (tx) => {
      const existing = await findOneWithDecryption(
        tx,
        AiPendingAction,
        {
          tenantId: ctx.tenantId,
          organizationId: ctx.organizationId ?? null,
          idempotencyKey: input.idempotencyKey
        },
        { orderBy: { createdAt: "desc" } },
        {
          tenantId: ctx.tenantId ?? null,
          organizationId: ctx.organizationId ?? null
        }
      );
      if (existing && existing.status === "pending") {
        return existing;
      }
      if (existing && (existing.status === "failed" || existing.status === "cancelled" || existing.status === "expired")) {
        await tx.remove(existing).flush();
      }
      const row = tx.create(AiPendingAction, {
        tenantId: ctx.tenantId,
        organizationId: ctx.organizationId ?? null,
        agentId: input.agentId,
        toolName: input.toolName,
        conversationId: input.conversationId ?? null,
        targetEntityType: input.targetEntityType ?? null,
        targetRecordId: input.targetRecordId ?? null,
        normalizedInput: input.normalizedInput ?? {},
        fieldDiff: Array.isArray(input.fieldDiff) ? input.fieldDiff : [],
        records: normalizeRecords(input.records),
        failedRecords: null,
        sideEffectsSummary: input.sideEffectsSummary ?? null,
        recordVersion: input.recordVersion ?? null,
        attachmentIds: Array.isArray(input.attachmentIds) ? input.attachmentIds : [],
        idempotencyKey: input.idempotencyKey,
        createdByUserId: input.createdByUserId,
        status: "pending",
        queueMode: input.queueMode ?? "inline",
        executionResult: null,
        createdAt: now,
        expiresAt,
        resolvedAt: null,
        resolvedByUserId: null
      });
      await tx.persist(row).flush();
      return row;
    });
  }
  async getById(id, ctx) {
    if (!id || !ctx?.tenantId) return null;
    const row = await findOneWithDecryption(
      this.em,
      AiPendingAction,
      {
        id,
        tenantId: ctx.tenantId,
        organizationId: ctx.organizationId ?? null
      },
      {},
      {
        tenantId: ctx.tenantId ?? null,
        organizationId: ctx.organizationId ?? null
      }
    );
    return row ?? null;
  }
  async listPendingForAgent(agentId, ctx, limit = 50) {
    if (!agentId || !ctx?.tenantId) return [];
    const capped = Math.max(1, Math.min(Math.floor(limit), 200));
    const rows = await findWithDecryption(
      this.em,
      AiPendingAction,
      {
        tenantId: ctx.tenantId,
        organizationId: ctx.organizationId ?? null,
        agentId,
        status: "pending"
      },
      {
        orderBy: { createdAt: "desc" },
        limit: capped
      },
      {
        tenantId: ctx.tenantId ?? null,
        organizationId: ctx.organizationId ?? null
      }
    );
    return rows;
  }
  async setStatus(id, nextStatus, ctx, extra) {
    if (!ctx?.tenantId) {
      throw new Error("AiPendingActionRepository.setStatus requires tenantId");
    }
    if (!id) {
      throw new Error("AiPendingActionRepository.setStatus requires id");
    }
    return this.em.transactional(async (tx) => {
      const existing = await findOneWithDecryption(
        tx,
        AiPendingAction,
        {
          id,
          tenantId: ctx.tenantId,
          organizationId: ctx.organizationId ?? null
        },
        {},
        {
          tenantId: ctx.tenantId ?? null,
          organizationId: ctx.organizationId ?? null
        }
      );
      if (!existing) {
        throw new Error(`AiPendingAction not found: ${id}`);
      }
      if (existing.status === nextStatus) {
        return existing;
      }
      const allowed = AI_PENDING_ACTION_ALLOWED_TRANSITIONS[existing.status] ?? [];
      if (!allowed.includes(nextStatus)) {
        throw new AiPendingActionStateError(existing.status, nextStatus);
      }
      const now = extra?.now ?? /* @__PURE__ */ new Date();
      existing.status = nextStatus;
      if (nextStatus === "confirmed" || nextStatus === "cancelled" || nextStatus === "expired" || nextStatus === "failed") {
        existing.resolvedAt = existing.resolvedAt ?? now;
        if (extra && Object.prototype.hasOwnProperty.call(extra, "resolvedByUserId")) {
          existing.resolvedByUserId = extra.resolvedByUserId ?? null;
        } else if (nextStatus === "expired") {
          existing.resolvedByUserId = null;
        }
      }
      if (extra && Object.prototype.hasOwnProperty.call(extra, "executionResult")) {
        existing.executionResult = extra.executionResult ?? null;
      }
      if (extra && Object.prototype.hasOwnProperty.call(extra, "failedRecords")) {
        existing.failedRecords = normalizeFailedRecords(extra.failedRecords);
      }
      await tx.persist(existing).flush();
      return existing;
    });
  }
  async listExpired(ctx, now, limit = 100) {
    if (!ctx?.tenantId) return [];
    const capped = Math.max(1, Math.min(Math.floor(limit), 500));
    const rows = await findWithDecryption(
      this.em,
      AiPendingAction,
      {
        tenantId: ctx.tenantId,
        organizationId: ctx.organizationId ?? null,
        status: "pending",
        expiresAt: { $lt: now }
      },
      {
        orderBy: { expiresAt: "asc" },
        limit: capped
      },
      {
        tenantId: ctx.tenantId ?? null,
        organizationId: ctx.organizationId ?? null
      }
    );
    return rows;
  }
}
function normalizeRecords(records) {
  if (!Array.isArray(records) || records.length === 0) return null;
  return records;
}
function normalizeFailedRecords(failed) {
  if (!Array.isArray(failed) || failed.length === 0) return null;
  return failed;
}
var AiPendingActionRepository_default = AiPendingActionRepository;
export {
  AiPendingActionRepository,
  AiPendingActionRepository_default as default
};
//# sourceMappingURL=AiPendingActionRepository.js.map
