var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// src/solution/BaseSales/utils/cartPromotion.ts
var cartPromotion_exports = {};
__export(cartPromotion_exports, {
  appendPromotionTags: () => appendPromotionTags,
  buildConsolidateKeyForPromotion: () => buildConsolidateKeyForPromotion,
  calculateUnfulfilledPromotions: () => calculateUnfulfilledPromotions,
  extractProductMatchIdentifiersFromStrategyConfig: () => extractProductMatchIdentifiersFromStrategyConfig,
  getOrderProductBasePrice: () => getOrderProductBasePrice,
  getOrderProductOriginalPriceForPromotion: () => getOrderProductOriginalPriceForPromotion,
  getOrderProductUid: () => getOrderProductUid,
  mergeIdenticalPromotionCartLines: () => mergeIdenticalPromotionCartLines,
  processCartPromotion: () => processCartPromotion,
  resolveStrategyEligibleProducts: () => resolveStrategyEligibleProducts,
  resolveStrategyRequiredQuantity: () => resolveStrategyRequiredQuantity
});
module.exports = __toCommonJS(cartPromotion_exports);
var import_decimal = __toESM(require("decimal.js"));
var import_lodash_es = require("lodash-es");
var import_utils = require("../../../modules/Order/utils");
var import_utils2 = require("../../ScanOrder/utils");
function getOrderProductBasePrice(product) {
  const metadata = (product == null ? void 0 : product.metadata) || {};
  const optionSum = (0, import_utils.sumOptionUnitPrice)((0, import_utils.getProductSkuOptions)(product)).toNumber();
  if (metadata.source_product_price !== void 0) {
    const v2 = Number(metadata.source_product_price);
    return Number.isFinite(v2) ? v2 : 0;
  }
  if (metadata.main_product_original_price !== void 0) {
    const v2 = Number(metadata.main_product_original_price) - optionSum;
    return Number.isFinite(v2) ? v2 : 0;
  }
  const v = Number((product == null ? void 0 : product.original_price) ?? (product == null ? void 0 : product.selling_price) ?? 0);
  return Number.isFinite(v) ? v : 0;
}
function getOrderProductOriginalPriceForPromotion(product) {
  var _a;
  const metadata = (product == null ? void 0 : product.metadata) || {};
  const optionSum = (0, import_utils.sumOptionUnitPrice)((0, import_utils.getProductSkuOptions)(product)).toNumber();
  if (metadata.main_product_original_price !== void 0 && metadata.main_product_original_price !== null && metadata.main_product_original_price !== "") {
    const v = Number(metadata.main_product_original_price) - optionSum;
    if (Number.isFinite(v) && v >= 0)
      return v;
  }
  const promoOriginal = (_a = metadata._promotion) == null ? void 0 : _a.originalPrice;
  if (promoOriginal !== void 0 && promoOriginal !== null && promoOriginal !== "") {
    const v = Number(promoOriginal);
    if (Number.isFinite(v) && v >= 0)
      return v;
  }
  return getOrderProductBasePrice(product);
}
function updatePromotionDiscountList(item, promotionDiscount) {
  let discountList = Array.isArray(item.discount_list) ? [...item.discount_list] : [];
  discountList = discountList.filter((d) => (d == null ? void 0 : d.type) !== "promotion");
  if (promotionDiscount) {
    discountList.push({
      type: "promotion",
      amount: promotionDiscount.amount,
      discount: {
        original_amount: promotionDiscount.original_amount,
        fixed_amount: promotionDiscount.fixed_amount,
        title: promotionDiscount.title,
        resource_id: ""
      }
    });
  }
  if (discountList.length > 0) {
    item.discount_list = discountList;
  } else {
    delete item.discount_list;
  }
}
var X_ITEMS_FOR_Y_PRICE = "X_ITEMS_FOR_Y_PRICE";
function applyPromoUnitPriceToLine(item, input) {
  const metadata = item.metadata || (item.metadata = {});
  const optionSum = (0, import_utils.sumOptionUnitPrice)((0, import_utils.getProductSkuOptions)(item)).toNumber();
  const promoUnitPrice = new import_decimal.default(Number(input.promoUnitPrice) || 0).toDecimalPlaces(2).toFixed(2);
  const mainSelling = new import_decimal.default(Number(promoUnitPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
  const catalogSource = new import_decimal.default(input.originalBasePrice).toDecimalPlaces(2).toFixed(2);
  metadata.source_product_price = catalogSource;
  metadata.main_product_original_price = new import_decimal.default(Number(catalogSource) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
  metadata.main_product_selling_price = mainSelling;
  metadata.price_schema_version = 2;
  item.selling_price = (0, import_utils.composeLinePrice)({
    mainPrice: mainSelling,
    bundle: item.product_bundle
  });
  item.original_price = (0, import_utils.composeLinePrice)({
    mainPrice: metadata.main_product_original_price,
    bundle: item.product_bundle,
    useOriginalBundle: true
  });
  const discountAmount = new import_decimal.default(input.originalBasePrice).minus(Number(promoUnitPrice) || 0).toDecimalPlaces(2).toFixed(2);
  if (Number(catalogSource) !== Number(promoUnitPrice)) {
    updatePromotionDiscountList(item, {
      title: input.strategyTitle || "",
      amount: discountAmount,
      original_amount: catalogSource,
      fixed_amount: discountAmount
    });
  } else {
    updatePromotionDiscountList(item, null);
  }
}
function getPromotionActionType(promotion) {
  var _a, _b;
  if (promotion == null ? void 0 : promotion.actionType)
    return String(promotion.actionType);
  const actions = (_a = promotion == null ? void 0 : promotion.strategyMetadata) == null ? void 0 : _a.actions;
  if (Array.isArray(actions) && ((_b = actions[0]) == null ? void 0 : _b.type))
    return String(actions[0].type);
  return void 0;
}
function resolveXItemsExpectedUnitPrice(promotion, sourceCartNum) {
  if (getPromotionActionType(promotion) !== X_ITEMS_FOR_Y_PRICE)
    return void 0;
  const detail = promotion == null ? void 0 : promotion.actionDetail;
  const x = Number(detail == null ? void 0 : detail.x);
  const y = Number(detail == null ? void 0 : detail.price);
  if (!Number.isFinite(x) || x <= 0 || !Number.isFinite(y))
    return void 0;
  if (sourceCartNum < x)
    return void 0;
  return Number(new import_decimal.default(y).div(x).toDecimalPlaces(2).toFixed(2));
}
function mergeIdenticalPromotionCartLines(products) {
  var _a, _b, _c, _d;
  if (!Array.isArray(products) || products.length <= 1) {
    return [...products || []];
  }
  const merged = [];
  const indexByKey = /* @__PURE__ */ new Map();
  for (const line of products) {
    if (getGiftInfo(line)) {
      merged.push(line);
      continue;
    }
    const promotion = (_a = line == null ? void 0 : line.metadata) == null ? void 0 : _a._promotion;
    if (!(promotion == null ? void 0 : promotion.inPromotion) || !promotion.strategyId) {
      merged.push(line);
      continue;
    }
    const fingerprint = (0, import_utils2.buildProductLineFingerprint)(
      (0, import_utils.getProductSkuOptions)(line),
      line.product_bundle
    );
    const unitPrice = String(
      ((_b = line.metadata) == null ? void 0 : _b.source_product_price) ?? line.selling_price ?? ""
    );
    const promoUnitPrice = String(
      promotion.finalPrice ?? ((_c = line.metadata) == null ? void 0 : _c.main_product_selling_price) ?? ""
    );
    const bookingUid = (line == null ? void 0 : line.booking_uid) ?? ((_d = line == null ? void 0 : line.metadata) == null ? void 0 : _d.booking_uid);
    const orderDetailId = (line == null ? void 0 : line.order_detail_id) ?? (line == null ? void 0 : line.orderDetailId);
    const mergeKey = [
      line.product_id,
      line.product_variant_id ?? 0,
      fingerprint,
      bookingUid ? String(bookingUid) : "",
      orderDetailId !== void 0 && orderDetailId !== null && orderDetailId !== "" ? String(orderDetailId) : "",
      promotion.strategyId,
      unitPrice,
      promoUnitPrice
    ].join("#");
    const hitIndex = indexByKey.get(mergeKey);
    if (hitIndex === void 0) {
      indexByKey.set(mergeKey, merged.length);
      merged.push(line);
      continue;
    }
    const target = merged[hitIndex];
    target.num = (0, import_utils2.getSafeProductNum)(Number(target.num || 0) + Number(line.num || 0));
  }
  return merged;
}
function dedupePromotionProcessedLines(lines) {
  var _a, _b, _c, _d, _e, _f, _g, _h;
  if (!Array.isArray(lines) || lines.length <= 1)
    return [...lines || []];
  const passthrough = [];
  const buckets = /* @__PURE__ */ new Map();
  for (const line of lines) {
    const promo = (_a = line == null ? void 0 : line.metadata) == null ? void 0 : _a._promotion;
    const originalUid = String(line._originalProductUid || getOrderProductUid(line) || "");
    if (!(promo == null ? void 0 : promo.inPromotion) || !originalUid) {
      passthrough.push(line);
      continue;
    }
    const group = buckets.get(originalUid) || [];
    group.push(line);
    buckets.set(originalUid, group);
  }
  const promotionDelta = (line) => {
    var _a2;
    const promo = (_a2 = line.metadata) == null ? void 0 : _a2._promotion;
    const original = Number(promo == null ? void 0 : promo.originalPrice);
    const final = Number(promo == null ? void 0 : promo.finalPrice);
    if (!Number.isFinite(original) || !Number.isFinite(final))
      return 0;
    return Math.abs(original - final);
  };
  const deduped = [...passthrough];
  for (const [originalUid, group] of buckets) {
    if (group.length === 1) {
      deduped.push(group[0]);
      continue;
    }
    const sourceCartNum = Math.max(
      ...group.map((line) => Number(line._sourceCartNum || line.num || 0))
    );
    const pricingLines = group.filter((line) => promotionDelta(line) > 0);
    const xItemsLines = (pricingLines.length ? pricingLines : group).filter(
      (line) => {
        var _a2;
        return getPromotionActionType((_a2 = line.metadata) == null ? void 0 : _a2._promotion) === X_ITEMS_FOR_Y_PRICE;
      }
    );
    const pool = xItemsLines.length ? xItemsLines : pricingLines.length ? pricingLines : group;
    let winner = pool[0];
    const expectedUnit = resolveXItemsExpectedUnitPrice(
      (_b = winner.metadata) == null ? void 0 : _b._promotion,
      sourceCartNum
    );
    if (expectedUnit != null && pool.length > 1) {
      winner = pool.reduce((best, line) => {
        var _a2, _b2, _c2, _d2;
        const bestDiff = Math.abs(Number((_b2 = (_a2 = best.metadata) == null ? void 0 : _a2._promotion) == null ? void 0 : _b2.finalPrice) - expectedUnit);
        const lineDiff = Math.abs(Number((_d2 = (_c2 = line.metadata) == null ? void 0 : _c2._promotion) == null ? void 0 : _d2.finalPrice) - expectedUnit);
        return lineDiff < bestDiff ? line : best;
      }, pool[0]);
    } else if (pool.length > 1 && !expectedUnit) {
      winner = pool.reduce((best, line) => {
        var _a2, _b2, _c2, _d2, _e2, _f2;
        const bestFinal = Number((_b2 = (_a2 = best.metadata) == null ? void 0 : _a2._promotion) == null ? void 0 : _b2.finalPrice);
        const lineFinal = Number((_d2 = (_c2 = line.metadata) == null ? void 0 : _c2._promotion) == null ? void 0 : _d2.finalPrice);
        const original = Number((_f2 = (_e2 = line.metadata) == null ? void 0 : _e2._promotion) == null ? void 0 : _f2.originalPrice);
        if (Number.isFinite(original) && lineFinal >= original && bestFinal >= original) {
          return lineFinal < bestFinal ? line : best;
        }
        return promotionDelta(line) > promotionDelta(best) ? line : best;
      }, pool[0]);
    }
    const winnerIsXItems = getPromotionActionType((_c = winner.metadata) == null ? void 0 : _c._promotion) === X_ITEMS_FOR_Y_PRICE;
    if (winnerIsXItems) {
      const xItemsGroupLines = group.filter(
        (line) => {
          var _a2;
          return getPromotionActionType((_a2 = line.metadata) == null ? void 0 : _a2._promotion) === X_ITEMS_FOR_Y_PRICE;
        }
      );
      const evaluatedPromoQty = xItemsGroupLines.reduce(
        (sum, line) => sum + Number(line.num || 0),
        0
      );
      winner.num = (0, import_utils2.getSafeProductNum)(evaluatedPromoQty || winner.num || 1);
    } else {
      winner.num = (0, import_utils2.getSafeProductNum)(sourceCartNum || winner.num || 1);
    }
    const recomputeUnit = resolveXItemsExpectedUnitPrice(
      (_d = winner.metadata) == null ? void 0 : _d._promotion,
      sourceCartNum
    );
    if (recomputeUnit != null) {
      const promo = winner.metadata._promotion;
      applyPromoUnitPriceToLine(winner, {
        promoUnitPrice: recomputeUnit,
        originalBasePrice: Number(promo.originalPrice),
        strategyTitle: (_e = promo.strategyMetadata) == null ? void 0 : _e.name
      });
      promo.finalPrice = recomputeUnit;
    }
    for (const line of group) {
      const giftInfo = (_g = (_f = line.metadata) == null ? void 0 : _f._promotion) == null ? void 0 : _g.giftInfo;
      if (giftInfo && ((_h = winner.metadata) == null ? void 0 : _h._promotion) && !winner.metadata._promotion.giftInfo) {
        winner.metadata._promotion.giftInfo = giftInfo;
      }
    }
    deduped.push(winner);
  }
  return deduped;
}
function getOrderProductUid(product) {
  var _a;
  return String(
    ((_a = product == null ? void 0 : product.metadata) == null ? void 0 : _a.unique_identification_number) ?? (product == null ? void 0 : product.unique_identification_number) ?? ""
  );
}
function getGiftInfo(product) {
  var _a;
  return ((_a = product == null ? void 0 : product.metadata) == null ? void 0 : _a._giftInfo) || (product == null ? void 0 : product._giftInfo) || null;
}
function isPersistedGeneratedVoucher(product) {
  const orderDetailId = (product == null ? void 0 : product.order_detail_id) ?? (product == null ? void 0 : product.orderDetailId);
  if (orderDetailId === void 0 || orderDetailId === null || orderDetailId === "") {
    return false;
  }
  const metadata = (product == null ? void 0 : product.metadata) || {};
  return (product == null ? void 0 : product.extension_type) === "product_voucher" || metadata.parent_order_detail_id !== void 0 || metadata.gift_card_type !== void 0;
}
function getProductBundleForEvaluator(product) {
  const bundles = Array.isArray(product == null ? void 0 : product.product_bundle) ? product.product_bundle : [];
  return bundles.map((b) => ({
    product_id: (b == null ? void 0 : b.bundle_product_id) ?? (b == null ? void 0 : b._bundle_product_id) ?? (b == null ? void 0 : b.product_id),
    product_variant_id: (b == null ? void 0 : b.bundle_variant_id) ?? (b == null ? void 0 : b.product_variant_id) ?? 0,
    quantity: Number((b == null ? void 0 : b.num) ?? (b == null ? void 0 : b.quantity) ?? 1) || 1,
    price: Number((b == null ? void 0 : b.bundle_selling_price) ?? (b == null ? void 0 : b.price) ?? 0) || 0,
    original_price: Number((b == null ? void 0 : b.original_price) ?? (b == null ? void 0 : b.product_price) ?? (b == null ? void 0 : b.price) ?? 0) || 0,
    id: (b == null ? void 0 : b.bundle_id) ?? (b == null ? void 0 : b.id) ?? 0,
    _bundle_product_id: (b == null ? void 0 : b._bundle_product_id) ?? (b == null ? void 0 : b.bundle_product_id) ?? (b == null ? void 0 : b.product_id)
  }));
}
function buildConsolidateKeyForPromotion(item) {
  var _a;
  const fingerprint = (0, import_utils2.buildProductLineFingerprint)(
    (0, import_utils.getProductSkuOptions)(item),
    item.product_bundle
  );
  const bookingUid = (item == null ? void 0 : item.booking_uid) ?? ((_a = item == null ? void 0 : item.metadata) == null ? void 0 : _a.booking_uid);
  const orderDetailId = (item == null ? void 0 : item.order_detail_id) ?? (item == null ? void 0 : item.orderDetailId);
  if (orderDetailId !== void 0 && orderDetailId !== null && orderDetailId !== "") {
    return [
      item.product_id,
      item.product_variant_id ?? 0,
      fingerprint,
      String(orderDetailId)
    ].join("#");
  }
  if (bookingUid !== void 0 && bookingUid !== null && String(bookingUid) !== "") {
    return [
      item.product_id,
      item.product_variant_id ?? 0,
      fingerprint,
      String(bookingUid)
    ].join("#");
  }
  return [item.product_id, item.product_variant_id ?? 0, fingerprint].join("#");
}
function consolidateMainProductsForPromotion(entries) {
  if (!Array.isArray(entries) || entries.length <= 1) {
    return [...entries || []];
  }
  const groups = /* @__PURE__ */ new Map();
  for (const { item, originalListIndex } of entries) {
    const key = buildConsolidateKeyForPromotion(item);
    const existing = groups.get(key);
    if (!existing) {
      const mergedItem = (0, import_lodash_es.cloneDeep)(item);
      const metadata = mergedItem.metadata || {};
      delete metadata._promotion;
      mergedItem.metadata = metadata;
      if (Array.isArray(mergedItem.product_bundle)) {
        for (const bundle of mergedItem.product_bundle) {
          if (bundle && typeof bundle === "object")
            delete bundle._promotion;
        }
      }
      groups.set(key, { item: mergedItem, originalListIndex });
      continue;
    }
    existing.item.num = (0, import_utils2.getSafeProductNum)(
      Number(existing.item.num || 0) + Number(item.num || 0)
    );
  }
  return Array.from(groups.values());
}
function splitXItemsPromoLinesByGroup(lines, strategyActionMap) {
  var _a;
  const result = [];
  for (const line of lines) {
    const promo = (_a = line == null ? void 0 : line.metadata) == null ? void 0 : _a._promotion;
    if (!(promo == null ? void 0 : promo.inPromotion) || !promo.strategyId) {
      result.push(line);
      continue;
    }
    if (getPromotionActionType(promo) !== X_ITEMS_FOR_Y_PRICE) {
      result.push(line);
      continue;
    }
    const actionMeta = strategyActionMap.get(promo.strategyId);
    const detail = (actionMeta == null ? void 0 : actionMeta.actionDetail) ?? promo.actionDetail;
    const x = Number(detail == null ? void 0 : detail.x);
    const num = Number(line.num || 0);
    const isCumulative = (detail == null ? void 0 : detail.cumulative) !== false;
    if (!isCumulative || !Number.isFinite(x) || x <= 0 || num <= x || num % x !== 0) {
      result.push(line);
      continue;
    }
    const groupCount = num / x;
    for (let i = 0; i < groupCount; i++) {
      const splitLine = (0, import_lodash_es.cloneDeep)(line);
      splitLine.num = (0, import_utils2.getSafeProductNum)(x);
      splitLine.metadata = {
        ...splitLine.metadata || {},
        unique_identification_number: (0, import_utils.createUuidV4)()
      };
      result.push(splitLine);
    }
  }
  return result;
}
function generateNumericId() {
  return Math.floor(Math.random() * 1e9) + Date.now();
}
function convertToPromotionProduct(product, index) {
  const metadata = (product == null ? void 0 : product.metadata) || {};
  const numericId = generateNumericId();
  const originalItem = product;
  return {
    id: numericId,
    product_id: Number((product == null ? void 0 : product.product_id) ?? 0) || 0,
    product_variant_id: Number((product == null ? void 0 : product.product_variant_id) ?? 0) || 0,
    name: metadata.product_name || (product == null ? void 0 : product.title) || "",
    price: getOrderProductOriginalPriceForPromotion(product),
    quantity: Number((product == null ? void 0 : product.num) ?? 1) || 1,
    bundle: getProductBundleForEvaluator(product),
    _originalItem: originalItem,
    _originalId: getOrderProductUid(product),
    _originalIndex: index
  };
}
function calculateGiftActions(gifts, existingGifts) {
  var _a, _b, _c;
  const toAdd = [];
  const toReduce = [];
  const toRemove = [];
  const existingByStrategy = /* @__PURE__ */ new Map();
  for (const gift of existingGifts) {
    const strategyId = (_a = getGiftInfo(gift)) == null ? void 0 : _a.strategyId;
    if (!strategyId)
      continue;
    const existing = existingByStrategy.get(strategyId);
    const qty = Number((gift == null ? void 0 : gift.num) ?? 1) || 1;
    if (existing) {
      existing.items.push(gift);
      existing.totalQuantity += qty;
    } else {
      existingByStrategy.set(strategyId, { items: [gift], totalQuantity: qty });
    }
  }
  const processedStrategyIds = /* @__PURE__ */ new Set();
  for (const giftInfo of gifts) {
    const { strategyId, strategyName, giftCount, giftOptions, sourceProductIds } = giftInfo;
    processedStrategyIds.add(strategyId);
    const existing = existingByStrategy.get(strategyId);
    const existingCount = (existing == null ? void 0 : existing.totalQuantity) ?? 0;
    const diff = giftCount - existingCount;
    if (diff > 0) {
      const lastEvaluated = (_c = getGiftInfo((_b = existing == null ? void 0 : existing.items) == null ? void 0 : _b[0])) == null ? void 0 : _c.lastEvaluatedGiftCount;
      const strategyChanged = lastEvaluated === void 0 || giftCount !== lastEvaluated;
      if (strategyChanged) {
        toAdd.push({
          strategyId,
          strategyName,
          giftCount: diff,
          giftOptions,
          sourceProductIds
        });
      }
    } else if (diff < 0) {
      let remaining = Math.abs(diff);
      const reduceItems = [];
      const removeIds = [];
      const reversed = [...(existing == null ? void 0 : existing.items) || []].reverse();
      for (const giftItem of reversed) {
        if (remaining <= 0)
          break;
        const uid = getOrderProductUid(giftItem);
        const qty = Number((giftItem == null ? void 0 : giftItem.num) ?? 1) || 1;
        if (qty <= remaining) {
          removeIds.push(uid);
          remaining -= qty;
        } else {
          reduceItems.push({ uid, currentQuantity: qty, newQuantity: qty - remaining });
          remaining = 0;
        }
      }
      if (reduceItems.length > 0)
        toReduce.push({ strategyId, items: reduceItems });
      toRemove.push(...removeIds);
    }
  }
  for (const [strategyId, existing] of existingByStrategy) {
    if (processedStrategyIds.has(strategyId))
      continue;
    for (const giftItem of existing.items) {
      toRemove.push(getOrderProductUid(giftItem));
    }
  }
  return { toAdd, toReduce, toRemove };
}
function extractProductMatchIdentifiersFromStrategyConfig(strategyConfig) {
  var _a;
  const rules = (_a = strategyConfig == null ? void 0 : strategyConfig.conditions) == null ? void 0 : _a.rules;
  if (!Array.isArray(rules))
    return [];
  for (const rule of rules) {
    if ((rule == null ? void 0 : rule.field) === "productIdAndVariantId" && ((rule == null ? void 0 : rule.operator) === "product_match" || (rule == null ? void 0 : rule.operator) === "object_in") && Array.isArray(rule == null ? void 0 : rule.value)) {
      return rule.value.map((item) => ({
        product_id: Number(item == null ? void 0 : item.product_id) || 0,
        product_variant_id: Number(item == null ? void 0 : item.product_variant_id) || 0
      })).filter((item) => item.product_id > 0);
    }
  }
  return [];
}
function resolveStrategyRequiredQuantity(matched) {
  const preset = Number(matched.requiredQuantity);
  if (Number.isFinite(preset) && preset > 0)
    return preset;
  const detail = matched.actionDetail || {};
  if (matched.actionType === "X_ITEMS_FOR_Y_PRICE") {
    return Number(detail.x) || 2;
  }
  if (matched.actionType === "BUY_X_GET_Y_FREE") {
    return Number(detail.buyQuantity) || 1;
  }
  return 1;
}
function resolveStrategyEligibleProducts(matched) {
  if (Array.isArray(matched.eligibleProducts) && matched.eligibleProducts.length > 0) {
    return matched.eligibleProducts;
  }
  return extractProductMatchIdentifiersFromStrategyConfig(matched.strategyConfig);
}
function calculateUnfulfilledPromotions(processedProducts, applicableStrategiesResult, mainProducts) {
  var _a, _b;
  const unfulfilledMap = /* @__PURE__ */ new Map();
  for (let i = 0; i < processedProducts.length; i++) {
    const product = processedProducts[i];
    const promotion = (_a = product == null ? void 0 : product.metadata) == null ? void 0 : _a._promotion;
    if (!promotion || promotion.inPromotion !== false)
      continue;
    const strategyId = promotion.strategyId;
    if (!strategyId)
      continue;
    const lookupUid = String(
      (product == null ? void 0 : product._originalProductUid) ?? ((_b = product == null ? void 0 : product.metadata) == null ? void 0 : _b.unique_identification_number) ?? ""
    );
    const originalIndex = mainProducts.findIndex(
      (item) => getOrderProductUid(item) === lookupUid
    );
    if (originalIndex === -1)
      continue;
    const strategyData = applicableStrategiesResult[originalIndex];
    if (!strategyData || !strategyData.hasApplicableStrategy)
      continue;
    const matched = strategyData.applicableStrategies.find(
      (s) => s.strategyId === strategyId
    );
    if (!matched)
      continue;
    const quantity = Number((product == null ? void 0 : product.num) ?? 1) || 1;
    if (unfulfilledMap.has(strategyId)) {
      unfulfilledMap.get(strategyId).currentQuantity += quantity;
    } else {
      unfulfilledMap.set(strategyId, {
        strategyId,
        strategyName: matched.strategyName,
        actionType: matched.actionType,
        currentQuantity: quantity,
        requiredQuantity: resolveStrategyRequiredQuantity(matched),
        needQuantity: 0,
        eligibleProducts: resolveStrategyEligibleProducts(matched),
        strategyMetadata: matched.strategyMetadata,
        display: matched.display
      });
    }
  }
  const result = [];
  for (const data of unfulfilledMap.values()) {
    const needQuantity = data.requiredQuantity - data.currentQuantity;
    if (needQuantity > 0) {
      result.push({ ...data, needQuantity });
    }
  }
  return result;
}
function appendPromotionTags(products, evaluator) {
  if (!Array.isArray(products) || products.length === 0 || !evaluator) {
    return products || [];
  }
  const evaluatorInput = products.map((item, index) => ({
    id: index,
    product_id: Number((item == null ? void 0 : item.id) ?? (item == null ? void 0 : item.product_id) ?? 0) || 0,
    product_variant_id: Number((item == null ? void 0 : item.product_variant_id) ?? 0) || 0,
    name: (item == null ? void 0 : item.title) || (item == null ? void 0 : item.name) || "",
    price: Number((item == null ? void 0 : item.price) ?? 0) || 0,
    quantity: Number((item == null ? void 0 : item.quantity) ?? 1) || 1,
    bundle: []
  }));
  const strategies = evaluator.getProductsApplicableStrategies(
    { products: evaluatorInput },
    false
  );
  return products.map((item, index) => {
    const data = strategies[index];
    const tags = [];
    if (data == null ? void 0 : data.hasApplicableStrategy) {
      for (const strategy of data.applicableStrategies) {
        if (strategy.display) {
          tags.push({
            text: strategy.display.text,
            type: strategy.display.type,
            strategyId: strategy.strategyId
          });
        }
      }
    }
    if (tags.length === 0) {
      const { _promotionTags, ...rest } = item;
      return rest;
    }
    return { ...item, _promotionTags: tags };
  });
}
function processCartPromotion(list, evaluator, options) {
  var _a, _b, _c, _d, _e, _f, _g, _h;
  const empty = {
    products: Array.isArray(list) ? [...list] : [],
    totalDiscount: 0,
    gifts: [],
    giftActions: { toAdd: [], toReduce: [], toRemove: [] },
    unfulfilledPromotions: []
  };
  if (!list || list.length === 0 || !evaluator)
    return empty;
  const mainProductsWithIndex = [];
  const existingGiftsWithIndex = [];
  const editProductsWithIndex = [];
  for (let i = 0; i < list.length; i++) {
    const item = list[i];
    const giftInfo = getGiftInfo(item);
    if (isPersistedGeneratedVoucher(item)) {
      editProductsWithIndex.push({ item, originalListIndex: i });
    } else if (giftInfo) {
      existingGiftsWithIndex.push({ item, originalListIndex: i });
    } else if ((item == null ? void 0 : item.booking_id) !== void 0 && (item == null ? void 0 : item.booking_id) !== null && item.booking_id !== 0 && item.booking_id !== "0") {
      editProductsWithIndex.push({ item, originalListIndex: i });
    } else {
      mainProductsWithIndex.push({ item, originalListIndex: i });
    }
  }
  const consolidatedMainProductsWithIndex = consolidateMainProductsForPromotion(
    mainProductsWithIndex
  );
  const mainProducts = consolidatedMainProductsWithIndex.map((x) => x.item);
  const existingGifts = existingGiftsWithIndex.map((x) => x.item);
  if (mainProducts.length === 0) {
    const editProducts = editProductsWithIndex.map((x) => x.item);
    if (existingGifts.length > 0) {
      const toRemove = [];
      for (const gift of existingGifts) {
        const uid = getOrderProductUid(gift);
        if (uid)
          toRemove.push(uid);
      }
      return {
        products: editProducts,
        totalDiscount: 0,
        gifts: [],
        giftActions: { toAdd: [], toReduce: [], toRemove },
        unfulfilledPromotions: []
      };
    }
    return {
      ...empty,
      products: editProducts.length > 0 ? editProducts : [...list]
    };
  }
  const promotionProducts = [];
  const originalItemMap = /* @__PURE__ */ new Map();
  const productsForStrategies = consolidatedMainProductsWithIndex.map(({ item }, i) => {
    var _a2;
    return {
      id: i,
      product_id: Number((item == null ? void 0 : item.product_id) ?? 0) || 0,
      product_variant_id: Number((item == null ? void 0 : item.product_variant_id) ?? 0) || 0,
      name: ((_a2 = item == null ? void 0 : item.metadata) == null ? void 0 : _a2.product_name) || (item == null ? void 0 : item.title) || "",
      price: getOrderProductBasePrice(item),
      quantity: Number((item == null ? void 0 : item.num) ?? 1) || 1,
      bundle: getProductBundleForEvaluator(item)
    };
  });
  for (let i = 0; i < consolidatedMainProductsWithIndex.length; i++) {
    const { item, originalListIndex } = consolidatedMainProductsWithIndex[i];
    const promotionProduct = convertToPromotionProduct(item, originalListIndex);
    promotionProducts.push(promotionProduct);
    originalItemMap.set(promotionProduct.id, { item, index: originalListIndex });
  }
  const applicableStrategiesResult = evaluator.getProductsApplicableStrategies({
    products: productsForStrategies,
    channel: options == null ? void 0 : options.channel
  });
  const strategyActionMap = /* @__PURE__ */ new Map();
  for (const row of applicableStrategiesResult || []) {
    for (const strategy of (row == null ? void 0 : row.applicableStrategies) || []) {
      if (!(strategy == null ? void 0 : strategy.strategyId))
        continue;
      strategyActionMap.set(strategy.strategyId, {
        actionType: strategy.actionType,
        actionDetail: strategy.actionDetail
      });
    }
  }
  const result = evaluator.evaluateCartWithPricing({
    products: promotionProducts,
    channel: options == null ? void 0 : options.channel
  });
  const strategySourceProductIds = /* @__PURE__ */ new Map();
  for (const priced of result.products) {
    if (priced.inPromotion && priced.strategyId) {
      const sourceId = priced.originalId ?? priced.id;
      const originalData = originalItemMap.get(sourceId);
      if (originalData) {
        const uid = getOrderProductUid(originalData.item);
        if (!uid)
          continue;
        const arr = strategySourceProductIds.get(priced.strategyId) || [];
        if (!arr.includes(uid))
          arr.push(uid);
        strategySourceProductIds.set(priced.strategyId, arr);
      }
    }
  }
  const extendedGifts = result.gifts.map((gift) => ({
    ...gift,
    sourceProductIds: strategySourceProductIds.get(gift.strategyId) || []
  }));
  const strategyGiftInfoMap = /* @__PURE__ */ new Map();
  for (const gift of extendedGifts)
    strategyGiftInfoMap.set(gift.strategyId, gift);
  const strategyGiftInfoOwner = /* @__PURE__ */ new Map();
  const processedProducts = [];
  for (const priced of result.products) {
    const rawSourceId = priced.originalId ?? priced.id;
    const sourceId = Number(rawSourceId);
    const originalData = Number.isFinite(sourceId) ? originalItemMap.get(sourceId) : void 0;
    if (!originalData)
      continue;
    const { item: originalItem, index: originalIndex } = originalData;
    const pricedProductId = Number(priced.product_id ?? 0);
    const originalProductId = Number(originalItem.product_id ?? 0);
    if (pricedProductId > 0 && originalProductId > 0 && pricedProductId !== originalProductId) {
      continue;
    }
    const newItem = (0, import_lodash_es.cloneDeep)(originalItem);
    const metadata = newItem.metadata || {};
    const sourceCartNum = (0, import_utils2.getSafeProductNum)(originalItem.num);
    newItem._sourceCartNum = sourceCartNum;
    const wasInPromotion = ((_a = metadata._promotion) == null ? void 0 : _a.inPromotion) === true;
    delete metadata._promotion;
    if (Array.isArray(newItem.product_bundle)) {
      for (const b of newItem.product_bundle) {
        if (b && typeof b === "object")
          delete b._promotion;
      }
    }
    const originalBasePrice = getOrderProductOriginalPriceForPromotion(originalItem);
    const shouldModifyPrice = priced.inPromotion || wasInPromotion;
    const priceUnchangedPromo = priced.inPromotion && Number(priced.finalPrice) === Number(originalBasePrice);
    if (shouldModifyPrice) {
      if (priced.inPromotion && !priceUnchangedPromo) {
        const promoUnitPrice = new import_decimal.default(priced.finalPrice).toDecimalPlaces(2).toFixed(2);
        applyPromoUnitPriceToLine(newItem, {
          promoUnitPrice,
          originalBasePrice,
          strategyTitle: (_b = priced.strategyMetadata) == null ? void 0 : _b.name
        });
      } else {
        const optionSum = (0, import_utils.sumOptionUnitPrice)((0, import_utils.getProductSkuOptions)(newItem)).toNumber();
        const sourceRestore = new import_decimal.default(originalBasePrice).toDecimalPlaces(2).toFixed(2);
        const mainRestore = new import_decimal.default(Number(sourceRestore) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
        metadata.source_product_price = sourceRestore;
        metadata.main_product_selling_price = mainRestore;
        metadata.price_schema_version = 2;
        newItem.selling_price = (0, import_utils.composeLinePrice)({
          mainPrice: mainRestore,
          bundle: newItem.product_bundle
        });
        updatePromotionDiscountList(newItem, null);
      }
    }
    newItem.num = priced.isSplit ? (0, import_utils2.getSafeProductNum)(priced.quantity) : sourceCartNum;
    if (priced.isSplit) {
      metadata.unique_identification_number = (0, import_utils.createUuidV4)();
    }
    if (priced.strategyId) {
      const actionMeta = strategyActionMap.get(priced.strategyId);
      const promotionInfo = {
        strategyId: priced.strategyId,
        strategyMetadata: priced.strategyMetadata,
        inPromotion: priced.inPromotion,
        originalPrice: originalBasePrice,
        finalPrice: priced.finalPrice,
        actionType: actionMeta == null ? void 0 : actionMeta.actionType,
        actionDetail: actionMeta == null ? void 0 : actionMeta.actionDetail
      };
      const bundleIdx = priced.matchedBundleIndex;
      if (bundleIdx !== void 0 && Array.isArray(newItem.product_bundle) && newItem.product_bundle[bundleIdx]) {
        newItem.product_bundle[bundleIdx]._promotion = promotionInfo;
      } else {
        metadata._promotion = promotionInfo;
      }
    }
    newItem.metadata = metadata;
    if (priced.inPromotion && priced.strategyId) {
      const gift = strategyGiftInfoMap.get(priced.strategyId);
      if (gift && Array.isArray(gift.giftOptions) && gift.giftOptions.length > 0 && !strategyGiftInfoOwner.has(priced.strategyId)) {
        const giftInfoData = {
          strategyName: gift.strategyName,
          giftCount: gift.giftCount,
          giftOptions: gift.giftOptions
        };
        const bundleIdx = priced.matchedBundleIndex;
        if (bundleIdx !== void 0 && Array.isArray(newItem.product_bundle) && ((_c = newItem.product_bundle[bundleIdx]) == null ? void 0 : _c._promotion)) {
          newItem.product_bundle[bundleIdx]._promotion.giftInfo = giftInfoData;
        } else if (metadata._promotion) {
          metadata._promotion.giftInfo = giftInfoData;
        }
        strategyGiftInfoOwner.set(
          priced.strategyId,
          getOrderProductUid(newItem)
        );
      }
    }
    newItem._sortIndex = originalIndex;
    newItem._sortSubIndex = priced.inPromotion ? 0 : 1;
    newItem._originalProductUid = getOrderProductUid(originalItem);
    processedProducts.push(newItem);
  }
  for (const { item, originalListIndex } of existingGiftsWithIndex) {
    const next = item;
    next._sortIndex = originalListIndex;
    next._sortSubIndex = 0;
    const strategyId = (_d = getGiftInfo(next)) == null ? void 0 : _d.strategyId;
    if (strategyId) {
      const latest = strategyGiftInfoMap.get(strategyId);
      const owner = strategyGiftInfoOwner.get(strategyId);
      if (latest && ((_e = next.metadata) == null ? void 0 : _e._giftInfo)) {
        next.metadata._giftInfo.sourceProductIds = latest.sourceProductIds;
      }
      if (owner && ((_f = next.metadata) == null ? void 0 : _f._giftInfo)) {
        next.metadata._giftInfo.sourceProductId = owner;
      }
    }
    processedProducts.push(next);
  }
  for (const { item, originalListIndex } of editProductsWithIndex) {
    const next = item;
    next._sortIndex = originalListIndex;
    next._sortSubIndex = 0;
    processedProducts.push(next);
  }
  processedProducts.sort((a, b) => {
    if (a._sortIndex !== b._sortIndex)
      return a._sortIndex - b._sortIndex;
    return a._sortSubIndex - b._sortSubIndex;
  });
  const dedupedPromotionLines = dedupePromotionProcessedLines(processedProducts);
  processedProducts.splice(0, processedProducts.length, ...dedupedPromotionLines);
  const mergedPromotionLines = mergeIdenticalPromotionCartLines(processedProducts);
  processedProducts.splice(0, processedProducts.length, ...mergedPromotionLines);
  const splitPromoLines = splitXItemsPromoLinesByGroup(processedProducts, strategyActionMap);
  processedProducts.splice(0, processedProducts.length, ...splitPromoLines);
  for (const product of processedProducts) {
    const metadata = product.metadata || {};
    if (metadata._promotion)
      continue;
    if (getGiftInfo(product))
      continue;
    const lookupUid = String(product._originalProductUid ?? getOrderProductUid(product));
    const originalIndex = mainProducts.findIndex(
      (item) => getOrderProductUid(item) === lookupUid
    );
    if (originalIndex === -1)
      continue;
    const strategyData = applicableStrategiesResult[originalIndex];
    if (!strategyData || !strategyData.hasApplicableStrategy)
      continue;
    const first = strategyData.applicableStrategies[0];
    if (!first)
      continue;
    const originalPrice = getOrderProductOriginalPriceForPromotion(mainProducts[originalIndex]);
    metadata._promotion = {
      strategyId: first.strategyId,
      strategyMetadata: first.strategyMetadata,
      inPromotion: false,
      originalPrice,
      finalPrice: originalPrice
    };
    product.metadata = metadata;
  }
  const giftActions = calculateGiftActions(extendedGifts, existingGifts);
  for (const gift of existingGifts) {
    const strategyId = (_g = getGiftInfo(gift)) == null ? void 0 : _g.strategyId;
    if (!strategyId)
      continue;
    const latest = strategyGiftInfoMap.get(strategyId);
    if (latest && ((_h = gift.metadata) == null ? void 0 : _h._giftInfo)) {
      gift.metadata._giftInfo.lastEvaluatedGiftCount = latest.giftCount;
    }
  }
  const unfulfilledPromotions = calculateUnfulfilledPromotions(
    processedProducts,
    applicableStrategiesResult,
    mainProducts
  );
  for (const product of processedProducts) {
    delete product._sortIndex;
    delete product._sortSubIndex;
    delete product._originalProductUid;
    delete product._sourceCartNum;
  }
  return {
    products: processedProducts,
    totalDiscount: result.totalDiscount,
    gifts: extendedGifts,
    giftActions,
    unfulfilledPromotions
  };
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  appendPromotionTags,
  buildConsolidateKeyForPromotion,
  calculateUnfulfilledPromotions,
  extractProductMatchIdentifiersFromStrategyConfig,
  getOrderProductBasePrice,
  getOrderProductOriginalPriceForPromotion,
  getOrderProductUid,
  mergeIdenticalPromotionCartLines,
  processCartPromotion,
  resolveStrategyEligibleProducts,
  resolveStrategyRequiredQuantity
});
