"use client"

import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { Maximize2, Minimize2, X } from "lucide-react"

import { cn } from "@/lib/utils"

const WINDOW_GAP = 16

const Dialog = DialogPrimitive.Root
const DialogTrigger = DialogPrimitive.Trigger
const DialogClose = DialogPrimitive.Close

const DialogPortal = DialogPrimitive.Portal

const DialogOverlay = React.forwardRef<
  React.ElementRef<typeof DialogPrimitive.Overlay>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
  <DialogPrimitive.Overlay
    ref={ref}
    className={cn(
      "fixed inset-0 z-50 bg-black/60 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
      className
    )}
    {...props}
  />
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName

type Position = {
  x: number
  y: number
}

type DragState = {
  pointerId: number
  startPointerX: number
  startPointerY: number
  startX: number
  startY: number
}

interface DialogContentProps
  extends Omit<React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>, "title"> {
  title?: React.ReactNode
  defaultWidth?: number
  defaultHeight?: number
  minWidth?: number
  minHeight?: number
}

const DialogContent = React.forwardRef<
  React.ElementRef<typeof DialogPrimitive.Content>,
  DialogContentProps
>(
  (
    {
      className,
      children,
      title,
      defaultWidth = 900,
      defaultHeight = 640,
      minWidth = 560,
      minHeight = 360,
      ...props
    },
    ref
  ) => {
    const [viewport, setViewport] = React.useState({ width: 0, height: 0 })
    const [position, setPosition] = React.useState<Position>({
      x: WINDOW_GAP,
      y: WINDOW_GAP,
    })
    const [isMaximized, setIsMaximized] = React.useState(false)
    const dragRef = React.useRef<DragState | null>(null)

    const clampPosition = React.useCallback(
      (x: number, y: number, width: number, height: number): Position => {
        const maxX = Math.max(WINDOW_GAP, window.innerWidth - width - WINDOW_GAP)
        const maxY = Math.max(WINDOW_GAP, window.innerHeight - height - WINDOW_GAP)
        return {
          x: Math.min(Math.max(WINDOW_GAP, x), maxX),
          y: Math.min(Math.max(WINDOW_GAP, y), maxY),
        }
      },
      []
    )

    const centerWindow = React.useCallback(() => {
      const width = Math.min(defaultWidth, window.innerWidth - WINDOW_GAP * 2)
      const height = Math.min(defaultHeight, window.innerHeight - WINDOW_GAP * 2)
      setPosition(clampPosition((window.innerWidth - width) / 2, (window.innerHeight - height) / 2, width, height))
    }, [clampPosition, defaultHeight, defaultWidth])

    React.useEffect(() => {
      const syncViewport = () =>
        setViewport({ width: window.innerWidth, height: window.innerHeight })

      syncViewport()
      window.addEventListener("resize", syncViewport)
      return () => window.removeEventListener("resize", syncViewport)
    }, [])

    React.useEffect(() => {
      if (viewport.width === 0 || viewport.height === 0) {
        return
      }
      centerWindow()
    }, [centerWindow, viewport.height, viewport.width])

    React.useEffect(() => {
      const handleResize = () => {
        if (isMaximized) {
          return
        }
        const width = Math.min(defaultWidth, window.innerWidth - WINDOW_GAP * 2)
        const height = Math.min(defaultHeight, window.innerHeight - WINDOW_GAP * 2)
        setPosition((prev) => clampPosition(prev.x, prev.y, width, height))
      }

      window.addEventListener("resize", handleResize)
      return () => window.removeEventListener("resize", handleResize)
    }, [clampPosition, defaultHeight, defaultWidth, isMaximized])

    const isInteractiveTarget = (target: EventTarget | null) => {
      if (!(target instanceof HTMLElement)) {
        return false
      }
      return Boolean(target.closest("button, a, input, select, textarea, [role='button']"))
    }

    const onHeaderPointerDown = (event: React.PointerEvent<HTMLDivElement>) => {
      if (isMaximized || event.button !== 0 || isInteractiveTarget(event.target)) {
        return
      }

      dragRef.current = {
        pointerId: event.pointerId,
        startPointerX: event.clientX,
        startPointerY: event.clientY,
        startX: position.x,
        startY: position.y,
      }

      event.currentTarget.setPointerCapture(event.pointerId)
      event.preventDefault()
    }

    const onHeaderPointerMove = (event: React.PointerEvent<HTMLDivElement>) => {
      const drag = dragRef.current
      if (!drag || drag.pointerId !== event.pointerId || isMaximized) {
        return
      }

      const width = Math.min(defaultWidth, window.innerWidth - WINDOW_GAP * 2)
      const height = Math.min(defaultHeight, window.innerHeight - WINDOW_GAP * 2)
      const nextX = drag.startX + (event.clientX - drag.startPointerX)
      const nextY = drag.startY + (event.clientY - drag.startPointerY)

      setPosition(clampPosition(nextX, nextY, width, height))
    }

    const onHeaderPointerUp = (event: React.PointerEvent<HTMLDivElement>) => {
      if (dragRef.current?.pointerId === event.pointerId) {
        event.currentTarget.releasePointerCapture(event.pointerId)
        dragRef.current = null
      }
    }

    const width = Math.min(
      defaultWidth,
      Math.max(minWidth, viewport.width - WINDOW_GAP * 2)
    )
    const height = Math.min(
      defaultHeight,
      Math.max(minHeight, viewport.height - WINDOW_GAP * 2)
    )

    return (
      <DialogPortal>
        <DialogOverlay />
        <DialogPrimitive.Content
          ref={ref}
          className={cn(
            "fixed z-50 overflow-hidden border bg-background shadow-2xl outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 sm:rounded-lg",
            className
          )}
          style={
            isMaximized
              ? {
                  inset: WINDOW_GAP,
                  width: "auto",
                  height: "auto",
                  transform: "none",
                }
              : {
                  left: position.x,
                  top: position.y,
                  width,
                  height,
                  minWidth,
                  minHeight,
                  maxWidth: `calc(100vw - ${WINDOW_GAP * 2}px)`,
                  maxHeight: `calc(100vh - ${WINDOW_GAP * 2}px)`,
                  transform: "none",
                }
          }
          {...props}
        >
          <div className="flex h-full min-h-0 flex-col">
            <div
              className="flex cursor-move items-center justify-between border-b px-4 py-3"
              onPointerDown={onHeaderPointerDown}
              onPointerMove={onHeaderPointerMove}
              onPointerUp={onHeaderPointerUp}
              onPointerCancel={onHeaderPointerUp}
            >
              <div className="truncate pr-3 font-medium">{title ?? "窗口"}</div>
              <div
                className="flex items-center gap-1"
                onPointerDown={(event) => event.stopPropagation()}
              >
                <button
                  type="button"
                  className="inline-flex h-8 w-8 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
                  onClick={() => setIsMaximized((prev) => !prev)}
                  aria-label={isMaximized ? "还原" : "最大化"}
                >
                  {isMaximized ? <Minimize2 className="h-4 w-4" /> : <Maximize2 className="h-4 w-4" />}
                </button>
                <DialogPrimitive.Close className="inline-flex h-8 w-8 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive">
                  <X className="h-4 w-4" />
                  <span className="sr-only">Close</span>
                </DialogPrimitive.Close>
              </div>
            </div>
            {children}
          </div>
        </DialogPrimitive.Content>
      </DialogPortal>
    )
  }
)
DialogContent.displayName = "DialogContent"

const DialogHeader = ({
  className,
  ...props
}: React.HTMLAttributes<HTMLDivElement>) => (
  <div className={cn("border-b px-4 py-3", className)} {...props} />
)
DialogHeader.displayName = "DialogHeader"

const DialogBody = ({
  className,
  ...props
}: React.HTMLAttributes<HTMLDivElement>) => (
  <div className={cn("min-h-0 flex-1 overflow-auto px-4 py-3", className)} {...props} />
)
DialogBody.displayName = "DialogBody"

const DialogFooter = ({
  className,
  ...props
}: React.HTMLAttributes<HTMLDivElement>) => (
  <div
    className={cn(
      "shrink-0 border-t bg-background/95 px-4 py-3 backdrop-blur supports-[backdrop-filter]:bg-background/70",
      className
    )}
    {...props}
  />
)
DialogFooter.displayName = "DialogFooter"

export {
  Dialog as Dialog,
  DialogTrigger as DialogTrigger,
  DialogClose as DialogClose,
  DialogContent,
  DialogHeader,
  DialogBody,
  DialogFooter,
}
