import { Product } from "@/models";
import { revalidateProductContent } from "@/lib/cache-invalidation";
import {
  productAllowsOversell,
  productTracksStock,
} from "@/lib/products/stock-policy";

export type InventoryAdjustmentLine = {
  productId: string;
  quantity: number;
  variantId?: string;
};

export type InventoryAdjustmentOptions = {
  channel?: "online" | "pos";
  locationId?: string;
};

export class InsufficientStockError extends Error {
  public readonly line: InventoryAdjustmentLine;
  public readonly channel: InventoryAdjustmentOptions["channel"];
  public readonly locationId?: string;

  constructor(params: {
    line: InventoryAdjustmentLine;
    channel?: InventoryAdjustmentOptions["channel"];
    locationId?: string;
  }) {
    super("Insufficient stock");
    this.name = "InsufficientStockError";
    this.line = params.line;
    this.channel = params.channel;
    this.locationId = params.locationId;
  }
}

type ProductModelLike = {
  updateOne?: (
    filter: unknown,
    update: unknown,
    options?: unknown,
  ) => Promise<unknown>;
  findByIdAndUpdate?: (id: unknown, update: unknown) => Promise<unknown>;
};

function isPlainObject(value: unknown): value is Record<string, unknown> {
  return (
    typeof value === "object" &&
    value !== null &&
    !Array.isArray(value) &&
    Object.prototype.toString.call(value) === "[object Object]"
  );
}

function readMatchedCount(result: unknown): number | null {
  if (!isPlainObject(result)) return null;
  const matchedCount = result.matchedCount;
  return typeof matchedCount === "number" ? matchedCount : null;
}

type LocationInventoryEntry = { locationId?: unknown; quantity?: number };

/** Map key for a line's per-location inventory: `productId` or `productId:variantId`. */
function lineLocationKey(line: InventoryAdjustmentLine): string {
  return line.variantId
    ? `${String(line.productId)}:${String(line.variantId)}`
    : String(line.productId);
}

/**
 * Batch-read the per-location inventory for every line in a single query and
 * return a map keyed by {@link lineLocationKey}.
 *
 * This replaces a per-line `findOne` (an N+1: a 3-item order previously issued 3
 * extra point reads before any write, on the hottest checkout path). One
 * `$in` load covers all lines; products/variants that don't track per-location
 * stock are simply absent from the map, and callers fall back to the plain
 * stock-only decrement — identical behavior to the old empty-array result.
 *
 * Guarded so the unit-test mocks (which may expose only `updateOne`) degrade to
 * an empty map rather than throwing.
 */
type LineLocationData = {
  /** Per-line location inventory, keyed by {@link lineLocationKey}. */
  locations: Map<string, LocationInventoryEntry[]>;
  /** Slugs of the affected products, for cache revalidation. */
  slugs: string[];
  /**
   * Products whose `stock` carries no meaning (digital, or "Track quantity"
   * off). Their lines are skipped entirely — there is nothing to draw down.
   */
  untracked: Set<string>;
  /**
   * Products that must never have a sale blocked by the stock on hand
   * (untracked, or "Continue selling when out of stock"). Their decrement runs
   * without the `stock >= quantity` guard and may go negative; that is the
   * documented meaning of the switch, and an inventory reconcile corrects it.
   */
  oversell: Set<string>;
};

async function readAllLineLocations(
  lines: InventoryAdjustmentLine[],
): Promise<LineLocationData> {
  const locations = new Map<string, LocationInventoryEntry[]>();
  const slugs: string[] = [];
  const untracked = new Set<string>();
  const oversell = new Set<string>();
  const result = { locations, slugs, untracked, oversell };
  const productModel = Product as unknown as {
    find?: (q: unknown, p?: unknown) => { lean: () => Promise<unknown> };
  };
  if (typeof productModel.find !== "function") return result;

  const productIds = Array.from(
    new Set(
      lines
        .map((line) => (line.productId ? String(line.productId) : ""))
        .filter(Boolean),
    ),
  );
  if (productIds.length === 0) return result;

  const docs = (await productModel.find(
    { _id: { $in: productIds } },
    {
      slug: 1,
      locationInventory: 1,
      "shipping.isPhysicalProduct": 1,
      inventory: 1,
      "variants._id": 1,
      "variants.locationInventory": 1,
    },
  ).lean()) as Array<{
    _id: unknown;
    slug?: string;
    locationInventory?: LocationInventoryEntry[];
    shipping?: { isPhysicalProduct?: boolean };
    inventory?: { tracked?: boolean; continueSellingWhenOutOfStock?: boolean };
    variants?: Array<{
      _id?: unknown;
      locationInventory?: LocationInventoryEntry[];
    }>;
  }> | null;
  if (!Array.isArray(docs)) return result;

  for (const doc of docs) {
    const productId = String(doc._id);
    if (typeof doc.slug === "string" && doc.slug.length > 0) {
      slugs.push(doc.slug);
    }
    if (!productTracksStock(doc)) untracked.add(productId);
    if (productAllowsOversell(doc)) oversell.add(productId);
    if (Array.isArray(doc.locationInventory)) {
      locations.set(productId, doc.locationInventory);
    }
    if (Array.isArray(doc.variants)) {
      for (const variant of doc.variants) {
        if (variant?._id && Array.isArray(variant.locationInventory)) {
          locations.set(
            `${productId}:${String(variant._id)}`,
            variant.locationInventory,
          );
        }
      }
    }
  }
  return result;
}

/**
 * Pick which location an ONLINE sale should draw down. Online orders aren't
 * tied to a physical location, but per-location stock is the source of truth
 * for `stock` (the product pre-validate hook recomputes stock = Σ location
 * quantities). If we lowered only `stock`, the next product.save() would
 * resurrect the sold units — so online sales must also decrement a location.
 * Prefer a single location that can cover the line; otherwise draw from the
 * fullest (it may go slightly negative, which a later inventory reconcile
 * corrects — far better than overstating sellable stock).
 */
function pickDecrementLocationId(
  locations: LocationInventoryEntry[],
  quantity: number,
): string | undefined {
  if (locations.length === 0) return undefined;
  const covering = locations.find(
    (loc) => Number(loc.quantity || 0) >= quantity,
  );
  const target =
    covering ||
    locations
      .slice()
      .sort((a, b) => Number(b.quantity || 0) - Number(a.quantity || 0))[0];
  return target?.locationId ? String(target.locationId) : undefined;
}

function pickRestoreLocationId(
  locations: LocationInventoryEntry[],
): string | undefined {
  const first = locations[0];
  return first?.locationId ? String(first.locationId) : undefined;
}

async function decrementSingleLine(
  productModel: ProductModelLike,
  line: InventoryAdjustmentLine,
  opts: InventoryAdjustmentOptions,
  lineLocations: LocationInventoryEntry[],
  allowOversell = false,
) {
  if (line.variantId) {
    if (typeof productModel.updateOne !== "function") {
      if (typeof productModel.findByIdAndUpdate !== "function") {
        throw new Error("Product model does not support inventory updates");
      }
      await productModel.findByIdAndUpdate(line.productId, {
        $inc: { stock: -line.quantity },
      });
      return;
    }

    const baseUpdate: Record<string, unknown> = {
      $inc: {
        stock: -line.quantity,
        "variants.$.stock": -line.quantity,
      },
    };

    const isPos =
      opts.channel === "pos" &&
      typeof opts.locationId === "string" &&
      opts.locationId.length > 0;

    // Oversell drops the "this location holds enough" guard the same way it
    // drops the stock guards below: the sale must go through, and the location
    // is allowed to go negative until an inventory reconcile corrects it.
    const locationHasStock = allowOversell
      ? {}
      : { quantity: { $gte: line.quantity } };
    const locationFilterHasStock = allowOversell
      ? {}
      : { "li.quantity": { $gte: line.quantity } };

    // For POS + location inventory, validate against the location quantity.
    // Parent/variant aggregate stock can be stale and should not block sale.
    const filter: Record<string, unknown> = isPos
      ? {
          _id: line.productId,
          variants: {
            $elemMatch: {
              _id: line.variantId,
              locationInventory: {
                $elemMatch: {
                  locationId: opts.locationId,
                  ...locationHasStock,
                },
              },
            },
          },
        }
      : allowOversell
        ? // "Continue selling when out of stock": the variant must exist, but
          // its stock is not allowed to block the sale.
          {
            _id: line.productId,
            variants: { $elemMatch: { _id: line.variantId } },
          }
        : {
            _id: line.productId,
            stock: { $gte: line.quantity },
            variants: {
              $elemMatch: {
                _id: line.variantId,
                stock: { $gte: line.quantity },
              },
            },
          };

    let options: Record<string, unknown> | undefined;
    if (isPos) {
      (baseUpdate.$inc as Record<string, unknown>)[
        "variants.$.locationInventory.$[li].quantity"
      ] = -line.quantity;
      options = {
        arrayFilters: [
          {
            "li.locationId": opts.locationId,
            ...locationFilterHasStock,
          },
        ],
      };
    } else {
      // Online sale: if the variant tracks per-location stock, also draw down a
      // location so Σ(locationInventory) stays equal to variant.stock and the
      // pre-validate hook can't resurrect the sold units.
      const locationId = pickDecrementLocationId(lineLocations, line.quantity);
      if (locationId) {
        (baseUpdate.$inc as Record<string, unknown>)[
          "variants.$.locationInventory.$[li].quantity"
        ] = -line.quantity;
        options = { arrayFilters: [{ "li.locationId": locationId }] };
      }
    }

    const result = await productModel.updateOne(filter, baseUpdate, options);
    if (readMatchedCount(result) === 0) {
      throw new InsufficientStockError({
        line,
        channel: opts.channel,
        locationId: opts.locationId,
      });
    }
    return;
  }

  if (typeof productModel.updateOne === "function") {
    const isPos =
      opts.channel === "pos" &&
      typeof opts.locationId === "string" &&
      opts.locationId.length > 0;

    const baseUpdate: Record<string, unknown> = {
      $inc: { stock: -line.quantity },
    };
    let filter: Record<string, unknown> = allowOversell
      ? { _id: line.productId }
      : { _id: line.productId, stock: { $gte: line.quantity } };
    let options: Record<string, unknown> | undefined;

    // `lineLocations` (passed in) is whether this product tracks per-location
    // stock at all. Stores that don't use multi-location inventory have no entry
    // — for them the plain stock-guarded decrement below remains the correct
    // (and only possible) behavior, including for POS sales.
    if (isPos && lineLocations.length > 0) {
      // POS simple product with location tracking: validate and draw down the
      // selling location so the sale is location-accurate (previously it
      // ignored the location entirely).
      filter = {
        _id: line.productId,
        locationInventory: {
          $elemMatch: {
            locationId: opts.locationId,
            ...(allowOversell ? {} : { quantity: { $gte: line.quantity } }),
          },
        },
      };
      (baseUpdate.$inc as Record<string, unknown>)[
        "locationInventory.$[li].quantity"
      ] = -line.quantity;
      options = {
        arrayFilters: [
          {
            "li.locationId": opts.locationId,
            ...(allowOversell ? {} : { "li.quantity": { $gte: line.quantity } }),
          },
        ],
      };
    } else {
      const locationId = pickDecrementLocationId(lineLocations, line.quantity);
      if (locationId) {
        (baseUpdate.$inc as Record<string, unknown>)[
          "locationInventory.$[li].quantity"
        ] = -line.quantity;
        options = { arrayFilters: [{ "li.locationId": locationId }] };
      }
    }

    const result = await productModel.updateOne(filter, baseUpdate, options);
    if (readMatchedCount(result) === 0) {
      throw new InsufficientStockError({
        line,
        channel: opts.channel,
        locationId: opts.locationId,
      });
    }
    return;
  }

  if (typeof productModel.findByIdAndUpdate === "function") {
    await productModel.findByIdAndUpdate(line.productId, {
      $inc: { stock: -line.quantity },
    });
    return;
  }

  throw new Error("Product model does not support inventory updates");
}

/**
 * Decrement inventory for a list of lines. Atomic from the caller's
 * perspective: if any line fails (insufficient stock or otherwise), all
 * previously-decremented lines in the same call are rolled back before the
 * error is re-thrown. Callers can therefore treat the operation as
 * all-or-nothing.
 */
export async function decrementInventory(
  lines: InventoryAdjustmentLine[],
  opts: InventoryAdjustmentOptions = {},
) {
  const productModel = Product as unknown as ProductModelLike;
  const applied: InventoryAdjustmentLine[] = [];
  // Single batched read of every line's per-location inventory (+ slugs for cache
  // invalidation), replacing the former per-line findOne inside
  // decrementSingleLine (an N+1 on the checkout path) and the separate slug
  // lookup in invalidateProductCache.
  const lineData = await readAllLineLocations(lines);

  try {
    for (const line of lines) {
      if (
        !line.productId ||
        !Number.isFinite(line.quantity) ||
        line.quantity <= 0
      ) {
        continue;
      }
      // Digital / untracked products have no counter to move. Skipping them
      // also keeps them out of `applied`, so a later failure doesn't try to
      // "restore" stock that was never taken.
      if (lineData.untracked.has(String(line.productId))) continue;
      await decrementSingleLine(
        productModel,
        line,
        opts,
        lineData.locations.get(lineLocationKey(line)) || [],
        lineData.oversell.has(String(line.productId)),
      );
      applied.push(line);
    }
  } catch (err) {
    if (applied.length > 0) {
      await restoreInventory(applied, opts).catch((rollbackErr) => {
        // If rollback itself fails, we surface the original error to the
        // caller (so they don't double-handle InsufficientStockError) but
        // log the rollback failure for operator follow-up.
        console.error(
          "Failed to roll back partial inventory decrement:",
          rollbackErr,
        );
      });
    }
    throw err;
  }

  // Refresh cached storefront product pages so the new stock count is
  // reflected immediately rather than waiting for the 60s revalidate window.
  await invalidateProductCache(applied, lineData.slugs);

  // Low-stock alerts. Fired only when this decrement CROSSED the threshold
  // (previous stock above, new stock at/below) so a product sitting at low
  // stock doesn't re-notify on every subsequent sale.
  await maybeNotifyLowStock(applied);
}

// Matches the admin inventory screen's low-stock boundary.
const LOW_STOCK_THRESHOLD = 10;

async function maybeNotifyLowStock(
  lines: InventoryAdjustmentLine[],
): Promise<void> {
  try {
    const decrementedByProduct = new Map<string, number>();
    for (const line of lines) {
      const key = String(line.productId);
      decrementedByProduct.set(
        key,
        (decrementedByProduct.get(key) || 0) + line.quantity,
      );
    }
    const productIds = Array.from(decrementedByProduct.keys());
    if (productIds.length === 0) return;

    const products = (await Product.find({ _id: { $in: productIds } })
      .select("name stock vendorId")
      .lean()) as Array<{
      _id: unknown;
      name?: string;
      stock?: number;
      vendorId?: unknown;
    }>;

    for (const product of products) {
      const decremented = decrementedByProduct.get(String(product._id)) || 0;
      const stock = Number(product.stock || 0);
      const previousStock = stock + decremented;
      if (stock > LOW_STOCK_THRESHOLD || previousStock <= LOW_STOCK_THRESHOLD) {
        continue;
      }

      const name = product.name || "Product";
      const { notifyStaffLowStock, notifyLowStock } = await import(
        "@/lib/notifications"
      );
      await notifyStaffLowStock(name, stock, {
        productId: String(product._id),
      }).catch((err) => console.error("Failed to notify staff low stock:", err));

      if (product.vendorId) {
        const { Vendor } = await import("@/models");
        const vendor = (await Vendor.findById(product.vendorId)
          .select("userId")
          .lean()) as { userId?: unknown } | null;
        if (vendor?.userId) {
          await notifyLowStock(String(vendor.userId), name, stock).catch(
            (err) => console.error("Failed to notify vendor low stock:", err),
          );
        }
      }
    }
  } catch (err) {
    // Alerts must never break order placement.
    console.error("Low-stock notification check failed:", err);
  }
}

/**
 * Restore inventory for cancelled/refunded orders.
 * Increments stock back for each line item.
 */
export async function restoreInventory(
  lines: InventoryAdjustmentLine[],
  opts: InventoryAdjustmentOptions = {},
) {
  const productModel = Product as unknown as ProductModelLike;
  // One batched read of all lines' locations (+ slugs), replacing the former
  // per-line findOne in each branch below and the slug lookup in
  // invalidateProductCache.
  const lineData = await readAllLineLocations(lines);

  for (const line of lines) {
    if (!line.productId || !Number.isFinite(line.quantity) || line.quantity <= 0) {
      continue;
    }
    // Mirrors decrementInventory: nothing was taken from an untracked product,
    // so crediting it back would invent stock.
    if (lineData.untracked.has(String(line.productId))) continue;
    const lineLocations = lineData.locations.get(lineLocationKey(line)) || [];

    if (line.variantId) {
      if (typeof productModel.updateOne !== "function") {
        if (typeof productModel.findByIdAndUpdate !== "function") {
          throw new Error("Product model does not support inventory updates");
        }
        await productModel.findByIdAndUpdate(line.productId, {
          $inc: { stock: line.quantity },
        });
        continue;
      }

      const baseUpdate: Record<string, unknown> = {
        $inc: {
          stock: line.quantity,
          "variants.$.stock": line.quantity,
        },
      };

      const isPos = opts.channel === "pos" && typeof opts.locationId === "string" && opts.locationId.length > 0;

      const filter: Record<string, unknown> = {
        _id: line.productId,
        "variants._id": line.variantId,
      };

      let options: Record<string, unknown> | undefined;
      if (isPos) {
        (baseUpdate.$inc as Record<string, unknown>)[
          "variants.$.locationInventory.$[li].quantity"
        ] = line.quantity;
        options = {
          arrayFilters: [{ "li.locationId": opts.locationId }],
        };
      } else {
        // Online restore: credit a location so Σ(locationInventory) stays equal
        // to variant.stock (mirrors the online decrement).
        const locationId = pickRestoreLocationId(lineLocations);
        if (locationId) {
          (baseUpdate.$inc as Record<string, unknown>)[
            "variants.$.locationInventory.$[li].quantity"
          ] = line.quantity;
          options = { arrayFilters: [{ "li.locationId": locationId }] };
        }
      }

      await productModel.updateOne(filter, baseUpdate, options);
      continue;
    }

    if (typeof productModel.updateOne === "function") {
      const isPos =
        opts.channel === "pos" &&
        typeof opts.locationId === "string" &&
        opts.locationId.length > 0;
      const baseUpdate: Record<string, unknown> = {
        $inc: { stock: line.quantity },
      };
      let options: Record<string, unknown> | undefined;
      // Only credit a location the product actually tracks — `$[li]` on a
      // missing locationInventory array would make the whole update fail, and
      // a restore must never fail because a product doesn't use locations.
      // `lineLocations` was batch-loaded above.
      const trackedPosLocation =
        isPos &&
        lineLocations.some((loc) => String(loc.locationId) === opts.locationId)
          ? opts.locationId
          : undefined;
      const locationId = isPos
        ? trackedPosLocation
        : pickRestoreLocationId(lineLocations);
      if (locationId) {
        (baseUpdate.$inc as Record<string, unknown>)[
          "locationInventory.$[li].quantity"
        ] = line.quantity;
        options = { arrayFilters: [{ "li.locationId": locationId }] };
      }
      await productModel.updateOne(
        { _id: line.productId },
        baseUpdate,
        options,
      );
    } else if (typeof productModel.findByIdAndUpdate === "function") {
      await productModel.findByIdAndUpdate(line.productId, {
        $inc: { stock: line.quantity },
      });
    } else {
      throw new Error("Product model does not support inventory updates");
    }
  }

  await invalidateProductCache(lines, lineData.slugs);
}

export type StockChangeRequest = {
  productId: string;
  variantId?: string;
  locationId?: string;
  /** Target value when `adjustment` is false; signed delta when true. */
  quantity: number;
  adjustment: boolean;
  /** Extra filter (staff scope, vendorId) merged into every match. */
  scopeFilter?: Record<string, unknown>;
};

export type StockChangeResult = { success: boolean; error?: string };

type StockSnapshot = {
  variantStock: number;
  productStock: number;
  locations: Array<{ locationId?: unknown; quantity?: number }>;
  variantFound: boolean;
};

async function readStockSnapshot(
  request: StockChangeRequest,
): Promise<StockSnapshot | null> {
  const doc = (await Product.findOne(
    { _id: request.productId, ...(request.scopeFilter || {}) },
    request.variantId
      ? { stock: 1, variants: { $elemMatch: { _id: request.variantId } } }
      : { stock: 1, locationInventory: 1 },
  ).lean()) as {
    stock?: number;
    locationInventory?: Array<{ locationId?: unknown; quantity?: number }>;
    variants?: Array<{
      stock?: number;
      locationInventory?: Array<{ locationId?: unknown; quantity?: number }>;
    }>;
  } | null;
  if (!doc) return null;
  const variant = request.variantId ? doc.variants?.[0] : undefined;
  return {
    variantFound: !request.variantId || Boolean(variant),
    variantStock: Number(variant?.stock || 0),
    productStock: Number(doc.stock || 0),
    locations: request.variantId
      ? variant?.locationInventory || []
      : doc.locationInventory || [],
  };
}

/**
 * Apply an admin/vendor stock set-or-adjust as a single guarded update.
 *
 * The previous implementation was findOne → mutate arrays in JS → save(),
 * which silently overwrote any sale that landed between the read and the
 * write (document save() $sets whole arrays from the stale in-memory copy).
 * This version pins the value it read in the update filter (compare-and-swap)
 * and retries on interference, so a concurrent atomic decrement is never
 * clobbered. All aggregate fields (variant.stock, inventory.quantity, product
 * stock) are maintained in the same update, preserving the
 * Σ(locationInventory) == stock invariant the pre-validate hook enforces.
 */
export async function applyStockChangeAtomic(
  request: StockChangeRequest,
): Promise<StockChangeResult> {
  if (!request.productId) {
    return { success: false, error: "productId is required" };
  }
  if (!Number.isFinite(request.quantity)) {
    return { success: false, error: "quantity must be a number" };
  }

  for (let attempt = 0; attempt < 3; attempt++) {
    const snapshot = await readStockSnapshot(request);
    if (!snapshot) return { success: false, error: "Product not found" };
    if (!snapshot.variantFound) {
      return { success: false, error: "Variant not found" };
    }

    const scope = request.scopeFilter || {};
    let matched: number | null = null;

    if (request.locationId) {
      const entry = snapshot.locations.find(
        (loc) => String(loc.locationId) === request.locationId,
      );
      const current = Number(entry?.quantity || 0);
      const nextValue = Math.max(
        0,
        request.adjustment ? current + request.quantity : request.quantity,
      );
      const delta = nextValue - current;

      if (entry) {
        if (request.variantId) {
          const result = await Product.updateOne(
            {
              _id: request.productId,
              ...scope,
              variants: {
                $elemMatch: {
                  _id: request.variantId,
                  locationInventory: {
                    $elemMatch: {
                      locationId: request.locationId,
                      quantity: current,
                    },
                  },
                },
              },
            },
            {
              $set: {
                "variants.$[v].locationInventory.$[li].quantity": nextValue,
              },
              $inc: {
                "variants.$[v].stock": delta,
                "variants.$[v].inventory.quantity": delta,
                stock: delta,
              },
            },
            {
              arrayFilters: [
                { "v._id": request.variantId },
                {
                  "li.locationId": request.locationId,
                  "li.quantity": current,
                },
              ],
            },
          );
          matched = result.matchedCount;
        } else {
          const result = await Product.updateOne(
            {
              _id: request.productId,
              ...scope,
              locationInventory: {
                $elemMatch: {
                  locationId: request.locationId,
                  quantity: current,
                },
              },
            },
            {
              $set: { "locationInventory.$[li].quantity": nextValue },
              $inc: { stock: delta },
            },
            {
              arrayFilters: [
                {
                  "li.locationId": request.locationId,
                  "li.quantity": current,
                },
              ],
            },
          );
          matched = result.matchedCount;
        }
      } else {
        // First entry for this location. The stock aggregate becomes
        // Σ(existing entries) + new value, matching the old recompute logic
        // (a legacy non-location stock value is replaced, not added to).
        const existingSum = snapshot.locations.reduce(
          (sum, loc) => sum + Number(loc.quantity || 0),
          0,
        );
        const nextAggregate = existingSum + nextValue;
        if (request.variantId) {
          const delta2 = nextAggregate - snapshot.variantStock;
          const result = await Product.updateOne(
            {
              _id: request.productId,
              ...scope,
              variants: {
                $elemMatch: {
                  _id: request.variantId,
                  stock: snapshot.variantStock,
                  "locationInventory.locationId": { $ne: request.locationId },
                },
              },
            },
            {
              $push: {
                "variants.$[v].locationInventory": {
                  locationId: request.locationId,
                  quantity: nextValue,
                },
              },
              $set: {
                "variants.$[v].stock": nextAggregate,
                "variants.$[v].inventory.quantity": nextAggregate,
              },
              $inc: { stock: delta2 },
            },
            { arrayFilters: [{ "v._id": request.variantId }] },
          );
          matched = result.matchedCount;
        } else {
          const result = await Product.updateOne(
            {
              _id: request.productId,
              ...scope,
              stock: snapshot.productStock,
              "locationInventory.locationId": { $ne: request.locationId },
            },
            {
              $push: {
                locationInventory: {
                  locationId: request.locationId,
                  quantity: nextValue,
                },
              },
              $set: { stock: nextAggregate },
            },
          );
          matched = result.matchedCount;
        }
      }
    } else if (request.variantId) {
      const current = snapshot.variantStock;
      const nextValue = Math.max(
        0,
        request.adjustment ? current + request.quantity : request.quantity,
      );
      const delta = nextValue - current;
      const result = await Product.updateOne(
        {
          _id: request.productId,
          ...scope,
          variants: { $elemMatch: { _id: request.variantId, stock: current } },
        },
        {
          $set: {
            "variants.$[v].stock": nextValue,
            "variants.$[v].inventory.quantity": nextValue,
          },
          $inc: { stock: delta },
        },
        { arrayFilters: [{ "v._id": request.variantId }] },
      );
      matched = result.matchedCount;
    } else {
      const current = snapshot.productStock;
      const nextValue = Math.max(
        0,
        request.adjustment ? current + request.quantity : request.quantity,
      );
      const result = await Product.updateOne(
        { _id: request.productId, ...scope, stock: current },
        { $set: { stock: nextValue } },
      );
      matched = result.matchedCount;
    }

    if (matched === 1) return { success: true };
    // matched 0 → a concurrent write moved the value we pinned; re-read and retry.
  }

  return {
    success: false,
    error: "Stock changed concurrently; please retry",
  };
}

/**
 * Look up slugs for the given inventory lines and trigger a cache
 * invalidation. Best-effort: failures are logged but never thrown, because
 * cache invalidation must not break order placement / refund flows.
 */
async function invalidateProductCache(
  lines: InventoryAdjustmentLine[],
  knownSlugs?: string[],
): Promise<void> {
  const productIds = Array.from(
    new Set(
      lines
        .map((line) => String(line.productId || "").trim())
        .filter(Boolean),
    ),
  );
  if (productIds.length === 0) return;

  try {
    // The decrement/restore paths already loaded these products (for their
    // location inventory) and pass the slugs in, so we avoid a second
    // `Product.find` per order. Other callers omit them and we look them up.
    const slugs =
      knownSlugs ??
      (
        await Product.find({ _id: { $in: productIds } })
          .select("slug")
          .lean()
      )
        .map((p) => p.slug)
        .filter(
          (slug): slug is string => typeof slug === "string" && slug.length > 0,
        );

    revalidateProductContent({ slugs });
  } catch (err) {
    console.error(
      "Failed to invalidate product cache after inventory change:",
      err,
    );
  }
}
