/**
 * Contextual zoom: when zoomed out past a threshold, nodes render as compact
 * icon-only views using each type's menuIcon from the node registry.
 * @see https://reactflow.dev/examples/interaction/contextual-zoom
 */

import React from 'react'
import { useViewport } from '@xyflow/react'
import { getNodeType, getDefaultStyle } from '@/lib/graph/nodeRegistry'
import { InputHandle, OutputHandle } from '@/components/graph/NodeHandles'
import { cn } from '@/lib/utils'

const MIN_ZOOM = 0.1
const MAX_ZOOM = 2
/** When zoom <= this value, show compact icon view (bottom ~30% of zoom range). */
export const CONTEXTUAL_ZOOM_THRESHOLD = MIN_ZOOM + 0.1 * (MAX_ZOOM - MIN_ZOOM)

/** Handle id per type for compact view (input, output). */
const COMPACT_HANDLES: Record<string, { input?: string; output?: string }> = {
  config: { input: 'ain', output: 'out' },
  render: { input: 'ain' },
  variable: { output: 'out' },
  function: { input: 'in', output: 'out' },
  data: { output: 'out' },
}

type NodeProps = {
  id: string
  type?: string
  data?: Record<string, unknown>
  selected?: boolean
  width?: number
  height?: number
  [key: string]: unknown
}

function CompactNodeView({ id, type, selected, width, height }: NodeProps) {
  const descriptor = type ? getNodeType(type) : undefined
  const icon = descriptor?.menuIcon ?? null
  const handles = type ? COMPACT_HANDLES[type] ?? { output: 'out' } : {}
  const defaultStyle = type ? getDefaultStyle(type) : { width: 320, height: 320 }
  const w = width ?? defaultStyle.width
  const h = height ?? defaultStyle.height

  return (
    <div
      className={cn(
        'flex flex-col rounded-md border bg-card text-card-foreground transition-[border-color,box-shadow] duration-200',
        'border-input hover:ring-1',
        selected &&
        'border-primary/50 shadow-[0_0_0_2px_hsl(var(--primary)_/_0.15)] dark:border-primary/35 dark:shadow-[0_0_0_2px_hsl(var(--primary)_/_0.1)]'
      )}
      style={{
        width: w,
        height: h,
        minWidth: w,
        minHeight: h,
      }}
      data-selected={selected}
    >
      {handles.input && (
        <InputHandle id={handles.input} nodeId={id} />
      )}
      <div className="flex flex-1 flex-col items-center justify-center gap-2 px-3 pb-3 pt-2">
        <span className="flex shrink-0 items-center justify-center [&>svg]:h-14 [&>svg]:w-14" aria-hidden>
          {icon}
        </span>
        <span className="truncate text-center text-lg font-medium text-foreground" title={id}>
          {id}
        </span>
      </div>
      {handles.output && (
        <OutputHandle id={handles.output} />
      )}
    </div>
  )
}

/**
 * Wraps a node component so that when the viewport zoom is at or below
 * CONTEXTUAL_ZOOM_THRESHOLD, the node renders as a compact icon-only view.
 * Inner is always mounted (hidden when compact) so switching zoom does not
 * remount and re-trigger effects (e.g. RenderingNode fetch).
 */
export function createContextualNode<P extends NodeProps>(
  Inner: React.ComponentType<P>
): React.ComponentType<P> {
  function ContextualZoomNode(props: P) {
    const { zoom } = useViewport()
    const showCompact = zoom <= CONTEXTUAL_ZOOM_THRESHOLD
    const p = props as NodeProps
    const type = p.type
    const defaultStyle = type ? getDefaultStyle(type) : { width: 320, height: 320 }
    const w = p.width ?? defaultStyle.width
    const h = p.height ?? defaultStyle.height

    return (
      <div
        style={
          showCompact
            ? { width: w, height: h, minWidth: w, minHeight: h, position: 'relative' }
            : { display: 'contents' }
        }
      >
        {showCompact && (
          <CompactNodeView
            id={p.id}
            type={type}
            data={p.data}
            selected={p.selected}
            width={p.width}
            height={p.height}
          />
        )}
        <div style={{ display: showCompact ? 'none' : undefined }} aria-hidden={showCompact}>
          <Inner {...props} />
        </div>
      </div>
    )
  }

  ContextualZoomNode.displayName = `ContextualZoom(${Inner.displayName ?? Inner.name ?? 'Node'})`
  return ContextualZoomNode as React.ComponentType<P>
}
