import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { formatZodErrors } from "./validation.js";
const LAST_OPERATION_LOG_ENTRY = /* @__PURE__ */ Symbol("inboxOps:lastOperationLogEntry");
function asHelperContext(ctx) {
  return ctx;
}
function clearLatestCommandLogEntry(ctx) {
  ;
  ctx[LAST_OPERATION_LOG_ENTRY] = null;
}
function readLatestCommandLogEntry(ctx) {
  return ctx[LAST_OPERATION_LOG_ENTRY] ?? null;
}
function hasSuperAdminAccess(auth) {
  if (!auth || typeof auth !== "object") return false;
  return auth.isSuperAdmin === true;
}
class ExecutionError extends Error {
  constructor(message, statusCode = 400) {
    super(message);
    this.statusCode = statusCode;
  }
}
async function executeCommand(ctx, commandId, input) {
  const commandBus = ctx.container.resolve("commandBus");
  if (!commandBus || typeof commandBus.execute !== "function") {
    throw new ExecutionError("Command bus is not available", 503);
  }
  const auth = ctx.auth ?? {
    sub: ctx.userId,
    userId: ctx.userId,
    tenantId: ctx.tenantId,
    orgId: ctx.organizationId,
    isSuperAdmin: false
  };
  const commandContext = {
    container: ctx.container,
    auth,
    organizationScope: null,
    selectedOrganizationId: ctx.organizationId,
    organizationIds: [ctx.organizationId]
  };
  const { result, logEntry } = await commandBus.execute(commandId, {
    input,
    ctx: commandContext
  });
  if (logEntry?.undoToken && logEntry.id && logEntry.commandId) {
    ;
    ctx[LAST_OPERATION_LOG_ENTRY] = logEntry;
  }
  return result;
}
async function userHasFeature(ctx, feature) {
  if (!feature || feature.trim().length === 0) return false;
  if (hasSuperAdminAccess(ctx.auth)) return true;
  try {
    const rbacService = ctx.container.resolve("rbacService");
    if (!rbacService || typeof rbacService.userHasAllFeatures !== "function") {
      return false;
    }
    return rbacService.userHasAllFeatures(
      ctx.userId,
      [feature],
      { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
    );
  } catch {
    return false;
  }
}
function resolveEntityClass(ctx, key) {
  const fromEntities = ctx.entities?.[key];
  if (fromEntities) return fromEntities;
  try {
    return ctx.container.resolve(key);
  } catch {
    return null;
  }
}
function buildSourceMetadata(actionId, proposalId) {
  return {
    source: "inbox_ops",
    inboxOpsActionId: actionId,
    inboxOpsProposalId: proposalId
  };
}
async function resolveOrderByReference(ctx, orderId, orderNumber) {
  const SalesOrderClass = resolveEntityClass(ctx, "SalesOrder");
  if (!SalesOrderClass) {
    throw new ExecutionError("Sales module entities not available", 503);
  }
  const where = {
    tenantId: ctx.tenantId,
    organizationId: ctx.organizationId,
    deletedAt: null
  };
  if (orderId) {
    where.id = orderId;
  } else if (orderNumber && orderNumber.trim().length > 0) {
    where.orderNumber = orderNumber.trim();
  } else {
    throw new ExecutionError("Order reference is required", 400);
  }
  const order = await findOneWithDecryption(
    ctx.em,
    SalesOrderClass,
    where,
    void 0,
    { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
  );
  if (!order) {
    throw new ExecutionError("Referenced order not found", 404);
  }
  return order;
}
async function resolveFirstChannelId(ctx) {
  const SalesChannelClass = resolveEntityClass(ctx, "SalesChannel");
  if (!SalesChannelClass) return null;
  try {
    const channel = await findOneWithDecryption(
      ctx.em,
      SalesChannelClass,
      {
        tenantId: ctx.tenantId,
        organizationId: ctx.organizationId,
        deletedAt: null
      },
      { orderBy: { name: "ASC" } },
      { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
    );
    return channel?.id ?? null;
  } catch {
    return null;
  }
}
async function resolveChannelCurrency(ctx, channelId) {
  const SalesChannelClass = resolveEntityClass(ctx, "SalesChannel");
  if (!SalesChannelClass) return null;
  try {
    const where = {
      tenantId: ctx.tenantId,
      organizationId: ctx.organizationId,
      deletedAt: null
    };
    if (channelId) where.id = channelId;
    const channel = await findOneWithDecryption(
      ctx.em,
      SalesChannelClass,
      where,
      channelId ? void 0 : { orderBy: { name: "ASC" } },
      { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
    );
    return channel?.currencyCode ?? null;
  } catch {
    return null;
  }
}
async function resolveEffectiveDocumentKind(ctx, channelId) {
  const SalesChannelClass = resolveEntityClass(ctx, "SalesChannel");
  if (!SalesChannelClass) return "order";
  const channel = await findOneWithDecryption(
    ctx.em,
    SalesChannelClass,
    {
      id: channelId,
      tenantId: ctx.tenantId,
      organizationId: ctx.organizationId,
      deletedAt: null
    },
    void 0,
    { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
  );
  if (!channel) return "order";
  const metadata = channel.metadata;
  if (metadata?.quotesRequired === true) {
    return "quote";
  }
  return "order";
}
const SALES_SHIPMENT_STATUS_DICTIONARY_KEY = "sales.shipment_status";
async function resolveShipmentStatusEntryId(ctx, statusLabel) {
  const DictionaryClass = resolveEntityClass(ctx, "Dictionary");
  const DictionaryEntryClass = resolveEntityClass(ctx, "DictionaryEntry");
  if (!DictionaryClass || !DictionaryEntryClass) return null;
  const encryptionScope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId };
  const dictionary = await findOneWithDecryption(
    ctx.em,
    DictionaryClass,
    {
      key: SALES_SHIPMENT_STATUS_DICTIONARY_KEY,
      tenantId: ctx.tenantId,
      organizationId: ctx.organizationId,
      deletedAt: null
    },
    void 0,
    encryptionScope
  );
  if (!dictionary) return null;
  const entries = await findWithDecryption(
    ctx.em,
    DictionaryEntryClass,
    {
      dictionary: dictionary.id,
      tenantId: ctx.tenantId,
      organizationId: ctx.organizationId
    },
    void 0,
    encryptionScope
  );
  if (!entries.length) return null;
  const normalizedTarget = normalizeDictionaryToken(statusLabel);
  const loweredTarget = statusLabel.trim().toLowerCase();
  const match = entries.find((entry) => {
    const label = entry.label.trim().toLowerCase();
    const value = entry.value.trim().toLowerCase();
    return entry.normalizedValue === normalizedTarget || label === loweredTarget || value === loweredTarget;
  });
  return match?.id ?? null;
}
async function resolveCustomerEntityIdByEmail(ctx, email) {
  const normalized = email.trim().toLowerCase();
  if (!normalized) return null;
  const CustomerEntityClass = resolveEntityClass(ctx, "CustomerEntity");
  if (!CustomerEntityClass) return null;
  const entity = await findOneWithDecryption(
    ctx.em,
    CustomerEntityClass,
    {
      primaryEmail: normalized,
      tenantId: ctx.tenantId,
      organizationId: ctx.organizationId,
      deletedAt: null
    },
    void 0,
    { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
  );
  if (entity) return entity.id;
  const candidates = await findWithDecryption(
    ctx.em,
    CustomerEntityClass,
    {
      tenantId: ctx.tenantId,
      organizationId: ctx.organizationId,
      deletedAt: null
    },
    { limit: 100, orderBy: { createdAt: "DESC" } },
    { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
  );
  const match = candidates.find(
    (e) => e.primaryEmail && e.primaryEmail.toLowerCase() === normalized
  );
  return match?.id ?? null;
}
async function resolveContactIdByNameAndType(ctx, contactName, contactType) {
  const CustomerEntityClass = resolveEntityClass(ctx, "CustomerEntity");
  if (!CustomerEntityClass) return null;
  const normalized = contactName.trim();
  if (!normalized) return null;
  const entity = await findOneWithDecryption(
    ctx.em,
    CustomerEntityClass,
    {
      displayName: normalized,
      kind: contactType,
      tenantId: ctx.tenantId,
      organizationId: ctx.organizationId,
      deletedAt: null
    },
    void 0,
    { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
  );
  return entity?.id ?? null;
}
async function loadOrderLineItems(ctx, orderId) {
  try {
    const result = await executeCommand(
      ctx,
      "sales.orders.lines.list",
      { orderId, organizationId: ctx.organizationId, tenantId: ctx.tenantId }
    );
    return result.lines ?? [];
  } catch {
    return [];
  }
}
function matchLineItemByName(orderLines, lineItemName) {
  const target = lineItemName.trim().toLowerCase();
  if (!target) return null;
  const exact = orderLines.find((l) => (l.name || "").trim().toLowerCase() === target);
  if (exact) return exact.id;
  const partial = orderLines.find((l) => {
    const name = (l.name || "").trim().toLowerCase();
    return name.includes(target) || target.includes(name);
  });
  return partial?.id ?? null;
}
function normalizeAddressSnapshot(address) {
  return {
    addressLine1: address.line1 ?? address.addressLine1 ?? "",
    addressLine2: address.line2 ?? address.addressLine2 ?? null,
    companyName: address.company ?? address.companyName ?? null,
    name: address.contactName ?? address.name ?? null,
    city: address.city ?? null,
    region: address.state ?? address.region ?? null,
    postalCode: address.postalCode ?? null,
    country: address.country ?? null
  };
}
function parseDateToken(value) {
  if (!value) return void 0;
  const parsed = new Date(value);
  if (Number.isNaN(parsed.getTime())) return void 0;
  return parsed;
}
function parseNumberToken(value, fieldName) {
  const parsed = Number(value);
  if (!Number.isFinite(parsed)) {
    throw new ExecutionError(`Invalid numeric value for ${fieldName}`, 400);
  }
  return parsed;
}
function normalizeDictionaryToken(value) {
  return value.trim().toLowerCase().replace(/[\s-]+/g, "_");
}
async function resolveProductDiscrepanciesInProposal(em, proposalId, productTitle, productId, scope) {
  const { InboxDiscrepancy, InboxProposalAction } = await import("../data/entities.js");
  const discrepancies = await findWithDecryption(
    em,
    InboxDiscrepancy,
    {
      proposalId,
      type: "product_not_found",
      resolved: false,
      tenantId: scope.tenantId,
      organizationId: scope.organizationId
    },
    void 0,
    scope
  );
  const normalizedTitle = productTitle.toLowerCase().trim();
  const matchingDiscrepancies = discrepancies.filter((d) => {
    const foundValue = (d.foundValue || "").toLowerCase().trim();
    return foundValue === normalizedTitle;
  });
  if (matchingDiscrepancies.length === 0) return;
  for (const discrepancy of matchingDiscrepancies) {
    discrepancy.resolved = true;
  }
  await em.flush();
  const actionIds = matchingDiscrepancies.map((d) => d.actionId).filter((id) => !!id);
  for (const actionId of actionIds) {
    const action = await findOneWithDecryption(
      em,
      InboxProposalAction,
      { id: actionId, deletedAt: null },
      void 0,
      scope
    );
    if (!action) continue;
    const payload = action.payload;
    const lineItems = Array.isArray(payload?.lineItems) ? payload.lineItems : [];
    let updated = false;
    for (const item of lineItems) {
      if (item.productId) continue;
      const itemName = (typeof item.productName === "string" ? item.productName : "").toLowerCase().trim();
      if (itemName === normalizedTitle) {
        item.productId = productId;
        updated = true;
        break;
      }
    }
    if (updated) {
      action.payload = { ...payload, lineItems };
    }
  }
  if (actionIds.length > 0) {
    await em.flush();
  }
}
export {
  ExecutionError,
  asHelperContext,
  buildSourceMetadata,
  clearLatestCommandLogEntry,
  executeCommand,
  formatZodErrors,
  loadOrderLineItems,
  matchLineItemByName,
  normalizeAddressSnapshot,
  normalizeDictionaryToken,
  parseDateToken,
  parseNumberToken,
  readLatestCommandLogEntry,
  resolveChannelCurrency,
  resolveContactIdByNameAndType,
  resolveCustomerEntityIdByEmail,
  resolveEffectiveDocumentKind,
  resolveEntityClass,
  resolveFirstChannelId,
  resolveOrderByReference,
  resolveProductDiscrepanciesInProposal,
  resolveShipmentStatusEntryId,
  userHasFeature
};
//# sourceMappingURL=executionHelpers.js.map
