import type { App, Directive, DirectiveBinding } from 'vue';

interface DraggableElement extends HTMLElement {
  __modal_draggable__?: {
    handleEl: HTMLElement;
    onMousedown: (e: MouseEvent) => void;
  };
}

const DRAGGABLE_KEY = '__modal_draggable__' as const;

function setupDrag(modalWrap: DraggableElement, handleSelector: string) {
  const dragDom = modalWrap.querySelector('.ant-modal') as HTMLElement;
  const handleEl = modalWrap.querySelector(handleSelector) as HTMLElement;

  if (!dragDom || !handleEl) return;
  if (modalWrap[DRAGGABLE_KEY]) return;

  handleEl.style.cursor = 'move';
  handleEl.style.userSelect = 'none';

  const onMousedown = (e: MouseEvent) => {
    if (!e || modalWrap.classList.contains('fullscreen-modal')) return;
    const disX = e.clientX;
    const disY = e.clientY;
    const screenWidth = document.body.clientWidth;
    const screenHeight = document.documentElement.clientHeight;

    const dragDomWidth = dragDom.offsetWidth;
    const dragDomHeight = dragDom.offsetHeight;
    const minDragDomLeft = dragDom.offsetLeft;
    const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;
    const minDragDomTop = dragDom.offsetTop;
    let maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight;
    if (maxDragDomTop < 0) maxDragDomTop = screenHeight - dragDom.offsetTop;

    const domLeft = getComputedStyle(dragDom).left;
    const domTop = getComputedStyle(dragDom).top;
    let styL = +domLeft;
    let styT = +domTop;

    if (domLeft.includes('%')) {
      styL = +document.body.clientWidth * (+domLeft.replaceAll('%', '') / 100);
      styT = +document.body.clientHeight * (+domTop.replaceAll('%', '') / 100);
    } else {
      styL = +domLeft.replaceAll('px', '');
      styT = +domTop.replaceAll('px', '');
    }

    document.body.style.cursor = 'move';
    document.body.style.userSelect = 'none';

    const onMousemove = (e: MouseEvent) => {
      let left = e.clientX - disX;
      let top = e.clientY - disY;

      if (-left > minDragDomLeft) {
        left = -minDragDomLeft;
      } else if (left > maxDragDomLeft) {
        left = maxDragDomLeft;
      }

      if (-top > minDragDomTop) {
        top = -minDragDomTop;
      } else if (top > maxDragDomTop) {
        top = maxDragDomTop;
      }

      dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
    };

    const onMouseup = () => {
      document.body.style.cursor = '';
      document.body.style.userSelect = '';
      document.removeEventListener('mousemove', onMousemove);
      document.removeEventListener('mouseup', onMouseup);
    };

    document.addEventListener('mousemove', onMousemove);
    document.addEventListener('mouseup', onMouseup);
  };

  handleEl.addEventListener('mousedown', onMousedown);
  (modalWrap as DraggableElement)[DRAGGABLE_KEY] = { handleEl, onMousedown };
}

function cleanupDrag(modalWrap: DraggableElement) {
  const dragData = modalWrap[DRAGGABLE_KEY];
  if (dragData) {
    dragData.handleEl.removeEventListener('mousedown', dragData.onMousedown);
    delete modalWrap[DRAGGABLE_KEY];
  }
}

/** 自动为 .common-container-modal 添加拖拽支持 */
export function setupGlobalDragObserver() {
  const observer = new MutationObserver((mutations) => {
    for (const mutation of mutations) {
      for (const node of mutation.addedNodes) {
        if (node instanceof HTMLElement) {
          if (node.classList.contains('ant-modal-wrap')) {
            const modal = node.querySelector('.common-container-modal');
            if (modal) {
              setTimeout(() => setupDrag(node as DraggableElement, '.ant-modal-header'), 50);
            }
          }
          const wraps = node.querySelectorAll?.('.ant-modal-wrap');
          wraps?.forEach((wrap: HTMLElement) => {
            const modal = wrap.querySelector('.common-container-modal');
            if (modal) {
              setTimeout(() => setupDrag(wrap as DraggableElement, '.ant-modal-header'), 50);
            }
          });
        }
      }
      for (const node of mutation.removedNodes) {
        if (node instanceof HTMLElement) {
          if (node.classList.contains('ant-modal-wrap')) {
            cleanupDrag(node as DraggableElement);
          }
          const wraps = node.querySelectorAll?.('.ant-modal-wrap');
          wraps?.forEach((wrap: HTMLElement) => cleanupDrag(wrap as DraggableElement));
        }
      }
    }
  });

  observer.observe(document.body, { childList: true, subtree: true });
  return observer;
}

/** v-draggable 指令，用于显式标记需要拖拽的 modal */
export const vDraggable: Directive = {
  mounted(el: HTMLElement, binding: DirectiveBinding<string | undefined>) {
    const handleSelector = binding.value || '.ant-modal-header';
    const modalWrap = (el.classList.contains('ant-modal-wrap') ? el : el.closest('.ant-modal-wrap')) as DraggableElement;
    if (!modalWrap) return;

    const observer = new MutationObserver(() => {
      const dragDom = modalWrap.querySelector('.ant-modal');
      if (dragDom) {
        observer.disconnect();
        setupDrag(modalWrap, handleSelector);
      }
    });

    observer.observe(modalWrap, { childList: true, subtree: true });

    if (modalWrap.querySelector('.ant-modal')) {
      observer.disconnect();
      setupDrag(modalWrap, handleSelector);
    }
  },

  unmounted(el: HTMLElement) {
    const modalWrap = (el.classList.contains('ant-modal-wrap') ? el : el.closest('.ant-modal-wrap')) as DraggableElement;
    if (!modalWrap) return;
    cleanupDrag(modalWrap);
  },
};

/** 全局注册指令和自动拖拽 */
export function registerDraggable(app: App) {
  app.directive('draggable', vDraggable);
  setupGlobalDragObserver();
}
