<template>
  <div
    ref="modalWrapRef"
    class="draggable-modal"
    :class="{ fullscreen: fullscreenModel }"
  >
    <Modal
      v-bind="
        omit(props, ['open', 'onCancel', 'onOk', 'onUpdate:open', 'visible'])
      "
      v-model:open="openModel"
      :mask-closable="false"
      :get-container="() => modalWrapRef"
      :width="innerWidth || width"
      @ok="emit('ok')"
      @cancel="emit('cancel')"
    >
      <template #title>
        <slot name="title">{{ $attrs.title || "&nbsp;" }}</slot>
      </template>
      <template #closeIcon>
        <slot name="closeIcon">
          <Space v-if="showFullScreen" class="ant-modal-operate" @click.stop>
            <FullscreenOutlined
              v-if="!fullscreenModel"
              @click="fullscreenModel = true"
            />
            <FullscreenExitOutlined v-else @click="restore" />
            <CloseOutlined @click="closeModal" />
          </Space>
          <span v-else @click.stop>
            <CloseOutlined @click="closeModal" />
          </span>
        </slot>
      </template>
      <slot>
        ① 窗口可以拖动；<br />
        ② 窗口可以通过八个方向改变大小；<br />
        ③ 窗口可以最小化、最大化、还原、关闭；<br />
        ④ 限制窗口最小宽度/高度。
      </slot>
      <template v-if="$slots.footer" #footer>
        <slot name="footer" />
      </template>
    </Modal>
  </div>
</template>

<script lang="ts" name="QmModal" setup>
import { ref, watch, nextTick, onMounted } from "vue";
import { ModalProps } from "@qmth/ui";
import {
  CloseOutlined,
  FullscreenOutlined,
  FullscreenExitOutlined,
} from "@ant-design/icons-vue";
import { throttle, omit } from "lodash-es";
import { Modal, Space } from "ant-design-vue";

// const props = defineProps({
//   ...modalProps(),
//   fullscreen: {
//     type: Boolean,
//     default: false,
//   },
//   showFullScreen: {
//     type: Boolean,
//     default: false,
//   },
//   canDrag: {
//     type: Boolean,
//     default: false,
//   },
// });
const props = withDefaults(
  defineProps<
    {
      fullscreen?: boolean;
      canDrag?: boolean;
      showFullScreen?: boolean;
    } & ModalProps
  >(),
  {
    fullscreen: false,
    canDrag: false,
    showFullScreen: false,
    mask: true,
    closable: true,
  }
);

const emit = defineEmits(["update:open", "update:fullscreen", "ok", "cancel"]);

const openModel = defineModel<boolean>("open");
const fullscreenModel = ref(props.fullscreen);
const innerWidth = ref("");

const cursorStyle = {
  top: "n-resize",
  left: "w-resize",
  right: "e-resize",
  bottom: "s-resize",
  topLeft: "nw-resize",
  topright: "ne-resize",
  bottomLeft: "sw-resize",
  bottomRight: "se-resize",
  auto: "auto",
} as const;

// 是否已经初始化过了
let inited = false;
const modalWrapRef = ref<HTMLDivElement>();

const closeModal = () => {
  openModel.value = false;
  emit("cancel");
};

// 居中弹窗
const centerModal = async () => {
  await nextTick();
  const modalEl =
    modalWrapRef.value?.querySelector<HTMLDivElement>(".ant-modal");

  if (modalEl && modalEl.getBoundingClientRect().left < 1) {
    modalEl.style.left = `${
      (document.documentElement.clientWidth - modalEl.offsetWidth) / 2
    }px`;
  }
};

const restore = async () => {
  fullscreenModel.value = false;
  centerModal();
};

const registerDragTitle = (
  dragEl: HTMLDivElement,
  handleEl: HTMLDivElement
) => {
  handleEl.style.cursor = "move";
  handleEl.onmousedown = throttle((e: MouseEvent) => {
    if (fullscreenModel.value) return;
    document.body.style.userSelect = "none";
    const disX = e.clientX - dragEl.getBoundingClientRect().left;
    const disY = e.clientY - dragEl.getBoundingClientRect().top;
    const mousemove = (event: MouseEvent) => {
      if (fullscreenModel.value) return;
      let iL = event.clientX - disX;
      let iT = event.clientY - disY;
      const maxL = document.documentElement.clientWidth - dragEl.offsetWidth;
      const maxT = document.documentElement.clientHeight - dragEl.offsetHeight;

      iL <= 0 && (iL = 0);
      iT <= 0 && (iT = 0);
      iL >= maxL && (iL = maxL);
      iT >= maxT && (iT = maxT);

      dragEl.style.left = `${Math.max(iL, 0)}px`;
      dragEl.style.top = `${Math.max(iT, 0)}px`;
    };
    const mouseup = () => {
      document.removeEventListener("mousemove", mousemove);
      document.removeEventListener("mouseup", mouseup);
      document.body.style.userSelect = "auto";
    };

    document.addEventListener("mousemove", mousemove);
    document.addEventListener("mouseup", mouseup);
  }, 20);
};

const initDrag = async () => {
  const modalWrapRefEl = modalWrapRef.value!;
  const modalWrapEl =
    modalWrapRefEl.querySelector<HTMLDivElement>(".ant-modal-wrap");
  const modalEl = modalWrapRefEl.querySelector<HTMLDivElement>(".ant-modal");
  if (modalWrapEl && modalEl) {
    centerModal();
    const headerEl = modalEl.querySelector<HTMLDivElement>(".ant-modal-header");
    headerEl && registerDragTitle(modalEl, headerEl);

    modalWrapEl.onmousemove = throttle((event: MouseEvent) => {
      if (fullscreenModel.value) return;
      const left = event.clientX - modalEl.offsetLeft;
      const top = event.clientY - modalEl.offsetTop;
      const right = event.clientX - modalEl.offsetWidth - modalEl.offsetLeft;
      const bottom = event.clientY - modalEl.offsetHeight - modalEl.offsetTop;
      const isLeft = left <= 0 && left > -8;
      const isTop = top < 5 && top > -8;
      const isRight = right >= 0 && right < 8;
      const isBottom = bottom > -5 && bottom < 8;
      // 向左
      if (isLeft && top > 5 && bottom < -5) {
        modalWrapEl.style.cursor = cursorStyle.left;
        // 向上
      } else if (isTop && left > 5 && right < -5) {
        modalWrapEl.style.cursor = cursorStyle.top;
        // 向右
      } else if (isRight && top > 5 && bottom < -5) {
        modalWrapEl.style.cursor = cursorStyle.right;
        // 向下
      } else if (isBottom && left > 5 && right < -5) {
        modalWrapEl.style.cursor = cursorStyle.bottom;
        // 左上角
      } else if (left > -8 && left <= 5 && top <= 5 && top > -8) {
        modalWrapEl.style.cursor = cursorStyle.topLeft;
        // 左下角
      } else if (left > -8 && left <= 5 && bottom <= 5 && bottom > -8) {
        modalWrapEl.style.cursor = cursorStyle.bottomLeft;
        // 右上角
      } else if (right < 8 && right >= -5 && top <= 5 && top > -8) {
        modalWrapEl.style.cursor = cursorStyle.topright;
        // 右下角
      } else if (right < 8 && right >= -5 && bottom <= 5 && bottom > -8) {
        modalWrapEl.style.cursor = cursorStyle.bottomRight;
      } else {
        modalWrapEl.style.cursor = cursorStyle.auto;
      }
    }, 20);
    modalWrapEl.onmousedown = (e: MouseEvent) => {
      if (fullscreenModel.value) return;
      const {
        top: iParentTop,
        bottom: iParentBottom,
        left: iParentLeft,
        right: iParentRight,
      } = modalEl.getBoundingClientRect();

      const disX = e.clientX - iParentLeft;
      const disY = e.clientY - iParentTop;
      const iParentWidth = modalEl.offsetWidth;
      const iParentHeight = modalEl.offsetHeight;

      const cursor = modalWrapEl.style.cursor;

      const mousemove = throttle((event: MouseEvent) => {
        if (fullscreenModel.value) return;
        if (cursor !== cursorStyle.auto) {
          document.body.style.userSelect = "none";
        }
        const mLeft = `${Math.max(0, event.clientX - disX)}px`;
        const mTop = `${Math.max(0, event.clientY - disY)}px`;
        const mLeftWidth = `${Math.min(
          iParentRight,
          iParentWidth + iParentLeft - event.clientX
        )}px`;
        const mRightWidth = `${Math.min(
          window.innerWidth - iParentLeft,
          event.clientX - iParentLeft
        )}px`;
        const mTopHeight = `${Math.min(
          iParentBottom,
          iParentHeight + iParentTop - event.clientY
        )}px`;
        const mBottomHeight = `${Math.min(
          window.innerHeight - iParentTop,
          event.clientY - iParentTop
        )}px`;

        // 向左边拖拽
        if (cursor === cursorStyle.left) {
          modalEl.style.left = mLeft;
          modalEl.style.width = mLeftWidth;
          // 向上边拖拽
        } else if (cursor === cursorStyle.top) {
          modalEl.style.top = mTop;
          modalEl.style.height = mTopHeight;
          // 向右边拖拽
        } else if (cursor === cursorStyle.right) {
          modalEl.style.width = mRightWidth;
          // 向下拖拽
        } else if (cursor === cursorStyle.bottom) {
          modalEl.style.height = mBottomHeight;
          // 左上角拖拽
        } else if (cursor === cursorStyle.topLeft) {
          modalEl.style.left = mLeft;
          modalEl.style.top = mTop;
          modalEl.style.height = mTopHeight;
          modalEl.style.width = mLeftWidth;
          // 右上角拖拽
        } else if (cursor === cursorStyle.topright) {
          modalEl.style.top = mTop;
          modalEl.style.width = mRightWidth;
          modalEl.style.height = mTopHeight;
          // 左下角拖拽
        } else if (cursor === cursorStyle.bottomLeft) {
          modalEl.style.left = mLeft;
          modalEl.style.width = mLeftWidth;
          modalEl.style.height = mBottomHeight;
          // 右下角拖拽
        } else if (cursor === cursorStyle.bottomRight) {
          modalEl.style.width = mRightWidth;
          modalEl.style.height = mBottomHeight;
        }
        innerWidth.value = modalEl.style.width;
      }, 20);

      const mouseup = () => {
        document.removeEventListener("mousemove", mousemove);
        document.removeEventListener("mouseup", mouseup);
        document.body.style.userSelect = "auto";
        modalWrapEl.style.cursor = cursorStyle.auto;
      };

      document.addEventListener("mousemove", mousemove);
      document.addEventListener("mouseup", mouseup);
    };
  }
  inited = true;
};

const init = async (timesObj = { num: 0 }) => {
  await nextTick();
  const modalWrapRefEl = modalWrapRef.value!;
  const modalWrapEl =
    modalWrapRefEl.querySelector<HTMLDivElement>(".ant-modal-wrap");
  const modalEl = modalWrapRefEl.querySelector<HTMLDivElement>(".ant-modal");
  if (timesObj.num >= 10) {
    return;
  }
  if (!(modalWrapEl && modalEl)) {
    timesObj.num++;
    setTimeout(() => {
      init(timesObj);
    }, 10);
    return;
  }
  if ((openModel.value && Object.is(inited, false)) || props.destroyOnClose) {
    if (props.canDrag) {
      initDrag();
    } else {
      centerModal();
    }
  }
};

watch(openModel, () => {
  init();
});
onMounted(() => {
  init();
});
</script>

<style lang="less">
.draggable-modal {
  &.fullscreen {
    .ant-modal {
      inset: 0 !important;
      width: 100% !important;
      max-width: 100vw !important;
      height: 100% !important;
    }

    .ant-modal-content {
      width: 100% !important;
      height: 100% !important;
    }
  }

  .ant-modal-wrap {
    overflow-x: hidden;
  }

  .ant-modal {
    position: relative;
    min-width: 200px;
    min-height: 200px;
    margin: 0;
    padding: 0;

    .ant-modal-header {
      user-select: none;
    }

    .ant-modal-close {
      right: 30px;
      background-color: transparent;
      cursor: inherit;

      &:hover,
      &:focus {
        color: rgb(0 0 0 / 45%);
      }

      .ant-space-item:hover .anticon,
      .ant-space-item:focus .anticon {
        color: rgb(0 0 0 / 75%);
        text-decoration: none;
      }

      .ant-modal-close-x {
        .ant-space {
          width: 100%;
          height: 100%;
        }
      }
    }

    .ant-modal-content {
      /* width: ~'v-bind("props.width")px'; */
      display: flex;
      flex-direction: column;
      width: 100%;
      min-width: 200px;
      height: 100%;
      min-height: 200px;
      padding-top: 0;
      overflow: hidden;

      .ant-modal-body {
        flex: auto;
        height: 100%;
        overflow: auto;
        padding-top: 20px;
        padding-right: 20px;
      }
    }
  }
}
</style>
