"use client";

import { AudioLines, BookOpen, ChevronRight, FileImage, FileText, FileVideo, Folder, Paperclip, Plus } from "lucide-react";
import { useMemo, useState } from "react";
import { cn } from "@/lib/utils";
import type { FileTreeRow } from "@/lib/file-tree/types";
import { getFileTreeRowLabel } from "@/lib/file-tree/types";
import { isRealFileAsset } from "@/lib/file-tree/asset";
import { isTextInputTarget } from "@/lib/file-tree/clipboard";

interface FileTreeProps {
  rows: FileTreeRow[];
  activeId: string;
  selectedRowIds: Set<string>;
  onRowClick: (row: FileTreeRow, event: React.MouseEvent) => void;
  onRowDoubleClick: (row: FileTreeRow, event: React.MouseEvent) => void;
  onRowContextMenu: (row: FileTreeRow, event: React.MouseEvent) => void;
  onRowDragStart?: (row: FileTreeRow, event: React.DragEvent) => void;
  onToggleExpand: (docId: string) => void;
  onToggleAssetFolderExpand?: (assetId: string) => void;
  onCreateChild: (parentId: string | null) => void;
  onBlankMouseDown?: (event: React.MouseEvent) => void;
  onDropFiles?: (docId: string, files: FileList, targetRow?: FileTreeRow) => void;
  onInternalDrop?: (args: { targetRow: FileTreeRow; rowIds: string[]; copy: boolean }) => void;
}

const INDENT = 16;

function resolveAssetIconKind(row: Extract<FileTreeRow, { kind: "asset" | "asset-folder" }>) {
  const assetType = String(row.asset.asset_type ?? "").trim().toLowerCase();
  if (assetType === "mindmap") return "mindmap";
  if (assetType === "luckysheet") return "table";

  const mimeType = String(row.asset.mime_type ?? "").trim().toLowerCase();
  const fileName = String(row.asset.file_name ?? "").trim().toLowerCase();
  const ext = fileName.includes(".") ? fileName.split(".").pop() ?? "" : "";

  if (ext === "pdf" || mimeType.includes("pdf")) return "pdf";
  if (ext === "epub" || mimeType.includes("epub")) return "book";
  if (mimeType.startsWith("image/")) return "image";
  if (mimeType.startsWith("video/")) return "video";
  if (mimeType.startsWith("audio/")) return "audio";
  return "file";
}

function renderAssetIcon(row: Extract<FileTreeRow, { kind: "asset" | "asset-folder" }>) {
  const iconKind = resolveAssetIconKind(row);
  const baseClass = "w-4 h-4 shrink-0";

  switch (iconKind) {
    case "mindmap":
      return <Folder className={`${baseClass} text-[#7c3aed]`} />;
    case "table":
      return <FileText className={`${baseClass} text-[#b45309]`} />;
    case "pdf":
      return <FileText className={`${baseClass} text-[#dc2626]`} />;
    case "book":
      return <BookOpen className={`${baseClass} text-[#0f766e]`} />;
    case "image":
      return <FileImage className={`${baseClass} text-[#0891b2]`} />;
    case "video":
      return <FileVideo className={`${baseClass} text-[#ea580c]`} />;
    case "audio":
      return <AudioLines className={`${baseClass} text-[#16a34a]`} />;
    default:
      return <Paperclip className={`${baseClass} text-wolai-text-secondary`} />;
  }
}

export function FileTree({
  rows,
  activeId,
  selectedRowIds,
  onRowClick,
  onRowDoubleClick,
  onRowContextMenu,
  onRowDragStart,
  onToggleExpand,
  onToggleAssetFolderExpand,
  onCreateChild,
  onBlankMouseDown,
  onDropFiles,
  onInternalDrop,
}: FileTreeProps) {
  const [dragOverRowId, setDragOverRowId] = useState<string | null>(null);

  const dragOverRange = useMemo(() => {
    if (!dragOverRowId) return null;
    const startIndex = rows.findIndex((row) => row.rowId === dragOverRowId);
    if (startIndex < 0) return null;

    const target = rows[startIndex];
    const targetDepth = target.depth;

    // VS Code 的树在拖拽悬停到“展开的文件夹”时，会把该节点的可渲染范围都
    // 标记为 drop feedback（包含它的所有可见子节点）。我们用“扁平化 rows +
    // depth”来近似计算该范围。
    let endIndex = startIndex + 1;
    if ((target.kind === "doc" || target.kind === "asset-folder") && target.isExpanded && target.hasChildren) {
      while (endIndex < rows.length && rows[endIndex].depth > targetDepth) {    
        endIndex += 1;
      }
    }

    return { startIndex, endIndex };
  }, [dragOverRowId, rows]);

  if (rows.length === 0) {
    return <div className="px-4 py-6 text-sm text-gray-400">暂无页面，点击下方按钮创建</div>;
  }

  return (
    <div
      className="w-full min-w-0 max-w-full space-y-0.5 overflow-x-hidden"
      onDragOver={(event) => {
        if (!onDropFiles) return;
        if (event.target === event.currentTarget) {
          setDragOverRowId(null);
        }
        const types = Array.from(event.dataTransfer.types ?? []);
        const hasFiles = types.includes("Files") || Boolean(event.dataTransfer.files?.length);
        if (!hasFiles) return;
        event.preventDefault();
        if (event.dataTransfer.files?.length) {
          event.dataTransfer.dropEffect = "copy";
        }
      }}
      onDrop={(event) => {
        if (onDropFiles && event.dataTransfer.files?.length) {
          event.preventDefault();
          setDragOverRowId(null);
          const files = event.dataTransfer.files;
          const activeDocRow = rows.find(
            (row) => row.kind === "doc" && row.docId === activeId,
          );
          const firstDocRow = rows.find((row) => row.kind === "doc");
          const targetDocId = activeDocRow?.docId ?? firstDocRow?.docId ?? "";  
          onDropFiles(targetDocId, files);
        }
      }}
      onDragLeave={(event) => {
        const relatedTarget = (event as unknown as { relatedTarget?: EventTarget | null }).relatedTarget;
        if (relatedTarget && event.currentTarget.contains(relatedTarget as Node)) return;
        setDragOverRowId(null);
      }}
      onMouseDown={(event) => {
        if (event.target === event.currentTarget) {
          onBlankMouseDown?.(event);
        }
      }}
    >
      {rows.map((row, index) => {
        const label = getFileTreeRowLabel(row);
        const selected = selectedRowIds.has(row.rowId);
        // 说明：只有“页面本身（doc/index.md）”才需要 active 高亮。
        // 否则当你打开某个页面时，它下面的思维导图文件夹/附件会出现“灰色假选中”的视觉误导。
        const active = (row.kind === "doc" || row.kind === "index") && row.docId === activeId;
        const draggable =
          row.kind === "doc" || (row.kind === "asset" && isRealFileAsset(row.asset));
        const inDropFeedback =
          dragOverRange &&
          index >= dragOverRange.startIndex &&
          index < dragOverRange.endIndex;
        const baseClass =
          "group flex items-center gap-2 px-3 py-[2px] min-h-[27px] w-full select-none cursor-pointer rounded-[3px] text-wolai-text-primary text-[14px] font-medium hover:bg-wolai-bg-hover transition-colors duration-150 data-[active=true]:bg-wolai-bg-active";
        const activeClass =
          row.kind === "index"
            ? "text-[#2563eb]"
            : row.kind === "doc"
              ? "text-[#2563eb]"
              : "";
        const paddingLeft =
          row.kind === "doc" ? row.depth * INDENT + 8 : row.depth * INDENT + 32;

        return (
          <div
            key={row.rowId}
            data-testid={
              row.kind === "doc"
                ? "filetree-doc-row"
                : row.kind === "index"
                  ? "filetree-index-row"
                  : row.kind === "asset-folder"
                    ? "filetree-asset-folder-row"
                    : "filetree-asset-row"
            }
            data-row-id={row.rowId}
            data-row-kind={row.kind}
            data-doc-id={row.docId}
            className={cn(
              baseClass,
              active && activeClass,
              selected && "bg-wolai-bg-active text-[#2563eb]",
              inDropFeedback && "bg-gray-200/70",
            )}
            data-active={active}
            style={{ paddingLeft }}
            onClick={(event) => onRowClick(row, event)}
            onDoubleClick={(event) => onRowDoubleClick(row, event)}
            onContextMenu={(event) => onRowContextMenu(row, event)}
            onMouseDown={(event) => {
              // 避免 Shift 多选时触发浏览器默认的文本范围选择（VS Code 资源管理器不会出现该行为）
              if (event.button !== 0) return;
              if (!event.shiftKey) return;
              if (isTextInputTarget(event.target)) return;
              event.preventDefault();
            }}
            draggable={draggable}
            onDragStart={(event) => {
              if (!draggable) return;
              if (!selectedRowIds.has(row.rowId)) {
                onRowDragStart?.(row, event);
              }
              const rowIds = selectedRowIds.has(row.rowId) ? Array.from(selectedRowIds) : [row.rowId];
              const payload = JSON.stringify({ type: "mnote-file-tree-dnd", version: 1, rowIds });
              try {
                event.dataTransfer.setData("application/x-mnote-file-tree", payload);
              } catch {
                // ignore
              }
              event.dataTransfer.setData("text/plain", payload);
              event.dataTransfer.effectAllowed = event.altKey ? "copyMove" : "move";
            }}
            onDragEnd={() => {
              setDragOverRowId(null);
            }}
            onDragOver={(event) => {
              const types = Array.from(event.dataTransfer.types ?? []);
              const hasInternal = types.includes("application/x-mnote-file-tree");
              if (hasInternal && onInternalDrop) {
                event.preventDefault();
                event.dataTransfer.dropEffect = event.altKey ? "copy" : "move";
                setDragOverRowId((prev) => (prev === row.rowId ? prev : row.rowId));
                return;
              }
              if (!onDropFiles) return;
              const hasFiles = types.includes("Files") || Boolean(event.dataTransfer.files?.length);
              if (!hasFiles) return;
              event.preventDefault();
              if (event.dataTransfer.files?.length) {
                event.dataTransfer.dropEffect = "copy";
              }
              setDragOverRowId((prev) => (prev === row.rowId ? prev : row.rowId));
            }}
            onDrop={(event) => {
              setDragOverRowId(null);
              const types = Array.from(event.dataTransfer.types ?? []);
              const isInternal = types.includes("application/x-mnote-file-tree");
              if (isInternal && onInternalDrop) {
                const raw =
                  event.dataTransfer.getData("application/x-mnote-file-tree") || "";
                try {
                  const parsed = JSON.parse(raw) as { type?: string; version?: number; rowIds?: unknown };
                  if (parsed?.type === "mnote-file-tree-dnd" && parsed.version === 1 && Array.isArray(parsed.rowIds)) {
                    event.preventDefault();
                    onInternalDrop({
                      targetRow: row,
                      rowIds: parsed.rowIds.filter((id) => typeof id === "string") as string[],
                      copy: event.altKey,
                    });
                    return;
                  }
                } catch {
                  // ignore
                }
              }

              if (onDropFiles && event.dataTransfer.files?.length) {
                event.preventDefault();
                event.stopPropagation();
                onDropFiles(row.docId, event.dataTransfer.files, row);
              }
            }}
            role="button"
            tabIndex={0}
          >
            {row.kind === "doc" ? (
              <>
                {row.hasChildren ? (
                  <button
                    type="button"
                    aria-label="展开或折叠"
                    data-testid="filetree-toggle"
                    onClick={(event) => {
                      event.stopPropagation();
                      onToggleExpand(row.docId);
                    }}
                    className="flex h-5 w-5 items-center justify-center rounded-md hover:bg-gray-100 shrink-0"
                  >
                    <ChevronRight
                      className={cn(
                        "w-4 h-4 text-wolai-text-secondary transition-transform group-hover:text-wolai-text-primary",
                        row.isExpanded && "rotate-90",
                      )}
                    />
                  </button>
                ) : (
                  <span className="w-5 h-5 shrink-0" />
                )}
                <Folder className="w-4 h-4 text-[#2563eb] shrink-0" />
                <span className="min-w-0 flex-1 truncate text-left">{label}</span>
                <button
                  type="button"
                  aria-label="新建子页面"
                  data-testid="filetree-create"
                  onClick={(event) => {
                    event.stopPropagation();
                    onCreateChild(row.docId);
                  }}
                  className="rounded-md p-1 text-gray-400 hover:bg-gray-100 hover:text-gray-700 shrink-0"
                >
                  <Plus className="w-4 h-4" />
                </button>
              </>
            ) : row.kind === "index" ? (
              <>
                <span className="w-4 h-4 shrink-0" />
                <FileText className="w-4 h-4 text-wolai-text-secondary shrink-0" />
                <span className="min-w-0 flex-1 truncate text-left">{label}</span>
              </>
            ) : row.kind === "asset-folder" ? (
              <>
                {row.hasChildren ? (
                  <button
                    type="button"
                    aria-label="展开或折叠"
                    data-testid="filetree-toggle"
                    onClick={(event) => {
                      event.stopPropagation();
                      onToggleAssetFolderExpand?.(row.asset.id);
                    }}
                    className="flex h-5 w-5 items-center justify-center rounded-md hover:bg-gray-100 shrink-0"
                  >
                    <ChevronRight
                      className={cn(
                        "w-4 h-4 text-wolai-text-secondary transition-transform group-hover:text-wolai-text-primary",
                        row.isExpanded && "rotate-90",
                      )}
                    />
                  </button>
                ) : (
                  <span className="w-5 h-5 shrink-0" />
                )}
                {renderAssetIcon(row)}
                <span className="min-w-0 flex-1 truncate text-left">{label}</span>
              </>
            ) : (
              <>
                <span className="w-4 h-4 shrink-0" />
                {renderAssetIcon(row)}
                <span className="min-w-0 flex-1 truncate text-left">{label}</span>
              </>
            )}
          </div>
        );
      })}
    </div>
  );
}
