'use client'

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useRef,
  useState,
  type ComponentPropsWithoutRef,
  type KeyboardEvent,
  type PointerEvent,
  type ReactNode,
} from 'react'
import { Maximize2, Minimize2 } from 'lucide-react'

export const SECTION_INSPECTOR_WIDTH_KEY = 'actuate:section-inspector-width'
export const SECTION_INSPECTOR_DEFAULT_WIDTH = 320
export const SECTION_INSPECTOR_MIN_WIDTH = 280
export const SECTION_INSPECTOR_MAX_WIDTH = 640
export const SECTION_INSPECTOR_MIN_CANVAS_WIDTH = 96
/** Below this editor-row width the expand action uses an overlay panel. */
export const SECTION_INSPECTOR_OVERLAY_BREAKPOINT = 768
export const SECTION_INSPECTOR_OVERLAY_MAX_WIDTH = 480

function clampWidth(width: number, max = SECTION_INSPECTOR_MAX_WIDTH): number {
  return Math.min(max, Math.max(SECTION_INSPECTOR_MIN_WIDTH, width))
}

/** Max inline width given the surrounding editor row. */
export function computeInspectorMaxWidth(containerWidth: number): number {
  if (!Number.isFinite(containerWidth) || containerWidth <= 0) {
    return SECTION_INSPECTOR_MAX_WIDTH
  }
  const containerCap = Math.max(
    SECTION_INSPECTOR_MIN_WIDTH,
    containerWidth - SECTION_INSPECTOR_MIN_CANVAS_WIDTH,
  )
  return Math.min(SECTION_INSPECTOR_MAX_WIDTH, containerCap)
}

/** Width when expanded as an overlay on tablet/mobile. */
export function computeInspectorOverlayWidth(containerWidth: number): number {
  if (!Number.isFinite(containerWidth) || containerWidth <= 0) {
    return SECTION_INSPECTOR_OVERLAY_MAX_WIDTH
  }
  return Math.min(
    SECTION_INSPECTOR_OVERLAY_MAX_WIDTH,
    Math.max(SECTION_INSPECTOR_MIN_WIDTH, containerWidth - 32),
  )
}

export function readSectionInspectorWidth(): number {
  if (typeof window === 'undefined') return SECTION_INSPECTOR_DEFAULT_WIDTH
  try {
    const raw = localStorage.getItem(SECTION_INSPECTOR_WIDTH_KEY)
    if (!raw) return SECTION_INSPECTOR_DEFAULT_WIDTH
    const parsed = Number.parseInt(raw, 10)
    if (!Number.isFinite(parsed)) return SECTION_INSPECTOR_DEFAULT_WIDTH
    return clampWidth(parsed)
  } catch {
    return SECTION_INSPECTOR_DEFAULT_WIDTH
  }
}

function persistSectionInspectorWidth(width: number, max = SECTION_INSPECTOR_MAX_WIDTH): void {
  try {
    localStorage.setItem(SECTION_INSPECTOR_WIDTH_KEY, String(clampWidth(width, max)))
  } catch {
    // Storage may be unavailable in private mode.
  }
}

interface InspectorResizeContextValue {
  width: number
  maxWidth: number
  isExpanded: boolean
  canExpand: boolean
  toggleExpanded: () => void
}

const InspectorResizeContext = createContext<InspectorResizeContextValue | null>(null)

export function useInspectorResize(): InspectorResizeContextValue | null {
  return useContext(InspectorResizeContext)
}

interface ResizableAsideProps extends ComponentPropsWithoutRef<'aside'> {
  children: ReactNode
}

/**
 * Right-hand editor shell with a draggable left edge. Used by the page/post
 * section inspector and post header panel (default 320px, persisted in
 * localStorage). On tablet/mobile the handle uses a 44px touch target and
 * the header expand button can grow the panel to an overlay width.
 */
export function ResizableAside({ children, className, style, ...props }: ResizableAsideProps) {
  const asideRef = useRef<HTMLElement>(null)
  const [width, setWidth] = useState(SECTION_INSPECTOR_DEFAULT_WIDTH)
  const [containerWidth, setContainerWidth] = useState(0)
  const [isExpanded, setIsExpanded] = useState(false)
  const [dragging, setDragging] = useState(false)
  const widthRef = useRef(width)
  const compactWidthRef = useRef(SECTION_INSPECTOR_DEFAULT_WIDTH)
  const dragRef = useRef<{ startX: number; startWidth: number } | null>(null)

  const inlineMax = computeInspectorMaxWidth(containerWidth)
  const overlayMax = computeInspectorOverlayWidth(containerWidth)
  const useOverlay =
    containerWidth > 0 && containerWidth < SECTION_INSPECTOR_OVERLAY_BREAKPOINT && isExpanded
  const effectiveMax = useOverlay ? overlayMax : inlineMax

  useEffect(() => {
    setWidth(readSectionInspectorWidth())
  }, [])

  useEffect(() => {
    widthRef.current = width
  }, [width])

  useEffect(() => {
    const parent = asideRef.current?.parentElement
    if (!parent) return

    const update = (nextWidth: number) => {
      setContainerWidth(nextWidth)
    }

    update(parent.getBoundingClientRect().width)
    const observer = new ResizeObserver((entries) => {
      const entry = entries[0]
      if (!entry) return
      update(entry.contentRect.width)
    })
    observer.observe(parent)
    return () => observer.disconnect()
  }, [])

  useEffect(() => {
    setWidth((current) => clampWidth(current, effectiveMax))
  }, [effectiveMax])

  const endDrag = useCallback(() => {
    dragRef.current = null
    setDragging(false)
    document.body.style.cursor = ''
    document.body.style.userSelect = ''
    persistSectionInspectorWidth(widthRef.current, inlineMax)
    if (widthRef.current < effectiveMax - 24) {
      setIsExpanded(false)
    }
  }, [effectiveMax, inlineMax])

  const onPointerDown = useCallback((event: PointerEvent<HTMLDivElement>) => {
    if (event.button !== 0) return
    dragRef.current = { startX: event.clientX, startWidth: widthRef.current }
    setDragging(true)
    event.currentTarget.setPointerCapture(event.pointerId)
    document.body.style.cursor = 'col-resize'
    document.body.style.userSelect = 'none'
  }, [])

  const onPointerMove = useCallback(
    (event: PointerEvent<HTMLDivElement>) => {
      if (!dragRef.current) return
      const delta = dragRef.current.startX - event.clientX
      setWidth(clampWidth(dragRef.current.startWidth + delta, effectiveMax))
    },
    [effectiveMax],
  )

  const onPointerUp = useCallback(
    (event: PointerEvent<HTMLDivElement>) => {
      if (!dragRef.current) return
      if (event.currentTarget.hasPointerCapture(event.pointerId)) {
        event.currentTarget.releasePointerCapture(event.pointerId)
      }
      endDrag()
    },
    [endDrag],
  )

  const nudgeWidth = useCallback(
    (delta: number) => {
      setWidth((current) => {
        const next = clampWidth(current + delta, effectiveMax)
        persistSectionInspectorWidth(next, inlineMax)
        if (next < effectiveMax - 24) setIsExpanded(false)
        return next
      })
    },
    [effectiveMax, inlineMax],
  )

  const onKeyDown = useCallback(
    (event: KeyboardEvent<HTMLDivElement>) => {
      if (event.key === 'ArrowLeft') {
        event.preventDefault()
        nudgeWidth(16)
      } else if (event.key === 'ArrowRight') {
        event.preventDefault()
        nudgeWidth(-16)
      } else if (event.key === 'Home') {
        event.preventDefault()
        setIsExpanded(false)
        setWidth(SECTION_INSPECTOR_MIN_WIDTH)
        persistSectionInspectorWidth(SECTION_INSPECTOR_MIN_WIDTH, inlineMax)
      } else if (event.key === 'End') {
        event.preventDefault()
        setWidth(effectiveMax)
        persistSectionInspectorWidth(effectiveMax, inlineMax)
        setIsExpanded(effectiveMax >= inlineMax - 8)
      }
    },
    [effectiveMax, inlineMax, nudgeWidth],
  )

  const toggleExpanded = useCallback(() => {
    if (isExpanded) {
      const restored = clampWidth(compactWidthRef.current, inlineMax)
      setIsExpanded(false)
      setWidth(restored)
      persistSectionInspectorWidth(restored, inlineMax)
      return
    }

    compactWidthRef.current = widthRef.current
    const target =
      containerWidth > 0 && containerWidth < SECTION_INSPECTOR_OVERLAY_BREAKPOINT
        ? overlayMax
        : inlineMax
    setIsExpanded(true)
    setWidth(target)
    persistSectionInspectorWidth(target, inlineMax)
  }, [containerWidth, inlineMax, isExpanded, overlayMax])

  useEffect(() => {
    return () => {
      if (dragRef.current) endDrag()
    }
  }, [endDrag])

  const canExpand =
    !isExpanded &&
    (containerWidth > 0 && containerWidth < SECTION_INSPECTOR_OVERLAY_BREAKPOINT
      ? overlayMax > width + 8
      : inlineMax > width + 8)

  return (
    <InspectorResizeContext.Provider
      value={{ width, maxWidth: effectiveMax, isExpanded, canExpand, toggleExpanded }}
    >
      <aside
        {...props}
        ref={asideRef}
        data-overlay={useOverlay ? '' : undefined}
        className={`relative shrink-0 ${useOverlay ? 'absolute top-0 right-0 bottom-0 z-20 shadow-lg' : ''} ${className ?? ''}`}
        style={{ width, ...style }}
      >
        <div
          role="separator"
          aria-orientation="vertical"
          aria-label="Resize inspector panel"
          aria-valuemin={SECTION_INSPECTOR_MIN_WIDTH}
          aria-valuemax={effectiveMax}
          aria-valuenow={width}
          tabIndex={0}
          data-dragging={dragging ? '' : undefined}
          onPointerDown={onPointerDown}
          onPointerMove={onPointerMove}
          onPointerUp={onPointerUp}
          onPointerCancel={onPointerUp}
          onKeyDown={onKeyDown}
          className="group absolute inset-y-0 -left-5 z-10 flex w-11 -translate-x-1/2 cursor-col-resize touch-none focus-visible:outline-none md:-left-1 md:w-3 md:translate-x-0"
        >
          <div
            className={`mx-auto h-full w-px motion-safe:transition-colors md:mx-0 ${
              dragging
                ? 'bg-primary'
                : 'bg-border group-hover:bg-primary/50 group-focus-visible:bg-primary/50'
            }`}
          />
        </div>
        {children}
      </aside>
    </InspectorResizeContext.Provider>
  )
}

const EXPAND_BUTTON_CLASS =
  'text-muted-foreground hover:text-foreground hover:bg-accent flex h-9 w-9 items-center justify-center rounded-md transition-colors focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none md:h-7 md:w-7'

/** One-tap expand/collapse for tablet and mobile inspector panels. */
export function InspectorExpandButton({ className }: { className?: string }) {
  const ctx = useInspectorResize()
  if (!ctx) return null

  const { isExpanded, canExpand, toggleExpanded } = ctx
  if (!canExpand && !isExpanded) return null

  return (
    <button
      type="button"
      onClick={toggleExpanded}
      aria-label={isExpanded ? 'Collapse inspector panel' : 'Expand inspector panel'}
      aria-pressed={isExpanded}
      className={className ?? EXPAND_BUTTON_CLASS}
    >
      {isExpanded ? (
        <Minimize2 className="h-4 w-4" aria-hidden />
      ) : (
        <Maximize2 className="h-4 w-4" aria-hidden />
      )}
    </button>
  )
}
