import * as React from 'react'

import { cn } from './utils'

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

export interface FileTreeNode {
  id: string
  name: string
  type: 'file' | 'folder'
  icon?: React.ReactNode
  children?: FileTreeNode[]
  expanded?: boolean
  selected?: boolean
  data?: Record<string, string>
}

export interface FileTreeProps extends Omit<React.ComponentProps<'div'>, 'onDrop' | 'onSelect'> {
  data: FileTreeNode[]
  selectable?: boolean
  multiSelect?: boolean
  draggable?: boolean
  showIcons?: boolean
  indent?: number
  onSelect?: (node: FileTreeNode) => void
  onExpand?: (node: FileTreeNode) => void
  onDrop?: (source: FileTreeNode, target: FileTreeNode) => void
}

// ---------------------------------------------------------------------------
// Icons
// ---------------------------------------------------------------------------

function ChevronIcon({ className }: { className?: string }) {
  return (
    <svg viewBox="0 0 16 16" fill="none" className={cn('size-3.5', className)}>
      <path
        d="M6 4l4 4-4 4"
        stroke="currentColor"
        strokeWidth="1.5"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  )
}

function FolderIcon() {
  return (
    <svg viewBox="0 0 16 16" fill="none" className="size-4">
      <path
        d="M1.5 2.5h4.25l1.25 1.5h7.5v9.5h-13z"
        stroke="currentColor"
        strokeWidth="1.2"
        strokeLinejoin="round"
      />
    </svg>
  )
}

function FolderOpenIcon() {
  return (
    <svg viewBox="0 0 16 16" fill="none" className="size-4">
      <path
        d="M1.5 2.5h4.25l1.25 1.5h7.5v2H3.5l-2 6.5z"
        stroke="currentColor"
        strokeWidth="1.2"
        strokeLinejoin="round"
      />
      <path
        d="M3.5 6.5h11l-2 7h-11z"
        stroke="currentColor"
        strokeWidth="1.2"
        strokeLinejoin="round"
      />
    </svg>
  )
}

function FileIcon() {
  return (
    <svg viewBox="0 0 16 16" fill="none" className="size-4">
      <path
        d="M3 1.5h6.5L13 5v9.5H3z"
        stroke="currentColor"
        strokeWidth="1.2"
        strokeLinejoin="round"
      />
      <path d="M9.5 1.5v3.5H13" stroke="currentColor" strokeWidth="1.2" strokeLinejoin="round" />
    </svg>
  )
}

// ---------------------------------------------------------------------------
// Internal item component
// ---------------------------------------------------------------------------

interface FileTreeItemInternalProps {
  node: FileTreeNode
  depth: number
  indent: number
  showIcons: boolean
  selectable: boolean
  multiSelect: boolean
  isDraggable: boolean
  selectedIds: Set<string>
  expandedIds: Set<string>
  onToggle: (node: FileTreeNode) => void
  onSelectNode: (node: FileTreeNode, multi: boolean) => void
  onDrop?: ((source: FileTreeNode, target: FileTreeNode) => void) | undefined
}

function FileTreeItemInternal({
  node,
  depth,
  indent,
  showIcons,
  selectable,
  multiSelect,
  isDraggable,
  selectedIds,
  expandedIds,
  onToggle,
  onSelectNode,
  onDrop,
}: FileTreeItemInternalProps) {
  const isFolder = node.type === 'folder'
  const isExpanded = expandedIds.has(node.id)
  const isSelected = selectedIds.has(node.id)

  const handleClick = (e: React.MouseEvent) => {
    if (isFolder) {
      onToggle(node)
    }
    if (selectable) {
      const isMulti = multiSelect && (e.ctrlKey || e.metaKey)
      onSelectNode(node, isMulti)
    }
  }

  const handleDragStart = (e: React.DragEvent) => {
    e.dataTransfer.effectAllowed = 'move'
    e.dataTransfer.setData('text/plain', node.id)
    e.dataTransfer.setData('application/json', JSON.stringify(node))
  }

  const handleDragOver = (e: React.DragEvent) => {
    if (isFolder) {
      e.preventDefault()
      e.dataTransfer.dropEffect = 'move'
    }
  }

  const handleDropEvent = (e: React.DragEvent) => {
    e.preventDefault()
    if (isFolder && onDrop) {
      try {
        const sourceData = e.dataTransfer.getData('application/json')
        const source = JSON.parse(sourceData) as FileTreeNode
        if (source.id !== node.id) {
          onDrop(source, node)
        }
      } catch {
        // ignore invalid drag data
      }
    }
  }

  return (
    <li role="treeitem" aria-expanded={isFolder ? isExpanded : undefined}>
      <div
        className={cn(
          'flex cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-sm transition-colors',
          'hover:bg-surface-secondary',
          isSelected && 'bg-surface-secondary text-foreground',
          !isSelected && 'text-foreground-secondary'
        )}
        style={{ paddingLeft: `${depth * indent + 8}px` }}
        onClick={handleClick}
        draggable={isDraggable}
        onDragStart={isDraggable ? handleDragStart : undefined}
        onDragOver={isDraggable ? handleDragOver : undefined}
        onDrop={isDraggable ? handleDropEvent : undefined}
      >
        {/* Chevron */}
        <span className={cn('shrink-0', !isFolder && 'invisible')}>
          <ChevronIcon
            className={cn('transition-transform duration-150', isExpanded && 'rotate-90')}
          />
        </span>

        {/* Icon */}
        {showIcons && (
          <span className="text-foreground-secondary shrink-0">
            {node.icon ? (
              node.icon
            ) : isFolder ? (
              isExpanded ? (
                <FolderOpenIcon />
              ) : (
                <FolderIcon />
              )
            ) : (
              <FileIcon />
            )}
          </span>
        )}

        {/* Label */}
        <span className="truncate">{node.name}</span>
      </div>

      {/* Children */}
      {isFolder && isExpanded && node.children && node.children.length > 0 && (
        <ul role="group">
          {node.children.map(child => (
            <FileTreeItemInternal
              key={child.id}
              node={child}
              depth={depth + 1}
              indent={indent}
              showIcons={showIcons}
              selectable={selectable}
              multiSelect={multiSelect}
              isDraggable={isDraggable}
              selectedIds={selectedIds}
              expandedIds={expandedIds}
              onToggle={onToggle}
              onSelectNode={onSelectNode}
              onDrop={onDrop}
            />
          ))}
        </ul>
      )}
    </li>
  )
}

// ---------------------------------------------------------------------------
// Helper: collect expanded IDs from data
// ---------------------------------------------------------------------------

function collectExpandedIds(nodes: FileTreeNode[], set: Set<string>): void {
  for (const node of nodes) {
    if (node.type === 'folder' && node.expanded) {
      set.add(node.id)
    }
    if (node.children) {
      collectExpandedIds(node.children, set)
    }
  }
}

function collectSelectedIds(nodes: FileTreeNode[], set: Set<string>): void {
  for (const node of nodes) {
    if (node.selected) {
      set.add(node.id)
    }
    if (node.children) {
      collectSelectedIds(node.children, set)
    }
  }
}

// ---------------------------------------------------------------------------
// FileTree
// ---------------------------------------------------------------------------

function FileTree({
  data,
  selectable = true,
  multiSelect = false,
  draggable = false,
  showIcons = true,
  indent = 16,
  onSelect,
  onExpand,
  onDrop,
  className,
  ...props
}: FileTreeProps) {
  const [expandedIds, setExpandedIds] = React.useState<Set<string>>(() => {
    const set = new Set<string>()
    collectExpandedIds(data, set)
    return set
  })

  const [selectedIds, setSelectedIds] = React.useState<Set<string>>(() => {
    const set = new Set<string>()
    collectSelectedIds(data, set)
    return set
  })

  const handleToggle = React.useCallback(
    (node: FileTreeNode) => {
      setExpandedIds(prev => {
        const next = new Set(prev)
        if (next.has(node.id)) {
          next.delete(node.id)
        } else {
          next.add(node.id)
        }
        return next
      })
      onExpand?.(node)
    },
    [onExpand]
  )

  const handleSelectNode = React.useCallback(
    (node: FileTreeNode, isMulti: boolean) => {
      setSelectedIds(prev => {
        if (isMulti) {
          const next = new Set(prev)
          if (next.has(node.id)) {
            next.delete(node.id)
          } else {
            next.add(node.id)
          }
          return next
        }
        return new Set([node.id])
      })
      onSelect?.(node)
    },
    [onSelect]
  )

  return (
    <div
      data-slot="file-tree"
      className={cn(
        'bg-glass border-border-glass rounded-lg border backdrop-blur-[20px]',
        className
      )}
      {...props}
    >
      {data.length === 0 ? (
        <div className="text-foreground-secondary p-4 text-center text-sm">No files</div>
      ) : (
        <ul role="tree" className="p-1">
          {data.map(node => (
            <FileTreeItemInternal
              key={node.id}
              node={node}
              depth={0}
              indent={indent}
              showIcons={showIcons}
              selectable={selectable}
              multiSelect={multiSelect}
              isDraggable={draggable}
              selectedIds={selectedIds}
              expandedIds={expandedIds}
              onToggle={handleToggle}
              onSelectNode={handleSelectNode}
              onDrop={onDrop}
            />
          ))}
        </ul>
      )}
    </div>
  )
}
FileTree.displayName = 'FileTree'

export { FileTree }
