import { getElementSize, getOffsetElement, getElementBorder, getScroll } from "../_utils/dom.mjs";
import { useOutClick } from "../hooks/use-out-click.mjs";
function getWrapperContentRect(wrapperEl, wrapperRect) {
  const { left = 0, top = 0, right = 0, bottom = 0 } = getElementBorder(wrapperEl);
  const rect = wrapperRect || wrapperEl.getBoundingClientRect();
  return {
    left: rect.left + left,
    top: rect.top + top,
    right: rect.right - right,
    bottom: rect.bottom - bottom
  };
}
function getPopupViewOffset(position, t, p, {
  offset = 0
} = {}) {
  const formula = {
    top: {
      left: t.left + t.width / 2 - p.width / 2,
      top: t.top - p.height - offset
    },
    tl: {
      left: t.left,
      top: t.top - p.height - offset
    },
    tr: {
      left: t.right - p.width,
      top: t.top - p.height - offset
    },
    bottom: {
      left: t.left + t.width / 2 - p.width / 2,
      top: t.bottom + offset
    },
    bl: {
      left: t.left,
      top: t.bottom + offset
    },
    br: {
      left: t.right - p.width,
      top: t.bottom + offset
    },
    left: {
      left: t.left - p.width - offset,
      top: t.top + t.height / 2 - p.height / 2
    },
    lt: {
      left: t.left - p.width - offset,
      top: t.top
    },
    lb: {
      left: t.left - p.width - offset,
      top: t.bottom - p.height
    },
    right: {
      left: t.right + offset,
      top: t.top + t.height / 2 - p.height / 2
    },
    rt: {
      left: t.right + offset,
      top: t.top
    },
    rb: {
      left: t.right + offset,
      top: t.bottom - p.height
    }
  };
  return formula[position] || { left: 0, top: 0 };
}
function getWrapperViewEdge(popupSize, wrapperRect, edgeOffset = 0) {
  const viewport = {
    left: edgeOffset,
    right: window.innerWidth - popupSize.width - edgeOffset,
    top: edgeOffset,
    bottom: window.innerHeight - popupSize.height - edgeOffset
  };
  if (!wrapperRect) {
    return viewport;
  }
  return {
    left: Math.max(viewport.left, wrapperRect.left),
    top: Math.max(viewport.top, wrapperRect.top),
    right: Math.min(viewport.right, wrapperRect.right - popupSize.width),
    bottom: Math.min(viewport.bottom, wrapperRect.bottom - popupSize.height)
  };
}
function getPopupEdge(popupSize, targetRect, anchorOffset = 0) {
  const { left, right, top, bottom } = targetRect;
  const { width, height } = popupSize;
  return {
    left: left - width + anchorOffset,
    top: top - height + anchorOffset,
    right: right - anchorOffset,
    bottom: bottom - anchorOffset
  };
}
function getPopupWrapOffset(pos, wrapperEl, wrapperContentRect) {
  if (!wrapperEl) {
    return pos;
  }
  const cs = getScroll(wrapperEl);
  if (wrapperContentRect) {
    return {
      left: pos.left + cs.scrollLeft - wrapperContentRect.left,
      top: pos.top + cs.scrollTop - wrapperContentRect.top
    };
  }
  return {
    left: pos.left + cs.scrollLeft,
    top: pos.top + cs.scrollTop
  };
}
function getDirection(position) {
  switch (position) {
    case "tl":
    case "tr":
    case "top":
      return "top";
    case "bl":
    case "br":
    case "bottom":
      return "bottom";
    case "lt":
    case "lb":
    case "left":
      return "left";
    case "rt":
    case "rb":
    case "right":
      return "right";
  }
}
function adjustPosition(position, direction) {
  const fixFn = {
    top: (p) => {
      if (p === "bottom") {
        return "top";
      } else if (p === "bl") {
        return "tl";
      } else if (p === "br") {
        return "tr";
      }
      return p;
    },
    bottom: (p) => {
      if (p === "top") {
        return "bottom";
      } else if (p === "tl") {
        return "bl";
      } else if (p === "tr") {
        return "br";
      }
      return p;
    },
    left: (p) => {
      if (p === "right") {
        return "left";
      } else if (p === "rt") {
        return "lt";
      } else if (p === "rb") {
        return "lb";
      }
      return p;
    },
    right: (p) => {
      if (p === "left") {
        return "right";
      } else if (p === "lt") {
        return "rt";
      } else if (p === "lb") {
        return "rb";
      }
      return p;
    }
  };
  const fn = fixFn[direction];
  return fn ? fn(position) : position;
}
function adjustOffset(position, popupPosition, popupSize, pRect, tRect, wRect, {
  anchorOffset,
  offset,
  edgeOffset
} = {}) {
  const { top, left } = popupPosition;
  const edge = getWrapperViewEdge(popupSize, wRect, edgeOffset);
  let fixedPosition = position;
  let style = popupPosition;
  const d = getDirection(position);
  if (d === "top") {
    if (edge.top > top) {
      fixedPosition = adjustPosition(position, "bottom");
      style = getPopupViewOffset(fixedPosition, tRect, pRect, { offset });
    }
  } else if (d === "left") {
    if (edge.left > left) {
      fixedPosition = adjustPosition(position, "right");
      style = getPopupViewOffset(fixedPosition, tRect, pRect, { offset });
    }
  } else if (d === "right") {
    if (edge.right < left) {
      fixedPosition = adjustPosition(position, "left");
      style = getPopupViewOffset(fixedPosition, tRect, pRect, { offset });
    }
  } else if (d === "bottom") {
    if (edge.bottom < top) {
      fixedPosition = adjustPosition(position, "top");
      style = getPopupViewOffset(fixedPosition, tRect, pRect, { offset });
    }
  }
  const popEdge = getPopupEdge(popupSize, tRect, anchorOffset);
  if (["top", "bottom"].includes(d)) {
    if (edge.left > left) {
      style.left = edge.left < popEdge.right ? edge.left : popEdge.right;
    } else if (edge.right < left) {
      style.left = edge.right > popEdge.left ? edge.right : popEdge.left;
    }
  }
  if (["left", "right"].includes(d)) {
    if (edge.top > top) {
      style.top = edge.top < popEdge.bottom ? edge.top : popEdge.bottom;
    } else if (edge.bottom < top) {
      style.top = edge.bottom > popEdge.top ? edge.bottom : popEdge.top;
    }
  }
  return {
    position: fixedPosition,
    popupStyle: style
  };
}
function getAnchorOffset(position, tRect, popupStyle, popupSize, anchorOffset = 8) {
  const pos = {};
  const limit = anchorOffset;
  const { left: pl, top: pt } = popupStyle;
  if (["top", "tl", "tr", "bottom", "bl", "br"].includes(position)) {
    let l = tRect.left + tRect.width / 2 - pl;
    if (l > popupSize.width - limit) {
      l = popupSize.width - limit;
    } else if (l < limit) {
      l = limit;
    }
    pos.left = l;
    if (["top", "tl", "tr"].includes(position)) {
      pos.bottom = 0;
    } else {
      pos.top = 0;
    }
  } else if (["left", "lt", "lb", "right", "rt", "rb"].includes(position)) {
    let t = tRect.top + tRect.height / 2 - pt;
    if (t > popupSize.height - limit) {
      t = popupSize.height - limit;
    } else if (t < limit) {
      t = limit;
    }
    pos.top = t;
    if (["left", "lt", "lb"].includes(position)) {
      pos.right = 0;
    } else {
      pos.left = 0;
    }
  }
  return pos;
}
function calcPopupStyle(popupEl, targetEl, position, {
  adaptive = true,
  // 自适应容器边缘
  anchor = true,
  // 是否计算anchor
  anchorOffset = 8,
  // anchor与容器边缘偏移量
  offset = 8,
  // popup 距离target偏移量
  edgeOffset = 0
  // popup 与容器边缘最小距离
} = {}) {
  const tRect = targetEl.getBoundingClientRect();
  const pRect = popupEl.getBoundingClientRect();
  const popupSize = getElementSize(popupEl);
  let popupStyle = getPopupViewOffset(position, tRect, pRect, { offset });
  let anchorStyle = {};
  const wrapperEl = getOffsetElement(popupEl);
  if (!wrapperEl) {
    return {
      popupStyle,
      position,
      anchorStyle
    };
  }
  const wrapperRect = wrapperEl.getBoundingClientRect();
  let wrapperContentRect = void 0;
  if (wrapperEl.nodeName !== "HTML") {
    wrapperContentRect = getWrapperContentRect(wrapperEl, wrapperRect);
  }
  let fixedPosition = position;
  if (adaptive) {
    const rlt = adjustOffset(position, popupStyle, popupSize, pRect, tRect, wrapperContentRect, {
      offset,
      anchorOffset: anchor ? anchorOffset : 0,
      edgeOffset
    });
    fixedPosition = rlt.position;
    popupStyle = rlt.popupStyle;
  }
  if (anchor) {
    anchorStyle = getAnchorOffset(fixedPosition, tRect, popupStyle, popupSize, anchorOffset);
  }
  popupStyle = getPopupWrapOffset(popupStyle, wrapperEl, wrapperContentRect);
  return {
    position: fixedPosition,
    popupStyle,
    anchorStyle,
    isFixed: fixedPosition === position
  };
}
function bindTrigger(el, popupRef, triggers, {
  updateFn,
  hoverDelay = 100,
  autoHide = true
}) {
  if (!el) {
    return [];
  }
  const outClick = useOutClick();
  const listeners = [];
  const showFn = () => {
    updateFn(true);
  };
  const hideFn = () => {
    updateFn(false);
  };
  const toggleFn = () => {
    updateFn();
  };
  const enterFn = () => {
    updateFn(true, hoverDelay);
  };
  const leavefn = () => {
    updateFn(false, hoverDelay);
  };
  const onClickFn = () => {
    hideFn();
    outClick.removeListener(el, onClickFn);
  };
  const clickFn = (toggle) => {
    const handlerFn = toggle ? toggleFn : showFn;
    return () => {
      el == null ? void 0 : el.addEventListener("click", () => {
        handlerFn();
        if (autoHide) {
          outClick.addListener(el, onClickFn, {
            exception: (e) => {
              var _a;
              return !!((_a = popupRef.value) == null ? void 0 : _a.contains(e.target));
            }
          });
        }
      });
      listeners.push(() => {
        el == null ? void 0 : el.removeEventListener("click", handlerFn);
      });
    };
  };
  const triggerHandlers = {
    hover: () => {
      el == null ? void 0 : el.addEventListener("mouseenter", enterFn);
      listeners.push(() => {
        el == null ? void 0 : el.removeEventListener("mouseenter", enterFn);
      });
      if (autoHide) {
        el == null ? void 0 : el.addEventListener("mouseleave", leavefn);
        listeners.push(() => {
          el == null ? void 0 : el.removeEventListener("mouseleave", leavefn);
        });
      }
    },
    // 点击目标，切换显示
    click: clickFn(true),
    // 点击目标，始终显示，当点击外部才隐藏
    "click-outclick": clickFn(false),
    focus: () => {
      el == null ? void 0 : el.addEventListener("focusin", showFn);
      listeners.push(() => {
        el == null ? void 0 : el.removeEventListener("focusin", showFn);
      });
      if (autoHide) {
        el == null ? void 0 : el.addEventListener("focusout", hideFn);
        listeners.push(() => {
          el == null ? void 0 : el.removeEventListener("focusout", hideFn);
        });
      }
    },
    contextmenu: () => {
      const fn = (e) => {
        e.preventDefault();
        showFn();
      };
      el == null ? void 0 : el.addEventListener("contextmenu", fn);
      listeners.push(() => {
        el == null ? void 0 : el.removeEventListener("contextmenu", fn);
      });
      if (autoHide) {
        outClick.addListener(el, hideFn, {
          exception: (e) => {
            var _a;
            return !!((_a = popupRef.value) == null ? void 0 : _a.contains(e.target));
          }
        });
        listeners.push(() => {
          outClick.removeListener(el, hideFn);
        });
      }
    },
    none: () => {
    },
    // hover 显示 outclick隐藏
    "hover-outclick": () => {
      el == null ? void 0 : el.addEventListener("mouseenter", enterFn);
      listeners.push(() => {
        el == null ? void 0 : el.removeEventListener("mouseenter", enterFn);
      });
      if (autoHide) {
        outClick.addListener(el, hideFn, {
          exception: (e) => {
            var _a;
            return !!((_a = popupRef.value) == null ? void 0 : _a.contains(e.target));
          }
        });
        listeners.push(() => {
          outClick.removeListener(el, hideFn);
        });
      }
    }
  };
  triggers.forEach((tr) => {
    const fn = triggerHandlers[tr];
    if (fn) {
      fn();
    }
  });
  return listeners;
}
function getTransformOrigin(position) {
  let left = "0px";
  let top = "0px";
  if (["lt", "lb", "left", "tr", "br"].includes(position)) {
    left = "100%";
  } else if (["top", "bottom"].includes(position)) {
    left = "50%";
  }
  if (["tl", "tr", "top", "lb", "rb"].includes(position)) {
    top = "100%";
  } else if (["left", "right"].includes(position)) {
    top = "50%";
  }
  return {
    top,
    left
  };
}
export {
  bindTrigger,
  calcPopupStyle,
  getTransformOrigin
};
