import { useCallback, useEffect, useRef, useState } from 'react'
import {
  clipboardSetText,
  fsCreate,
  fsDelete,
  fsListDir,
  fsRename,
  fsWatch,
  onFsChanged,
  relativePath,
  revealInExplorer
} from '../ipc'
import type { FsEntry } from '../types'
import { ConfirmDialog, Modal } from './Modal'
import { IconChevronDown, IconChevronRight, IconFile, IconFolder, IconRefresh } from './Icons'

interface Props {
  projectPath: string
  /** 把文本填入当前终端输入框（无可见终端时为 null） */
  onInsertPath: ((text: string) => void) | null
  /** 单击文件打开预览 */
  onPreviewFile: (path: string) => void
}

interface MenuState {
  x: number
  y: number
  entry: FsEntry | null // null = 树空白处（项目根目录）
}

interface NamingState {
  kind: 'new-file' | 'new-dir' | 'rename'
  /** new-* 时为父目录，rename 时为目标条目路径 */
  path: string
  initial: string
}

/** IDE 式文件树：按需逐层加载，变更自动刷新，Ctrl 多选，右键文件操作 */
export function FileTree({ projectPath, onInsertPath, onPreviewFile }: Props) {
  const [children, setChildren] = useState<Record<string, FsEntry[]>>({})
  const [expanded, setExpanded] = useState<Set<string>>(new Set())
  const [selected, setSelected] = useState<Set<string>>(new Set())
  const [menu, setMenu] = useState<MenuState | null>(null)
  const [naming, setNaming] = useState<NamingState | null>(null)
  const [nameText, setNameText] = useState('')
  const [deleting, setDeleting] = useState<string[] | null>(null)
  const [copied, setCopied] = useState('')
  const expandedRef = useRef(expanded)
  expandedRef.current = expanded

  const loadDir = useCallback(async (path: string) => {
    try {
      const entries = await fsListDir(path)
      setChildren((c) => ({ ...c, [path]: entries }))
    } catch {
      setChildren((c) => ({ ...c, [path]: [] }))
    }
  }, [])

  const refresh = useCallback(() => {
    void loadDir(projectPath)
    for (const path of expandedRef.current) void loadDir(path)
  }, [projectPath, loadDir])

  // 项目切换时重置、加载根目录并启动文件监听
  useEffect(() => {
    setChildren({})
    setExpanded(new Set())
    setSelected(new Set())
    void loadDir(projectPath)
    fsWatch(projectPath).catch(() => {
      // 监听失败不影响手动刷新
    })
  }, [projectPath, loadDir])

  // 文件系统变更（去抖后）自动刷新已展开目录
  useEffect(() => {
    let unlisten: (() => void) | null = null
    void onFsChanged(refresh).then((fn) => {
      unlisten = fn
    })
    return () => unlisten?.()
  }, [refresh])

  const toggleDir = (path: string) => {
    setExpanded((prev) => {
      const next = new Set(prev)
      if (next.has(path)) {
        next.delete(path)
      } else {
        next.add(path)
        if (!children[path]) void loadDir(path)
      }
      return next
    })
  }

  const copy = async (text: string) => {
    await clipboardSetText(text)
    setCopied(text)
    setTimeout(() => setCopied(''), 1500)
    setMenu(null)
  }

  /** 右键目标 + 已多选的并集（右键条目不在选区时只作用于它自己） */
  const menuTargets = (entry: FsEntry): string[] =>
    selected.has(entry.path) && selected.size > 1 ? [...selected] : [entry.path]

  const quote = (paths: string[]) => paths.map((p) => `"${p}"`).join(' ')

  const submitNaming = async () => {
    if (!naming || !nameText.trim()) return
    try {
      if (naming.kind === 'rename') {
        await fsRename(naming.path, nameText)
      } else {
        await fsCreate(`${naming.path}\\${nameText.trim()}`, naming.kind === 'new-dir')
      }
      setNaming(null)
      refresh()
    } catch (e) {
      alert(String(e))
    }
  }

  const renderEntries = (path: string, depth: number) => {
    const entries = children[path]
    if (!entries) {
      return (
        <div className="tree-loading" style={{ paddingLeft: 14 + depth * 14 }}>
          加载中…
        </div>
      )
    }
    return entries.map((entry) => (
      <div key={entry.path}>
        <div
          className={`tree-item ${selected.has(entry.path) ? 'selected' : ''}`}
          style={{ paddingLeft: 6 + depth * 14 }}
          title={entry.path}
          onClick={(e) => {
            if (e.ctrlKey) {
              // Ctrl+点击：切换多选
              setSelected((prev) => {
                const next = new Set(prev)
                if (next.has(entry.path)) next.delete(entry.path)
                else next.add(entry.path)
                return next
              })
              return
            }
            setSelected(new Set())
            if (entry.isDir) toggleDir(entry.path)
            else onPreviewFile(entry.path)
          }}
          onDoubleClick={() => {
            if (!entry.isDir) onInsertPath?.(`"${entry.path}"`)
          }}
          onContextMenu={(e) => {
            e.preventDefault()
            e.stopPropagation()
            setMenu({ x: e.clientX, y: e.clientY, entry })
          }}
        >
          {entry.isDir ? (
            expanded.has(entry.path) ? (
              <IconChevronDown width={11} height={11} style={{ flexShrink: 0 }} />
            ) : (
              <IconChevronRight width={11} height={11} style={{ flexShrink: 0 }} />
            )
          ) : (
            <span style={{ width: 11, flexShrink: 0 }} />
          )}
          {entry.isDir ? (
            <IconFolder style={{ flexShrink: 0, color: 'var(--text-dim)' }} />
          ) : (
            <IconFile style={{ flexShrink: 0, color: 'var(--text-faint)' }} />
          )}
          <span className="tree-name">{entry.name}</span>
        </div>
        {entry.isDir && expanded.has(entry.path) && renderEntries(entry.path, depth + 1)}
      </div>
    ))
  }

  return (
    <div className="file-tree">
      <div className="tree-toolbar">
        <span className="tree-root" title={projectPath}>
          {projectPath.split('\\').pop()}
        </span>
        {selected.size > 1 && <span className="badge accent">{selected.size} 项</span>}
        <button className="btn ghost small" title="刷新" onClick={refresh}>
          <IconRefresh width={12} height={12} />
        </button>
      </div>
      <div
        className="tree-body"
        onContextMenu={(e) => {
          // 空白处右键：根目录新建
          e.preventDefault()
          setMenu({ x: e.clientX, y: e.clientY, entry: null })
        }}
      >
        {renderEntries(projectPath, 0)}
      </div>
      {copied && <div className="tree-copied">已复制</div>}

      {menu && (
        <>
          <div className="ctx-backdrop" onMouseDown={() => setMenu(null)} onContextMenu={(e) => e.preventDefault()} />
          <div className="ctx-menu" style={{ left: menu.x, top: menu.y }}>
            {menu.entry ? (
              <>
                {menuTargets(menu.entry).length > 1 ? (
                  <>
                    {onInsertPath && (
                      <div
                        className="ctx-item"
                        onClick={() => {
                          onInsertPath(quote(menuTargets(menu.entry!)))
                          setMenu(null)
                        }}
                      >
                        在终端中引用 {menuTargets(menu.entry).length} 项
                      </div>
                    )}
                    <div className="ctx-item" onClick={() => void copy(menuTargets(menu.entry!).join('\n'))}>
                      复制 {menuTargets(menu.entry).length} 个绝对路径
                    </div>
                    <div
                      className="ctx-item"
                      onClick={() => {
                        setDeleting(menuTargets(menu.entry!))
                        setMenu(null)
                      }}
                    >
                      删除 {menuTargets(menu.entry).length} 项（回收站）
                    </div>
                  </>
                ) : (
                  <>
                    <div className="ctx-item" onClick={() => void copy(menu.entry!.path)}>
                      复制绝对路径
                    </div>
                    <div
                      className="ctx-item"
                      onClick={async () => {
                        const rel = await relativePath(projectPath, menu.entry!.path)
                        void copy(rel)
                      }}
                    >
                      复制相对路径
                    </div>
                    <div
                      className="ctx-item"
                      onClick={() => {
                        void revealInExplorer(menu.entry!.path)
                        setMenu(null)
                      }}
                    >
                      打开所在目录
                    </div>
                    {onInsertPath && (
                      <div
                        className="ctx-item"
                        onClick={() => {
                          onInsertPath(`"${menu.entry!.path}"`)
                          setMenu(null)
                        }}
                      >
                        在终端中引用
                      </div>
                    )}
                    <div className="ctx-sep" />
                    {menu.entry.isDir && (
                      <>
                        <div
                          className="ctx-item"
                          onClick={() => {
                            setNaming({ kind: 'new-file', path: menu.entry!.path, initial: '' })
                            setNameText('')
                            setMenu(null)
                          }}
                        >
                          新建文件
                        </div>
                        <div
                          className="ctx-item"
                          onClick={() => {
                            setNaming({ kind: 'new-dir', path: menu.entry!.path, initial: '' })
                            setNameText('')
                            setMenu(null)
                          }}
                        >
                          新建文件夹
                        </div>
                      </>
                    )}
                    <div
                      className="ctx-item"
                      onClick={() => {
                        setNaming({ kind: 'rename', path: menu.entry!.path, initial: menu.entry!.name })
                        setNameText(menu.entry!.name)
                        setMenu(null)
                      }}
                    >
                      重命名
                    </div>
                    <div
                      className="ctx-item"
                      onClick={() => {
                        setDeleting([menu.entry!.path])
                        setMenu(null)
                      }}
                    >
                      删除（回收站）
                    </div>
                  </>
                )}
              </>
            ) : (
              <>
                <div
                  className="ctx-item"
                  onClick={() => {
                    setNaming({ kind: 'new-file', path: projectPath, initial: '' })
                    setNameText('')
                    setMenu(null)
                  }}
                >
                  新建文件
                </div>
                <div
                  className="ctx-item"
                  onClick={() => {
                    setNaming({ kind: 'new-dir', path: projectPath, initial: '' })
                    setNameText('')
                    setMenu(null)
                  }}
                >
                  新建文件夹
                </div>
              </>
            )}
          </div>
        </>
      )}

      {naming && (
        <Modal
          title={
            naming.kind === 'rename' ? '重命名' : naming.kind === 'new-dir' ? '新建文件夹' : '新建文件'
          }
          onClose={() => setNaming(null)}
          footer={
            <>
              <button className="btn" onClick={() => setNaming(null)}>
                取消
              </button>
              <button className="btn primary" disabled={!nameText.trim()} onClick={() => void submitNaming()}>
                确定
              </button>
            </>
          }
        >
          <input
            className="input"
            style={{ width: '100%' }}
            value={nameText}
            autoFocus
            onChange={(e) => setNameText(e.target.value)}
            onKeyDown={(e) => {
              if (e.key === 'Enter') void submitNaming()
            }}
          />
        </Modal>
      )}

      {deleting && (
        <ConfirmDialog
          title="删除"
          message={`将以下 ${deleting.length} 项移入回收站：\n${deleting.slice(0, 8).join('\n')}${deleting.length > 8 ? '\n…' : ''}`}
          confirmText="移入回收站"
          danger
          onCancel={() => setDeleting(null)}
          onConfirm={async () => {
            for (const path of deleting) {
              try {
                await fsDelete(path)
              } catch (e) {
                alert(String(e))
                break
              }
            }
            setDeleting(null)
            setSelected(new Set())
            refresh()
          }}
        />
      )}
    </div>
  )
}
