import { Button } from '@cherrystudio/ui/components/primitives/button'
import { cn } from '@cherrystudio/ui/lib/utils'
import { AnimatePresence, motion } from 'framer-motion'
import { XIcon } from 'lucide-react'
import * as React from 'react'
import { useCallback, useEffect, useId, useLayoutEffect, useRef, useState } from 'react'
import { createPortal } from 'react-dom'

import Scrollbar from '../scrollbar'

/**
 * A page-owned floating side panel. It portals to `document.body` so the fixed
 * panel and viewport backdrop are not clipped or re-based by page layout,
 * transformed ancestors, virtualized lists, or scroll containers.
 *
 * For edge-attached modal sheets, use the shadcn `Drawer` primitive instead.
 */
type PageSidePanelPlacement = 'left' | 'right'
const PAGE_SIDE_PANEL_ROOT_SELECTOR = '[data-page-side-panel-root="true"]'

function resolvePortalContainer() {
  if (typeof document === 'undefined') return null
  return document.querySelector<HTMLElement>(PAGE_SIDE_PANEL_ROOT_SELECTOR) ?? document.body
}

interface PageSidePanelProps {
  open: boolean
  onClose: () => void
  children?: React.ReactNode
  title?: React.ReactNode
  header?: React.ReactNode
  footer?: React.ReactNode
  side?: PageSidePanelPlacement
  showCloseButton?: boolean
  closeLabel?: string
  backdropClassName?: string
  contentClassName?: string
  headerClassName?: string
  bodyClassName?: string
  footerClassName?: string
  closeButtonClassName?: string
}

function PageSidePanel({
  open,
  onClose,
  children,
  title,
  header,
  footer,
  side = 'right',
  showCloseButton = true,
  closeLabel = 'Close',
  backdropClassName,
  contentClassName,
  headerClassName,
  bodyClassName,
  footerClassName,
  closeButtonClassName
}: PageSidePanelProps) {
  const standardTitle = title ? <span className="font-semibold text-base text-foreground">{title}</span> : null
  const headerContent = header ?? standardTitle
  const hasHeader = !!headerContent || showCloseButton
  const headerId = useId()
  const panelRef = useRef<HTMLDivElement>(null)
  const triggerRef = useRef<HTMLElement | null>(null)
  const closedByPointerDownRef = useRef(false)
  const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(resolvePortalContainer)
  const isScopedPortal =
    typeof document !== 'undefined' && portalContainer !== null && portalContainer !== document.body

  useLayoutEffect(() => {
    const nextPortalContainer = resolvePortalContainer()
    setPortalContainer((currentPortalContainer) =>
      currentPortalContainer === nextPortalContainer ? currentPortalContainer : nextPortalContainer
    )
  }, [])

  const handleClose = useCallback(
    (event?: React.MouseEvent | React.PointerEvent | React.KeyboardEvent) => {
      event?.preventDefault()
      event?.stopPropagation()
      onClose()
    },
    [onClose]
  )

  useEffect(() => {
    if (open) {
      closedByPointerDownRef.current = false
      triggerRef.current = document.activeElement as HTMLElement | null
      requestAnimationFrame(() => {
        panelRef.current?.focus()
      })
    } else {
      triggerRef.current?.focus()
      triggerRef.current = null
    }
  }, [open])

  const panel = (
    <AnimatePresence>
      {open && (
        <>
          <motion.div
            key="backdrop"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            transition={{ duration: 0.15 }}
            data-slot="page-side-panel-backdrop"
            className={cn(isScopedPortal ? 'absolute inset-0' : 'fixed inset-0', 'z-60 bg-black/50', backdropClassName)}
            onClick={handleClose}
          />
          <motion.aside
            ref={panelRef}
            key="panel"
            role="dialog"
            aria-modal="true"
            aria-labelledby={headerContent ? headerId : undefined}
            tabIndex={-1}
            onKeyDown={(e) => {
              if (e.key === 'Escape') handleClose(e)
            }}
            initial={{ x: side === 'right' ? '100%' : '-100%' }}
            animate={{ x: 0 }}
            exit={{ x: side === 'right' ? '100%' : '-100%' }}
            transition={{ type: 'spring', damping: 30, stiffness: 350 }}
            data-slot="page-side-panel"
            className={cn(
              isScopedPortal ? 'absolute' : 'fixed',
              'top-3 bottom-3 z-70 flex w-100 flex-col overflow-hidden rounded-3xl bg-card text-card-foreground shadow-xl outline-none',
              side === 'right' ? 'right-3' : 'left-3',
              contentClassName
            )}>
            {hasHeader && (
              <div
                data-slot="page-side-panel-header"
                className={cn('flex shrink-0 items-center justify-between px-6 pt-6 pb-3', headerClassName)}>
                <div id={headerContent ? headerId : undefined} className="min-w-0 flex flex-1 items-center">
                  {headerContent}
                </div>
                {showCloseButton && (
                  <Button
                    type="button"
                    variant="ghost"
                    size="icon-sm"
                    onPointerDown={(event) => {
                      closedByPointerDownRef.current = true
                      handleClose(event)
                    }}
                    onClick={(event) => {
                      if (closedByPointerDownRef.current) {
                        closedByPointerDownRef.current = false
                        event.preventDefault()
                        event.stopPropagation()
                        return
                      }
                      handleClose(event)
                    }}
                    aria-label={closeLabel}
                    data-slot="page-side-panel-close"
                    className={cn(
                      'ml-3 shrink-0 rounded-md opacity-70 shadow-none transition-opacity hover:bg-transparent hover:opacity-100',
                      closeButtonClassName
                    )}>
                    <XIcon size={16} />
                  </Button>
                )}
              </div>
            )}

            <Scrollbar
              data-slot="page-side-panel-body"
              className={cn('min-h-0 flex-1 space-y-4 px-6 py-4', bodyClassName)}>
              {children}
            </Scrollbar>

            {footer && (
              <div
                data-slot="page-side-panel-footer"
                className={cn('shrink-0 space-y-2.5 px-6 pt-3 pb-6', footerClassName)}>
                {footer}
              </div>
            )}
          </motion.aside>
        </>
      )}
    </AnimatePresence>
  )

  return portalContainer ? createPortal(panel, portalContainer) : panel
}

interface PageSidePanelSectionProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
  title: React.ReactNode
  actions?: React.ReactNode
  children: React.ReactNode
}

function PageSidePanelSection({ title, actions, children, className, ...props }: PageSidePanelSectionProps) {
  return (
    <div className={cn('flex flex-col gap-3', className)} {...props}>
      <div className="flex items-center justify-between gap-2">
        <span className="font-semibold text-foreground text-sm">{title}</span>
        {actions && <div className="flex shrink-0 items-center gap-1">{actions}</div>}
      </div>
      {children}
    </div>
  )
}

interface PageSidePanelItemProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
  title: React.ReactNode
  description?: React.ReactNode
  action?: React.ReactNode
  children?: React.ReactNode
}

function PageSidePanelItem({ title, description, action, children, className, ...props }: PageSidePanelItemProps) {
  return (
    <div className={cn('flex flex-col gap-2', className)} {...props}>
      <div className="flex items-center justify-between gap-3">
        <div className="flex min-w-0 flex-col gap-0.5">
          <span className="text-foreground text-sm">{title}</span>
          {description && <span className="text-muted-foreground text-xs">{description}</span>}
        </div>
        {action && <div className="shrink-0">{action}</div>}
      </div>
      {children}
    </div>
  )
}

export {
  PageSidePanel,
  PageSidePanelItem,
  type PageSidePanelItemProps,
  type PageSidePanelPlacement,
  type PageSidePanelProps,
  PageSidePanelSection,
  type PageSidePanelSectionProps
}
