(function (root, factory) {
  "use strict";

  const api = factory();

  if (typeof module === "object" && module.exports) {
    module.exports = api;
  }

  if (root) {
    root.LegacyCutDrag = api;
  }
}(typeof globalThis !== "undefined" ? globalThis : this, function () {
  "use strict";

  function assertPoint(point, name) {
    if (!point || typeof point !== "object" ||
        !Number.isFinite(point.x) || !Number.isFinite(point.y)) {
      throw new TypeError(name + " must contain finite x and y numbers.");
    }
  }

  function assertDragSize(dragSize) {
    if (!dragSize || typeof dragSize !== "object" ||
        !Number.isFinite(dragSize.width) ||
        !Number.isFinite(dragSize.height) ||
        dragSize.width <= 0 || dragSize.height <= 0) {
      throw new RangeError(
        "dragSize must contain positive finite width and height values.");
    }
  }

  function assertRectangle(rectangle) {
    if (!rectangle || typeof rectangle !== "object" ||
        !Number.isFinite(rectangle.left) ||
        !Number.isFinite(rectangle.top) ||
        !Number.isFinite(rectangle.right) ||
        !Number.isFinite(rectangle.bottom) ||
        !Number.isFinite(rectangle.width) ||
        !Number.isFinite(rectangle.height) ||
        rectangle.width <= 0 || rectangle.height <= 0 ||
        rectangle.right !== rectangle.left + rectangle.width ||
        rectangle.bottom !== rectangle.top + rectangle.height) {
      throw new TypeError("rectangle must be a valid legacy drag rectangle.");
    }
  }

  function createDragRectangle(down, dragSize) {
    assertPoint(down, "down");
    assertDragSize(dragSize);

    const left = down.x - dragSize.width;
    const top = down.y - dragSize.height;

    return Object.freeze({
      left: left,
      top: top,
      width: dragSize.width,
      height: dragSize.height,
      right: left + dragSize.width,
      bottom: top + dragSize.height
    });
  }

  function contains(rectangle, point) {
    assertRectangle(rectangle);
    assertPoint(point, "point");

    // System.Drawing.Rectangle.Contains includes left/top and excludes
    // right/bottom. The original mouse-down point is therefore outside.
    return point.x >= rectangle.left && point.x < rectangle.right &&
      point.y >= rectangle.top && point.y < rectangle.bottom;
  }

  function shouldStart(rectangle, point) {
    return !contains(rectangle, point);
  }

  function toCssDragSize(dipSize, hostRasterizationScale, devicePixelRatio) {
    assertDragSize(dipSize);

    // Chromium's devicePixelRatio includes both the XAML monitor scale and
    // CoreWebView2 browser zoom. Dividing the DIP metric by their ratio maps
    // the Windows threshold into PointerEvent clientX/clientY CSS pixels.
    const hostScale = Number.isFinite(hostRasterizationScale) &&
      hostRasterizationScale > 0 ? hostRasterizationScale : 1;
    const deviceScale = Number.isFinite(devicePixelRatio) &&
      devicePixelRatio > 0 ? devicePixelRatio : hostScale;
    const candidateZoom = deviceScale / hostScale;
    // WebView2 normalizes controller zoom into its supported practical range.
    // Treat values outside that range as an untrustworthy measurement and use
    // a neutral ratio instead of producing an unusable threshold.
    const browserZoom = Number.isFinite(candidateZoom) &&
      candidateZoom >= 0.25 && candidateZoom <= 5
      ? candidateZoom
      : 1;
    return Object.freeze({
      width: dipSize.width / browserZoom,
      height: dipSize.height / browserZoom
    });
  }

  return Object.freeze({
    createDragRectangle: createDragRectangle,
    contains: contains,
    shouldStart: shouldStart,
    toCssDragSize: toCssDragSize
  });
}));
