"use client"

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react"
import { useIsXs } from "@/hooks/use-xs"
import {
  getDragPointerSnapshot,
  notifyDragPointerMove,
  resetDragPointer,
} from "@/lib/drag-pointer-store"

export type EmailDragPhase = "dragging" | "returning"

export type EmailDragState = {
  ids: string[]
  sourceFolderId: string
  hoveredTargetId: string | null
  hoveredTargetLabel: string | null
  /** Last committed pointer (begin drag + snapshot when entering `returning`). */
  pointerX: number
  pointerY: number
  originX: number
  originY: number
  phase: EmailDragPhase
}

type OnDropCallback = (targetId: string, targetLabel: string, ids: string[]) => void

type EmailDragContextValue = {
  state: EmailDragState | null
  beginDrag: (ids: string[], sourceFolderId: string, pointerX: number, pointerY: number) => void
  setHoveredTarget: (id: string | null, label: string | null) => void
  clearHoveredTarget: (id: string) => void
  completeDrop: (targetId: string, targetLabel: string) => void
  cancelDrag: () => void
  registerOnDrop: (cb: OnDropCallback) => () => void
}

const EmailDragContext = createContext<EmailDragContextValue | null>(null)

/** Duration of the "return to origin" animation when drag is cancelled. */
const RETURN_ANIMATION_MS = 240

export function EmailDragProvider({ children }: { children: React.ReactNode }) {
  const isXs = useIsXs()
  const [state, setState] = useState<EmailDragState | null>(null)
  const stateRef = useRef<EmailDragState | null>(null)
  const onDropRef = useRef<OnDropCallback | null>(null)

  useEffect(() => {
    stateRef.current = state
  }, [state])

  useEffect(() => {
    if (!isXs) return
    stateRef.current = null
    setState(null)
    resetDragPointer(0, 0)
  }, [isXs])

  const beginDrag = useCallback(
    (ids: string[], sourceFolderId: string, pointerX: number, pointerY: number) => {
      if (isXs || ids.length === 0) return
      const next: EmailDragState = {
        ids,
        sourceFolderId,
        hoveredTargetId: null,
        hoveredTargetLabel: null,
        pointerX,
        pointerY,
        originX: pointerX,
        originY: pointerY,
        phase: "dragging",
      }
      stateRef.current = next
      resetDragPointer(pointerX, pointerY)
      setState(next)
    },
    [isXs]
  )

  const setHoveredTarget = useCallback((id: string | null, label: string | null) => {
    setState((prev) => {
      if (!prev || prev.phase !== "dragging") return prev
      if (prev.hoveredTargetId === id) return prev
      return { ...prev, hoveredTargetId: id, hoveredTargetLabel: label }
    })
  }, [])

  const clearHoveredTarget = useCallback((id: string) => {
    setState((prev) => {
      if (!prev) return prev
      if (prev.hoveredTargetId !== id) return prev
      return { ...prev, hoveredTargetId: null, hoveredTargetLabel: null }
    })
  }, [])

  const cancelDrag = useCallback(() => {
    const cur = stateRef.current
    if (!cur) return
    if (cur.phase === "returning") return
    const p = getDragPointerSnapshot()
    const next: EmailDragState = {
      ...cur,
      phase: "returning",
      hoveredTargetId: null,
      hoveredTargetLabel: null,
      pointerX: p.x,
      pointerY: p.y,
    }
    stateRef.current = next
    setState(next)
  }, [])

  const completeDrop = useCallback((targetId: string, targetLabel: string) => {
    const cur = stateRef.current
    if (!cur) return
    stateRef.current = null
    setState(null)
    resetDragPointer(0, 0)
    if (targetId !== cur.sourceFolderId && onDropRef.current) {
      const cb = onDropRef.current
      const ids = cur.ids
      queueMicrotask(() => cb(targetId, targetLabel, ids))
    }
  }, [])

  const registerOnDrop = useCallback((cb: OnDropCallback) => {
    onDropRef.current = cb
    return () => {
      if (onDropRef.current === cb) onDropRef.current = null
    }
  }, [])

  // Live pointer during native drag — updates external store only (no React state).
  useEffect(() => {
    if (!state || state.phase !== "dragging") return
    const onDragOver = (e: DragEvent) => {
      notifyDragPointerMove(e.clientX, e.clientY)
    }
    const onDragEnd = () => {
      const cur = stateRef.current
      if (!cur) return
      if (cur.phase === "returning") return
      const p = getDragPointerSnapshot()
      const next: EmailDragState = {
        ...cur,
        phase: "returning",
        hoveredTargetId: null,
        hoveredTargetLabel: null,
        pointerX: p.x,
        pointerY: p.y,
      }
      stateRef.current = next
      setState(next)
    }
    window.addEventListener("dragover", onDragOver)
    window.addEventListener("dragend", onDragEnd)
    return () => {
      window.removeEventListener("dragover", onDragOver)
      window.removeEventListener("dragend", onDragEnd)
    }
  }, [state])

  // Auto-clear after the return animation finishes.
  useEffect(() => {
    if (state?.phase !== "returning") return
    const id = window.setTimeout(() => {
      stateRef.current = null
      setState(null)
      resetDragPointer(0, 0)
    }, RETURN_ANIMATION_MS + 20)
    return () => window.clearTimeout(id)
  }, [state?.phase])

  const value = useMemo<EmailDragContextValue>(
    () => ({
      state,
      beginDrag,
      setHoveredTarget,
      clearHoveredTarget,
      completeDrop,
      cancelDrag,
      registerOnDrop,
    }),
    [
      state,
      beginDrag,
      setHoveredTarget,
      clearHoveredTarget,
      completeDrop,
      cancelDrag,
      registerOnDrop,
    ]
  )

  return <EmailDragContext.Provider value={value}>{children}</EmailDragContext.Provider>
}

export function useEmailDrag() {
  const ctx = useContext(EmailDragContext)
  if (!ctx) {
    throw new Error("useEmailDrag must be used inside <EmailDragProvider>")
  }
  return ctx
}

export const EMAIL_DRAG_RETURN_MS = RETURN_ANIMATION_MS

const noopDropHandlers = {
  onDragEnter: () => {},
  onDragOver: () => {},
  onDragLeave: () => {},
  onDrop: () => {},
}

export function useEmailDropTarget(targetId: string, targetLabel: string) {
  const isXs = useIsXs()
  const { state, setHoveredTarget, clearHoveredTarget, completeDrop } = useEmailDrag()

  if (isXs) {
    return {
      isOver: false,
      acceptsDrop: false,
      isSource: false,
      dropHandlers: noopDropHandlers,
    }
  }

  const isDragging = state !== null && state.phase === "dragging"
  const isSource = !!state && state.sourceFolderId === targetId
  const acceptsDrop = isDragging && !isSource
  const isOver = !!state && state.hoveredTargetId === targetId

  const onDragEnter = (e: React.DragEvent) => {
    if (!acceptsDrop) return
    e.preventDefault()
    setHoveredTarget(targetId, targetLabel)
  }
  const onDragOver = (e: React.DragEvent) => {
    if (!acceptsDrop) return
    e.preventDefault()
    if (e.dataTransfer) e.dataTransfer.dropEffect = "move"
    if (!isOver) setHoveredTarget(targetId, targetLabel)
  }
  const onDragLeave = (e: React.DragEvent) => {
    if (!acceptsDrop) return
    const rt = e.relatedTarget as Node | null
    if (rt && e.currentTarget instanceof Node && e.currentTarget.contains(rt)) {
      return
    }
    clearHoveredTarget(targetId)
  }
  const onDrop = (e: React.DragEvent) => {
    if (!acceptsDrop) return
    e.preventDefault()
    completeDrop(targetId, targetLabel)
  }

  return {
    isOver: acceptsDrop && isOver,
    acceptsDrop,
    isSource,
    dropHandlers: { onDragEnter, onDragOver, onDragLeave, onDrop },
  }
}
