import {
  useCallback,
  useEffect,
  useRef,
  useState,
  type MouseEvent as ReactMouseEvent,
  type ReactNode,
} from 'react'
import { Modal as AntModal, type ModalFuncProps, type ModalProps } from 'antd'

export type DraggableModalProps = ModalProps & {
  /** 타이틀바 우측(닫기 앞) 추가 액션 */
  titleActions?: ReactNode
  /** 타이틀바 드래그 (기본 true) */
  draggable?: boolean
}

type DragState = {
  startX: number
  startY: number
  origX: number
  origY: number
}

function useDraggableOffset(open?: boolean) {
  const [offset, setOffset] = useState({ x: 0, y: 0 })
  const offsetRef = useRef(offset)
  const dragRef = useRef<DragState | null>(null)
  /** 드래그 후 mask 합성 click 으로 닫히는 것 방지 */
  const suppressCloseUntilRef = useRef(0)

  useEffect(() => {
    offsetRef.current = offset
  }, [offset])

  useEffect(() => {
    if (!open) {
      setOffset({ x: 0, y: 0 })
      offsetRef.current = { x: 0, y: 0 }
      dragRef.current = null
      suppressCloseUntilRef.current = 0
    }
  }, [open])

  const onTitleMouseDown = useCallback((e: ReactMouseEvent) => {
    if (e.button !== 0) return
    const target = e.target as HTMLElement | null
    if (target?.closest('button, a, input, select, textarea, .app-modal-close, .ant-btn')) return

    e.preventDefault()
    e.stopPropagation()

    const current = offsetRef.current
    dragRef.current = {
      startX: e.clientX,
      startY: e.clientY,
      origX: current.x,
      origY: current.y,
    }
    let moved = false

    const onMove = (ev: MouseEvent) => {
      const d = dragRef.current
      if (!d) return
      const dx = ev.clientX - d.startX
      const dy = ev.clientY - d.startY
      if (Math.abs(dx) > 2 || Math.abs(dy) > 2) moved = true
      setOffset({ x: d.origX + dx, y: d.origY + dy })
    }

    const blockSyntheticClick = (ev: MouseEvent) => {
      if (Date.now() < suppressCloseUntilRef.current) {
        ev.preventDefault()
        ev.stopPropagation()
      }
    }

    const onUp = () => {
      dragRef.current = null
      // mousedown(헤더) → mouseup(마스크) 시 wrap 에 click 이 가서 닫히는 문제 방지
      suppressCloseUntilRef.current = Date.now() + (moved ? 300 : 120)
      window.removeEventListener('mousemove', onMove, true)
      window.removeEventListener('mouseup', onUp, true)
      window.setTimeout(() => {
        window.removeEventListener('click', blockSyntheticClick, true)
      }, 320)
    }

    window.addEventListener('mousemove', onMove, true)
    window.addEventListener('mouseup', onUp, true)
    window.addEventListener('click', blockSyntheticClick, true)
  }, [])

  const shouldSuppressClose = useCallback(() => Date.now() < suppressCloseUntilRef.current, [])

  return { offset, onTitleMouseDown, shouldSuppressClose }
}

function withModalClass(className?: string) {
  return ['app-draggable-modal', className].filter(Boolean).join(' ')
}

function TitleBar({
  title,
  titleActions,
  draggable,
  showClose,
  onClose,
  onMouseDown,
}: {
  title?: ReactNode
  titleActions?: ReactNode
  draggable: boolean
  showClose: boolean
  onClose?: (e: ReactMouseEvent<HTMLButtonElement>) => void
  onMouseDown: (e: ReactMouseEvent) => void
}) {
  return (
    <div
      className={`app-modal-titlebar${draggable ? ' is-draggable' : ''}`}
      onMouseDown={draggable ? onMouseDown : undefined}
    >
      <div className="app-modal-title">{title}</div>
      <div className="app-modal-title-right">
        {titleActions}
        {showClose && (
          <button
            type="button"
            className="app-modal-close"
            onClick={onClose}
            onMouseDown={(e) => e.stopPropagation()}
            aria-label="닫기"
          >
            ×
          </button>
        )}
      </div>
    </div>
  )
}

function DraggableModal({
  className,
  rootClassName,
  modalRender,
  open,
  title,
  titleActions,
  draggable = true,
  centered = false,
  closable = true,
  onCancel,
  classNames,
  styles,
  ...rest
}: DraggableModalProps) {
  const { offset, onTitleMouseDown, shouldSuppressClose } = useDraggableOffset(open)
  const showClose = closable !== false

  const handleCancel: ModalProps['onCancel'] = (e) => {
    if (shouldSuppressClose()) return
    onCancel?.(e)
  }

  const baseStyles = {
    container: {
      padding: 0,
      overflow: 'hidden' as const,
      borderRadius: 8,
      border: '1px solid var(--border)',
      background: 'var(--surface)',
      boxShadow: 'var(--shadow)',
    },
    header: {
      margin: 0,
      padding: 0,
      background: 'transparent',
      borderBottom: 'none',
    },
    body: {
      padding: '16px 18px 18px',
      background: 'var(--surface)',
      color: 'var(--text)',
    },
    footer: {
      margin: 0,
      padding: '12px 16px 14px',
      borderTop: '1px solid var(--border)',
      background: 'var(--surface)',
    },
  }

  return (
    <AntModal
      {...rest}
      open={open}
      centered={centered}
      closable={false}
      onCancel={handleCancel}
      className={withModalClass(className)}
      rootClassName={['app-draggable-modal-root', rootClassName].filter(Boolean).join(' ')}
      classNames={classNames}
      styles={baseStyles}
      title={(
        <TitleBar
          title={title}
          titleActions={titleActions}
          draggable={draggable}
          showClose={showClose}
          onClose={(e) => onCancel?.(e)}
          onMouseDown={onTitleMouseDown}
        />
      )}
      modalRender={(node) => {
        const wrapped = (
          <div
            className="app-modal-drag-root"
            style={{ transform: `translate(${offset.x}px, ${offset.y}px)` }}
            onMouseDown={(e) => e.stopPropagation()}
          >
            {node}
          </div>
        )
        return modalRender ? modalRender(wrapped) : wrapped
      }}
    />
  )
}

function StaticDragShell({
  title,
  children,
  onTitleMouseDown,
  offset,
}: {
  title?: ReactNode
  children: ReactNode
  onTitleMouseDown: (e: ReactMouseEvent) => void
  offset: { x: number; y: number }
}) {
  return (
    <div
      className="app-modal-drag-root"
      style={{ transform: `translate(${offset.x}px, ${offset.y}px)` }}
      onMouseDown={(e) => e.stopPropagation()}
    >
      <div className="app-modal-static-panel">
        <TitleBar
          title={title}
          draggable
          showClose={false}
          onMouseDown={onTitleMouseDown}
        />
        <div className="app-modal-slot">{children}</div>
      </div>
    </div>
  )
}

function StaticDragWrap({ title, children }: { title?: ReactNode; children: ReactNode }) {
  const [open] = useState(true)
  const { offset, onTitleMouseDown, shouldSuppressClose } = useDraggableOffset(open)

  useEffect(() => {
    const block = (ev: MouseEvent) => {
      if (shouldSuppressClose()) {
        ev.preventDefault()
        ev.stopPropagation()
      }
    }
    window.addEventListener('click', block, true)
    return () => window.removeEventListener('click', block, true)
  }, [shouldSuppressClose])

  return (
    <StaticDragShell title={title} offset={offset} onTitleMouseDown={onTitleMouseDown}>
      {children}
    </StaticDragShell>
  )
}

function wrapStatic(config: ModalFuncProps = {}): ModalFuncProps {
  const userRender = config.modalRender
  const title = config.title
  return {
    ...config,
    title: null,
    closable: false,
    centered: config.centered ?? false,
    className: withModalClass(config.className),
    rootClassName: ['app-draggable-modal-root', config.rootClassName].filter(Boolean).join(' '),
    modalRender: (node) => {
      const wrapped = <StaticDragWrap title={title}>{node}</StaticDragWrap>
      return userRender ? userRender(wrapped) : wrapped
    },
  }
}

DraggableModal.confirm = (props?: ModalFuncProps) => AntModal.confirm(wrapStatic(props))
DraggableModal.info = (props?: ModalFuncProps) => AntModal.info(wrapStatic(props))
DraggableModal.success = (props?: ModalFuncProps) => AntModal.success(wrapStatic(props))
DraggableModal.error = (props?: ModalFuncProps) => AntModal.error(wrapStatic(props))
DraggableModal.warning = (props?: ModalFuncProps) => AntModal.warning(wrapStatic(props))
DraggableModal.warn = (props?: ModalFuncProps) => AntModal.warn(wrapStatic(props))
DraggableModal.destroyAll = AntModal.destroyAll
DraggableModal.useModal = AntModal.useModal
DraggableModal.config = AntModal.config

export default DraggableModal
export type { ModalProps }
