import type { CSSProperties, DragEvent as ReactDragEvent, MouseEvent as ReactMouseEvent, ReactNode } from "react";
import { Copy, Download, FolderOpen, Home, MoveRight, Plus, Trash2, UploadCloud } from "lucide-react";
import { Button, Dialog, ExplorerTree, type ExplorerTreeNodeContext } from "@govoplan/core-webui";
import type { ConflictAction, FileSpace, RenameResponse } from "../../../api/files";
import type { ConflictDialogState, FileActionTarget, FileConflictItem, FolderNode, ContextMenuState } from "../types";
import { isPathUnderOrSame, normalizeFolder, treeNodeKey } from "../utils/fileManagerUtils";

export function TransferFolderSelector({
  space,
  nodes,
  selectedFolder,
  onSelect,
  disabled
}: {
  space: FileSpace | null;
  nodes: FolderNode[];
  selectedFolder: string;
  onSelect: (folderPath: string) => void;
  disabled?: boolean;
}) {
  return (
    <div className="file-folder-selector" role="tree" aria-label="Destination folder">
      <button
        type="button"
        className={`file-folder-selector-node ${selectedFolder === "" ? "is-selected" : ""}`}
        onClick={() => onSelect("")}
        disabled={disabled || !space}
      >
        <Home size={15} aria-hidden="true" />
        <span>{space?.label || "Root"}</span>
      </button>
      {nodes.length === 0 && <span className="muted small-text file-folder-selector-empty">No folders yet. Choose the root folder.</span>}
      <TransferFolderSelectorNodes nodes={nodes} selectedFolder={selectedFolder} onSelect={onSelect} disabled={disabled} />
    </div>
  );
}

export function TransferFolderSelectorNodes({
  nodes,
  selectedFolder,
  onSelect,
  disabled,
  depth = 1
}: {
  nodes: FolderNode[];
  selectedFolder: string;
  onSelect: (folderPath: string) => void;
  disabled?: boolean;
  depth?: number;
}) {
  if (nodes.length === 0) return null;
  return (
    <div className="file-folder-selector-children">
      {nodes.map((node) => (
        <div key={node.path}>
          <button
            type="button"
            className={`file-folder-selector-node ${selectedFolder === node.path ? "is-selected" : ""}`}
            style={{ paddingLeft: `${Math.min(depth * 16 + 10, 74)}px` }}
            onClick={() => onSelect(node.path)}
            disabled={disabled}
          >
            <FolderOpen size={15} aria-hidden="true" />
            <span>{node.name}</span>
          </button>
          <TransferFolderSelectorNodes nodes={node.children} selectedFolder={selectedFolder} onSelect={onSelect} disabled={disabled} depth={depth + 1} />
        </div>
      ))}
    </div>
  );
}

export function FolderTree({
  nodes,
  activeSpaceId,
  spaceId,
  currentFolder,
  dropTargetKey = "",
  expandedKeys,
  onOpen,
  onToggle,
  onContextMenu,
  onDragOverTarget,
  onDropOnTarget,
  onClearDropState,
  onRequestDragExpand,
  onDragStartFolder,
  onDragEndFolder,
  dragDropEnabled = true,
  contextMenuEnabled = true,
  disabled,
  depth = 1
}: {
  nodes: FolderNode[];
  activeSpaceId: string;
  spaceId: string;
  currentFolder: string;
  dropTargetKey?: string;
  expandedKeys: Set<string>;
  onOpen: (spaceId: string, path: string) => void;
  onToggle: (spaceId: string, path: string) => void;
  onContextMenu?: (event: ReactMouseEvent<HTMLElement>, spaceId: string, path: string) => void;
  onDragOverTarget?: (event: ReactDragEvent<HTMLElement>, target: FileActionTarget) => void;
  onDropOnTarget?: (event: ReactDragEvent<HTMLElement>, target: FileActionTarget) => Promise<void>;
  onClearDropState?: () => void;
  onRequestDragExpand?: (spaceId: string, path: string) => void;
  onDragStartFolder?: (spaceId: string, path: string, event: ReactDragEvent<HTMLElement>) => void;
  onDragEndFolder?: () => void;
  dragDropEnabled?: boolean;
  contextMenuEnabled?: boolean;
  disabled?: boolean;
  depth?: number;
}) {
  return (
    <ExplorerTree
      nodes={nodes}
      getNodeId={(node) => treeNodeKey(spaceId, node.path)}
      getNodeLabel={(node) => node.name}
      getNodeChildren={(node) => node.children}
      activeId={activeSpaceId === spaceId ? treeNodeKey(spaceId, currentFolder) : ""}
      expandedIds={expandedKeys}
      onOpen={(node) => onOpen(spaceId, node.path)}
      onToggle={(node) => onToggle(spaceId, node.path)}
      disabled={disabled}
      depth={depth}
      getNodeWrapClassName={(node) => {
        const target = { spaceId, folderPath: node.path };
        return dragDropEnabled && dropTargetKey === `${target.spaceId}:${normalizeFolder(target.folderPath)}` ? "is-drop-target" : undefined;
      }}
      getNodeWrapStyle={(_node, context: ExplorerTreeNodeContext) => ({ paddingLeft: `${Math.min(context.depth * 14, 56)}px` })}
      getNodeDraggable={() => dragDropEnabled && !disabled}
      onContextMenu={contextMenuEnabled && onContextMenu ? (event, node) => onContextMenu(event, spaceId, node.path) : undefined}
      onDragStart={dragDropEnabled && onDragStartFolder ? (event, node) => onDragStartFolder(spaceId, node.path, event) : undefined}
      onDragEnd={dragDropEnabled && onDragEndFolder ? () => onDragEndFolder() : undefined}
      onDragOver={dragDropEnabled && onDragOverTarget ? (event, node, context) => {
        const target = { spaceId, folderPath: node.path };
        onDragOverTarget(event, target);
        if (context.hasChildren && !context.expanded) onRequestDragExpand?.(spaceId, node.path);
      } : undefined}
      onDragLeave={dragDropEnabled && onClearDropState ? () => onClearDropState() : undefined}
      onDrop={dragDropEnabled && onDropOnTarget ? (event, node) => void onDropOnTarget(event, { spaceId, folderPath: node.path }) : undefined}
    />
  );
}

export function RenamePreviewList({
  response,
  selectedFileIds,
  selectedFolderPaths,
  recursive,
  visibleCount,
  onShowMore,
  onShowFewer
}: {
  response: RenameResponse;
  selectedFileIds: Set<string>;
  selectedFolderPaths: Set<string>;
  recursive: boolean;
  visibleCount: number;
  onShowMore: () => void;
  onShowFewer: () => void;
}) {
  const selectedFolderRoots = Array.from(selectedFolderPaths).map(normalizeFolder);
  const hiddenNonRecursiveItems = !recursive
    ? response.items.filter((item) => {
        if (item.kind === "file" && selectedFileIds.has(item.id)) return false;
        if (item.kind === "folder" && selectedFolderRoots.includes(normalizeFolder(item.old_path))) return false;
        return selectedFolderRoots.some((folder) => isPathUnderOrSame(item.old_path, folder));
      }).length
    : 0;
  const visibleItems = response.items.filter((item) => {
    if (recursive) return true;
    if (item.kind === "file") {
      if (selectedFileIds.has(item.id)) return true;
      return !selectedFolderRoots.some((folder) => isPathUnderOrSame(item.old_path, folder));
    }
    return selectedFolderRoots.includes(normalizeFolder(item.old_path));
  });
  const safeVisibleCount = Math.max(20, visibleCount);
  const shownItems = visibleItems.slice(0, safeVisibleCount);
  const remaining = visibleItems.length - shownItems.length;
  return (
    <div className="rename-preview-panel">
      <div className="placeholder-stack rename-preview-list">
        {hiddenNonRecursiveItems > 0 && <span className="muted">{hiddenNonRecursiveItems} contained path(s) will move with the selected folder(s), but their own names will stay unchanged.</span>}
        {shownItems.map((item) => <span key={`${item.kind}:${item.id}:${item.old_path}`}><code>{item.old_path}</code> → <code>{item.new_path}</code></span>)}
        {remaining > 0 && (
          <button type="button" className="rename-preview-more" onClick={onShowMore}>… and {remaining} more — show next {Math.min(20, remaining)}</button>
        )}
        {shownItems.length > 20 && (
          <button type="button" className="rename-preview-more" onClick={onShowFewer}>Show fewer</button>
        )}
      </div>
    </div>
  );
}

export function FileDialog({ title, onClose, children }: { title: string; onClose: () => void; children: ReactNode }) {
  return (
    <Dialog
      open
      title={title}
      onClose={onClose}
      backdropClassName="file-dialog-backdrop"
      className="file-dialog"
      headerClassName="file-dialog-header"
      titleClassName="file-dialog-title"
      bodyClassName="file-dialog-body"
    >
      {children}
    </Dialog>
  );
}

export function FileContextMenu({
  menu,
  hasSelection,
  canCreateFolder,
  canUpload,
  canDownload,
  canOrganize,
  canDelete,
  downloadLabel,
  onCreateFolder,
  onUpload,
  onDownload,
  onMove,
  onCopy,
  onDelete
}: {
  menu: ContextMenuState;
  hasSelection: boolean;
  canCreateFolder: boolean;
  canUpload: boolean;
  canDownload: boolean;
  canOrganize: boolean;
  canDelete: boolean;
  downloadLabel: string;
  onCreateFolder: () => void;
  onUpload: () => void;
  onDownload: () => void;
  onMove: () => void;
  onCopy: () => void;
  onDelete: () => void;
}) {
  const showNewFolder = true;
  const showDelete = menu.target !== "empty";
  const viewportWidth = typeof window === "undefined" ? 1024 : window.innerWidth;
  const viewportHeight = typeof window === "undefined" ? 768 : window.innerHeight;
  const estimatedWidth = 220;
  const estimatedHeight = 260;
  const left = Math.max(8, Math.min(menu.x, viewportWidth - estimatedWidth - 8));
  const openUp = menu.y + estimatedHeight > viewportHeight;
  const style: CSSProperties = openUp
    ? { left, bottom: Math.max(8, viewportHeight - menu.y) }
    : { left, top: Math.max(8, menu.y) };
  return (
    <div className="file-context-menu" style={style} role="menu" onClick={(event) => event.stopPropagation()}>
      {showNewFolder && <button type="button" role="menuitem" onClick={onCreateFolder} disabled={!canCreateFolder}><Plus size={15} aria-hidden="true" /> New folder</button>}
      <button type="button" role="menuitem" onClick={onUpload} disabled={!canUpload}><UploadCloud size={15} aria-hidden="true" /> Upload</button>
      <button type="button" role="menuitem" onClick={onDownload} disabled={!hasSelection || !canDownload}><Download size={15} aria-hidden="true" /> {downloadLabel}</button>
      <button type="button" role="menuitem" onClick={onMove} disabled={!hasSelection || !canOrganize}><MoveRight size={15} aria-hidden="true" /> Move…</button>
      <button type="button" role="menuitem" onClick={onCopy} disabled={!hasSelection || !canOrganize}><Copy size={15} aria-hidden="true" /> Copy…</button>
      {showDelete && <button type="button" role="menuitem" className="danger" onClick={onDelete} disabled={!canDelete}><Trash2 size={15} aria-hidden="true" /> Delete</button>}
    </div>
  );
}

export function FileConflictDialog({
  state,
  busy,
  onClose,
  onCancel,
  onOverwrite,
  onRename,
  onReview,
  onApplyReview,
  onUpdateItem
}: {
  state: ConflictDialogState;
  busy: boolean;
  onClose: () => void;
  onCancel: () => void;
  onOverwrite: () => void;
  onRename: () => void;
  onReview: () => void;
  onApplyReview: () => void;
  onUpdateItem: (id: string, patch: Partial<FileConflictItem>) => void;
}) {
  return (
    <FileDialog title={state.title} onClose={onClose}>
      <p className="muted">{state.message}</p>
      <div className="file-conflict-summary">
        <strong>{state.items.length} conflict(s)</strong>
        <span>{state.items.length > 1 ? "Choose the same action for all conflicts or review the target names individually." : "Choose how to resolve this conflict."}</span>
      </div>
      {state.review && (
        <div className="file-conflict-list">
          {state.items.map((item) => (
            <div className="file-conflict-row" key={item.id}>
              <div>
                <strong>{item.label}</strong>
                <small>{item.kind === "folder" ? "Folder" : "File"} conflict at <code>{item.targetPath}</code></small>
              </div>
              <select value={item.action} onChange={(event) => onUpdateItem(item.id, { action: event.target.value as ConflictAction })} disabled={busy}>
                <option value="overwrite">Overwrite</option>
                <option value="rename">Rename</option>
                <option value="skip">Skip</option>
              </select>
              <input
                value={item.newPath}
                onChange={(event) => onUpdateItem(item.id, { newPath: event.target.value })}
                disabled={busy || item.action !== "rename"}
                aria-label={`New path for ${item.label}`}
              />
            </div>
          ))}
        </div>
      )}
      {!state.review && (
        <div className="placeholder-stack">
          {state.items.slice(0, 8).map((item) => <span key={item.id}><code>{item.targetPath}</code></span>)}
          {state.items.length > 8 && <span>… and {state.items.length - 8} more</span>}
        </div>
      )}
      <div className="button-row compact-actions align-end">
        <Button onClick={onCancel} disabled={busy}>Cancel</Button>
        <Button onClick={onOverwrite} disabled={busy}>Overwrite all</Button>
        <Button onClick={onRename} disabled={busy}>Rename all</Button>
        {!state.review && state.items.length > 1 && <Button onClick={onReview} disabled={busy}>Review individually</Button>}
        {state.review && <Button variant="primary" onClick={onApplyReview} disabled={busy}>Apply reviewed choices</Button>}
      </div>
    </FileDialog>
  );
}