import React, { useCallback, useEffect, useMemo, useRef, useId } from 'react';
import type { CSSProperties, ReactNode } from 'react';
import { createPortal } from 'react-dom';
import {
  ChevronIcon,
  TrashIcon,
  EditIcon,
  FolderIcon,
  SidebarCollapseIcon,
  LogoutIcon,
  ChevronDownIcon,
  SettingsIcon,
  CheckIcon,
  PlusIcon,
  FolderPlusIcon,
  RestoreIcon,
  SunIcon,
  MoonIcon,
  DesktopIcon,
  UploadIcon,
  LogoIcon,
  GithubIcon,
  MatrixIcon,
  WorldIcon,
} from '../ui/icons';
import PanelHeader from '../ui/PanelHeader';
import useFloatingMenu from '../ui/useFloatingMenu';

import { getTagColorStyle } from '../utils/colors';
import { useSidebarContext } from './SidebarContext';
import { usePanelManager, usePanelResizeBindings } from '../app/PanelManagerContext';
import type { StatusMessage } from '../hooks/documents/store/useDocumentsStore';

type Identifier = string | number;
type FolderIdentifier = Identifier | 'root';

interface FolderTreeNode {
  id: FolderIdentifier;
  name?: string;
  children: FolderIdentifier[];
  hasChildren?: boolean;
  loaded?: boolean;
  expanded?: boolean;
}

interface FolderNodeProps {
  node: FolderTreeNode;
  depth: number;
  isSelected: boolean;
  onToggle: (folderId: FolderIdentifier) => void;
  onSelect: (folderId: FolderIdentifier) => void;
  onDrop: (event: React.DragEvent<HTMLDivElement>, folderId: FolderIdentifier) => void;
  onDragOver: (event: React.DragEvent<HTMLDivElement>, folderId: FolderIdentifier) => void;
  onDragLeave: (event: React.DragEvent<HTMLDivElement>) => void;
  onDelete: (folderId: FolderIdentifier) => void;
  onRename?: (folderId: FolderIdentifier, name: string) => void;
  renderChildren: (ids: FolderIdentifier[], depth: number) => ReactNode;
  onFolderDragStart?: (event: React.DragEvent<HTMLDivElement>, folderId: FolderIdentifier) => void;
  onFolderDragEnd?: (event: React.DragEvent<HTMLDivElement>) => void;
  draggingFolderId?: FolderIdentifier | null;
}

interface TagEntry {
  id: Identifier;
  label: string;
  color?: string | null;
}

interface CorrespondentEntry {
  id: Identifier;
  name: string;
}

interface TenantOption {
  id?: Identifier | null;
  name?: string | null;
}

type UploadHandler = (
  files: FileList | File[] | Iterable<File>,
  targetFolderId?: FolderIdentifier | null,
) => void;

interface SidebarProps {
  folderNodes: Map<FolderIdentifier, FolderTreeNode>;
  onToggle: (folderId: FolderIdentifier) => void;
  onSelect: (folderId: FolderIdentifier) => void;
  onDrop: (event: React.DragEvent<HTMLDivElement>, folderId: FolderIdentifier) => void;
  onDragOver: (event: React.DragEvent<HTMLDivElement>, folderId: FolderIdentifier) => void;
  onDragLeave: (event: React.DragEvent<HTMLDivElement>) => void;
  onDeleteFolder: (folderId: FolderIdentifier) => void;
  onRenameFolder?: (folderId: FolderIdentifier, name: string) => void;
  selectedFolder?: FolderIdentifier | null;
  onFolderDragStart?: (event: React.DragEvent<HTMLDivElement>, folderId: FolderIdentifier) => void;
  onFolderDragEnd?: (event: React.DragEvent<HTMLDivElement>) => void;
  draggedFolderId?: FolderIdentifier | null;
  onCreateFolder?: () => void;
  creatingFolder?: boolean;
  tags?: TagEntry[];
  untaggedFilterId?: Identifier | null;
  activeTagIds?: Array<Identifier | null | undefined>;
  onToggleTagFilter?: (tagId: Identifier | null | undefined) => void;
  correspondents?: CorrespondentEntry[];
  activeCorrespondentIds?: Array<Identifier | null | undefined>;
  onToggleCorrespondentFilter?: (correspondentId: Identifier | null | undefined) => void;
  onManageTags?: () => void;
  onManageCorrespondents?: () => void;
  onCreateTag?: (label: string) => Promise<void> | void;
  onCreateCorrespondent?: (name: string) => Promise<void> | void;
  searchQuery?: string;
  onSearchChange?: (value: string) => void;
  onSearchSubmit?: () => void;
  onSearchClear?: () => void;
  isFilterActive?: boolean;
  onLogout?: () => void;
  status?: StatusMessage | null;
  tenantName?: string | null;
  tenants?: TenantOption[];
  activeTenantId?: Identifier | null;
  onSelectTenant?: (tenant: TenantOption | null, options?: { refreshOnly?: boolean }) => void;
  onOpenSettings?: () => void;
  onUploadFiles?: UploadHandler;
}

const FolderNode: React.FC<FolderNodeProps> = ({
  node,
  depth,
  isSelected,
  onToggle,
  onSelect,
  onDrop,
  onDragOver,
  onDragLeave,
  onDelete,
  onRename,
  renderChildren,
  onFolderDragStart,
  onFolderDragEnd,
  draggingFolderId,
}) => {
  const isRoot = node.id === 'root';
  const childIds = Array.isArray(node.children) ? node.children : [];
  const hasChildren = Boolean(node.hasChildren || childIds.length);
  const canToggle = hasChildren || !node.loaded;
  const showChevron = canToggle;
  const icon = showChevron ? <ChevronIcon className="toggle-icon" /> : null;
  const canDrag = !isRoot;
  const isDragging = draggingFolderId === node.id;
  const isExpanded = Boolean(node.expanded);
  const rowClasses = ['folder-row'];
  if (isSelected) {
    rowClasses.push('active');
  }

  const handleToggleClick = (event: React.MouseEvent<HTMLSpanElement>) => {
    event.stopPropagation();
    if (canToggle) {
      onToggle(node.id);
    }
  };

  return (
    <li className={`folder-node${isDragging ? ' is-dragging' : ''}`}>
      <div
        className={rowClasses.join(' ')}
        draggable={canDrag}
        onClick={() => onSelect(node.id)}
        onDoubleClick={() => {
          if (canToggle) {
            onToggle(node.id);
          }
        }}
        onDragOver={(event) => onDragOver(event, node.id)}
        onDragLeave={onDragLeave}
        onDrop={(event) => onDrop(event, node.id)}
        onDragStart={(event) => {
          if (!canDrag || !onFolderDragStart) return;
          onFolderDragStart(event, node.id);
        }}
        onDragEnd={(event) => {
          onFolderDragEnd?.(event);
        }}
      >
        <span
          className={`toggle${showChevron ? '' : ' invisible'}${isExpanded ? ' expanded' : ''}`}
          onClick={handleToggleClick}
        >
          {icon}
        </span>
        <span className="name-wrap">
          <FolderIcon className="folder-icon-image" size={16} />
          <span className="name__label" title={node.name}>
            {node.name}
          </span>
        </span>
        {node.id !== 'root' && (
          <div className="folder-row__actions">
            <button
              type="button"
              className="icon-button ghost"
              onClick={(event) => {
                event.stopPropagation();
                if (!onRename) return;
                const nextName = window.prompt('Rename folder', node.name);
                if (!nextName) {
                  return;
                }
                const trimmed = nextName.trim();
                if (!trimmed || trimmed === node.name) {
                  return;
                }
                onRename(node.id, trimmed);
              }}
              title="Rename folder"
              aria-label={`Rename folder ${node.name}`}
            >
              <EditIcon className="icon-edit" size={18} />
            </button>
            <button
              type="button"
              className="icon-button ghost"
              onClick={(event) => {
                event.stopPropagation();
                onDelete(node.id);
              }}
              title="Delete folder"
              aria-label={`Delete folder ${node.name}`}
            >
              <TrashIcon className="icon-trash" size={18} />
            </button>
          </div>
        )}
      </div>
      {isExpanded && childIds.length > 0 && (
        <ul className={`folder-children${depth === 0 ? ' folder-children--level1' : ''}`}>
          {renderChildren(childIds, depth + 1)}
        </ul>
      )}
    </li>
  );
};

const THEME_MODES = ['system', 'light', 'dark'];
const THEME_MODE_LABELS = {
  system: 'System',
  light: 'Light',
  dark: 'Dark',
};

interface FloatingMenuControls {
  isOpen: boolean;
  toggle: () => void;
  close: () => void;
  menuRef: React.RefObject<HTMLDivElement>;
  menuStyle: CSSProperties | null;
  updatePosition: () => void;
}

const Sidebar: React.FC<SidebarProps> = ({
  folderNodes,
  onToggle,
  onSelect,
  onDrop,
  onDragOver,
  onDragLeave,
  onDeleteFolder,
  onRenameFolder,
  selectedFolder = null,
  onFolderDragStart,
  onFolderDragEnd,
  draggedFolderId = null,
  onCreateFolder,
  creatingFolder = false,
  tags = [],
  untaggedFilterId = null,
  activeTagIds = [],
  onToggleTagFilter,
  correspondents = [],
  activeCorrespondentIds = [],
  onToggleCorrespondentFilter,
  onManageTags,
  onManageCorrespondents,
  onCreateTag,
  onCreateCorrespondent,
  searchQuery = '',
  onSearchChange,
  onSearchSubmit,
  onSearchClear,
  isFilterActive = false,
  onLogout,
  status = null,
  tenantName,
  tenants = [],
  activeTenantId = null,
  onSelectTenant,
  onOpenSettings,
  onUploadFiles,
}) => {
  const {
    neutralHue,
    setNeutralHue,
    resetNeutralHue,
    neutralChroma,
    setNeutralChroma,
    resetNeutralChroma,
    neutralContrast,
    setNeutralContrast,
    resetNeutralContrast,
    themeMode,
    cycleThemeMode,
    themeModes,
  } = useSidebarContext();
  const {
    sidebarSuppressed,
    collapseSidebar,
  } = usePanelManager();
  const uploadInputRef = useRef<HTMLInputElement | null>(null);
  const sidebarRef = useRef<HTMLDivElement | null>(null);
  const {
    panelStyle: sidebarStyle,
    handleProps: sidebarHandleProps,
    isPanelResizing: isResizingSidebar,
  } = usePanelResizeBindings('sidebar', { panelRef: sidebarRef });

  const handleCollapse = useCallback(() => {
    collapseSidebar('sidebar');
  }, [collapseSidebar]);
  const neutralHueInputId = useId();
  const neutralChromaInputId = useId();
  const neutralContrastInputId = useId();
  const sortedCorrespondents = useMemo<CorrespondentEntry[]>(() => {
    if (!Array.isArray(correspondents)) {
      return [];
    }
    return correspondents
      .filter((entry): entry is CorrespondentEntry & { name: string } => Boolean(entry?.name))
      .slice()
      .sort((a, b) => a.name!.localeCompare(b.name!, undefined, { sensitivity: 'base' }));
  }, [correspondents]);
  const activeCorrespondentSet = useMemo(
    () => new Set<Identifier | null | undefined>(activeCorrespondentIds || []),
    [activeCorrespondentIds],
  );
  const handleToggleTag = useCallback(
    (tagId: Identifier | null | undefined) => {
      onToggleTagFilter?.(tagId);
    },
    [onToggleTagFilter],
  );
  const activeTagSet = useMemo(
    () => new Set<Identifier | null | undefined>(activeTagIds || []),
    [activeTagIds],
  );
  const untaggedActive = untaggedFilterId ? activeTagSet.has(untaggedFilterId) : false;
  const handleManageTags = useCallback(() => {
    onManageTags?.();
  }, [onManageTags]);
  const handleManageCorrespondents = useCallback(() => {
    onManageCorrespondents?.();
  }, [onManageCorrespondents]);
  const handleCreateTag = useCallback(async () => {
    const input = window.prompt('New tag name');
    if (!input) {
      return;
    }
    const trimmed = input.trim();
    if (!trimmed) {
      return;
    }
    try {
      await onCreateTag?.(trimmed);
    } catch (error: unknown) {
      console.error('[sidebar] failed to create tag', error);
    }
  }, [onCreateTag]);

  const handleCreateCorrespondent = useCallback(async () => {
    const input = window.prompt('New correspondent name');
    if (!input) {
      return;
    }
    const trimmed = input.trim();
    if (!trimmed) {
      return;
    }
    try {
      await onCreateCorrespondent?.(trimmed);
    } catch (error: unknown) {
      console.error('[sidebar] failed to create correspondent', error);
    }
  }, [onCreateCorrespondent]);

  const handleCreateFolder = useCallback(() => {
    if (creatingFolder) {
      return;
    }
    onCreateFolder?.();
  }, [creatingFolder, onCreateFolder]);

  const handleUploadButtonClick = useCallback(() => {
    if (!onUploadFiles || !uploadInputRef.current) {
      return;
    }
    uploadInputRef.current.click();
  }, [onUploadFiles]);

  const handleUploadInputChange = useCallback(
    (event: React.ChangeEvent<HTMLInputElement>) => {
      const files = event.target?.files;
      if (files && files.length && onUploadFiles) {
        onUploadFiles(files, selectedFolder ?? 'root');
      }
      if (event.target) {
        event.target.value = '';
      }
    },
    [onUploadFiles, selectedFolder],
  );

  const handleThemeAdjustmentsReset = useCallback(() => {
    resetNeutralHue();
    resetNeutralChroma();
    resetNeutralContrast();
  }, [resetNeutralHue, resetNeutralChroma, resetNeutralContrast]);

  const handleNeutralHueChange = useCallback(
    (value: string) => {
      if (value === '') {
        resetNeutralHue();
        return;
      }
      const parsed = Number(value);
      if (Number.isNaN(parsed)) {
        return;
      }
      setNeutralHue(parsed);
    },
    [resetNeutralHue, setNeutralHue],
  );

  const handleNeutralChromaChange = useCallback(
    (value: string) => {
      if (value === '') {
        resetNeutralChroma();
        return;
      }
      const parsed = Number(value);
      if (Number.isNaN(parsed)) {
        return;
      }
      setNeutralChroma(parsed);
    },
    [resetNeutralChroma, setNeutralChroma],
  );

  const handleNeutralContrastChange = useCallback(
    (value: string) => {
      if (value === '') {
        resetNeutralContrast();
        return;
      }
      const parsed = Number(value);
      if (Number.isNaN(parsed)) {
        return;
      }
      setNeutralContrast(parsed);
    },
    [resetNeutralContrast, setNeutralContrast],
  );

  const formatSliderValue = useCallback((value: number | string) => {
    const parsed = Number(value);
    if (Number.isNaN(parsed)) {
      return String(value);
    }
    return parsed.toFixed(2);
  }, []);

  const themeModeList = Array.isArray(themeModes) && themeModes.length ? themeModes : THEME_MODES;
  const themeModeIndex = themeModeList.indexOf(themeMode as string);
  const safeThemeModeIndex = themeModeIndex === -1 ? 0 : themeModeIndex;
  const resolvedThemeMode = themeModeList[safeThemeModeIndex] || THEME_MODES[0];
  const nextThemeMode = themeModeList[(safeThemeModeIndex + 1) % themeModeList.length];
  const themeModeLabel = THEME_MODE_LABELS[resolvedThemeMode] || THEME_MODE_LABELS.system;
  const nextThemeLabel = THEME_MODE_LABELS[nextThemeMode] || THEME_MODE_LABELS.system;
  const themeModeIcon = resolvedThemeMode === 'dark'
    ? <MoonIcon size={16} />
    : resolvedThemeMode === 'light'
      ? <SunIcon size={16} />
      : <DesktopIcon size={16} />;

  const handleThemeModeToggle = useCallback(() => {
    cycleThemeMode();
  }, [cycleThemeMode]);

  const themeMenuSection =
    neutralHue != null
      ? (
          <div className="menu__section">
            <div className="menu__heading menu__heading--with-actions">
              <span>Theme</span>
              <div className="menu__heading-actions">
                <button
                  type="button"
                  className="icon-button"
                  onClick={handleThemeModeToggle}
                  aria-label={`Switch theme (next: ${nextThemeLabel})`}
                  title={`Theme: ${themeModeLabel} (next: ${nextThemeLabel})`}
                >
                  {themeModeIcon}
                </button>
                <button
                  type="button"
                  className="icon-button"
                  onClick={handleThemeAdjustmentsReset}
                  aria-label="Reset theme adjustments"
                  title="Reset theme adjustments"
                >
                  <RestoreIcon size={16} />
                </button>
              </div>
            </div>
            <label className="menu__slider" htmlFor={neutralHueInputId}>
              <span className="menu__slider-label">Hue</span>
              <input
                id={neutralHueInputId}
                type="range"
                min="0"
                max="360"
                step="1"
                value={neutralHue}
                onChange={(event) => handleNeutralHueChange(event.target.value)}
                onDoubleClick={resetNeutralHue}
              />
              <span className="menu__slider-value">{neutralHue}°</span>
            </label>
            <label className="menu__slider" htmlFor={neutralChromaInputId}>
              <span className="menu__slider-label">Chroma</span>
              <input
                id={neutralChromaInputId}
                type="range"
                min="0"
                max="1"
                step="0.01"
                value={neutralChroma}
                onChange={(event) => handleNeutralChromaChange(event.target.value)}
                onDoubleClick={resetNeutralChroma}
              />
              <span className="menu__slider-value">{formatSliderValue(neutralChroma)}</span>
            </label>
            <label className="menu__slider" htmlFor={neutralContrastInputId}>
              <span className="menu__slider-label">Contrast</span>
              <input
                id={neutralContrastInputId}
                type="range"
                min="0"
                max="1"
                step="0.01"
                value={neutralContrast}
                onChange={(event) => handleNeutralContrastChange(event.target.value)}
                onDoubleClick={resetNeutralContrast}
              />
              <span className="menu__slider-value">{formatSliderValue(neutralContrast)}</span>
            </label>
          </div>
        )
      : null;

  const handleSearchInputChange = useCallback(
    (event: React.ChangeEvent<HTMLInputElement>) => {
      onSearchChange?.(event.target.value);
    },
    [onSearchChange],
  );
  const handleSearchFormSubmit = useCallback(
    (event: React.FormEvent<HTMLFormElement>) => {
      event.preventDefault();
      onSearchSubmit?.();
    },
    [onSearchSubmit],
  );
  const handleSearchClear = useCallback(() => {
    onSearchClear?.();
  }, [onSearchClear]);
  const tenantButtonRef = useRef<HTMLButtonElement | null>(null);
  const {
    isOpen: tenantMenuOpen,
    toggle: toggleTenantMenuFloating,
    close: closeTenantMenu,
    menuRef: tenantMenuRef,
    menuStyle: tenantMenuStyle,
    updatePosition: refreshTenantMenuPosition,
  } = useFloatingMenu({
    anchorRef: tenantButtonRef,
    minWidth: 220,
    offset: 6,
  }) as FloatingMenuControls;
  const showTenantList = tenants.length > 1;
  const menuClassName = `menu${!showTenantList && !themeMenuSection ? ' menu--simple' : ''}`;

  const toggleTenantMenu = useCallback(() => {
    if (!tenantMenuOpen && tenants.length === 0 && onSelectTenant) {
      onSelectTenant(null, { refreshOnly: true });
    }
    toggleTenantMenuFloating();
  }, [tenantMenuOpen, tenants.length, onSelectTenant, toggleTenantMenuFloating]);

  useEffect(() => {
    if (tenantMenuOpen) {
      refreshTenantMenuPosition();
    }
  }, [tenantMenuOpen, refreshTenantMenuPosition, tenants.length]);

  const handleTenantSelect = useCallback(
    (tenant: TenantOption | null) => {
      const targetId = tenant?.id || null;
      if (!targetId) {
        return;
      }
      closeTenantMenu();
      onSelectTenant?.(tenant);
    },
    [closeTenantMenu, onSelectTenant],
  );

  const handleLogoutFromMenu = useCallback(() => {
    closeTenantMenu();
    onLogout?.();
  }, [closeTenantMenu, onLogout]);

  const handleSettingsFromMenu = useCallback(() => {
    closeTenantMenu();
    onOpenSettings?.();
  }, [closeTenantMenu, onOpenSettings]);

  const renderNodes = useCallback(
    (ids: FolderIdentifier[], depth: number): ReactNode =>
      ids.map((id) => {
        const node = folderNodes.get(id);
        if (!node) return null;
        return (
          <FolderNode
            key={id}
            node={node}
            depth={depth}
            isSelected={selectedFolder === id}
            onToggle={onToggle}
            onSelect={onSelect}
            onDrop={onDrop}
            onDragOver={onDragOver}
            onDragLeave={onDragLeave}
            onDelete={onDeleteFolder}
            onRename={onRenameFolder}
            renderChildren={renderNodes}
            onFolderDragStart={onFolderDragStart}
            onFolderDragEnd={onFolderDragEnd}
            draggingFolderId={draggedFolderId}
          />
        );
      }),
    [
      folderNodes,
      selectedFolder,
      onToggle,
      onSelect,
      onDrop,
      onDragOver,
      onDragLeave,
      onDeleteFolder,
      onRenameFolder,
      onFolderDragStart,
      onFolderDragEnd,
      draggedFolderId,
    ],
  );

  const rootNode = folderNodes.get('root');
  const sidebarClassNames = ['sidebar'];
  if (isResizingSidebar) {
    sidebarClassNames.push('sidebar--resizing');
  }
  if (sidebarSuppressed) {
    sidebarClassNames.push('sidebar--suppressed');
  }

  const tenantMenuContent = tenantMenuOpen
    && tenantMenuStyle
    && typeof document !== 'undefined'
      ? createPortal(
          <div
            className={menuClassName}
            ref={tenantMenuRef}
            role="menu"
            aria-label="Account menu"
            style={tenantMenuStyle}
          >
            {showTenantList ? (
              <div className="menu__section">
                <div className="menu__heading">Switch tenant</div>
                <div className="menu__wrapper">
                  {tenants.map((tenant) => {
                    const tenantId = tenant?.id || null;
                    const isActive = tenantId === activeTenantId;
                    const tenantLabel = tenant?.name || tenantId || 'Tenant';
                    return (
                      <button
                        key={tenantId ?? tenantLabel}
                        type="button"
                        className={`menu__button${isActive ? ' active' : ''}`}
                        onClick={() => handleTenantSelect(tenant)}
                        role="menuitem"
                      >
                        <span className="menu__check-slot">
                          {isActive ? <CheckIcon size={16} /> : null}
                        </span>
                        <span className="menu__label">{tenantLabel}</span>
                      </button>
                    );
                  })}
                </div>
              </div>
            ) : null}
            <div className="menu__section">
              <div className="menu__wrapper">
                <button type="button" className="menu__button" onClick={handleSettingsFromMenu}>
                  <SettingsIcon size={16} />
                  Settings
                </button>
                <button
                  type="button"
                  className="menu__button menu__button--danger"
                  onClick={handleLogoutFromMenu}
                >
                  <LogoutIcon size={16} />
                  Log out
                </button>
              </div>
            </div>
            {themeMenuSection}
          </div>,
          document.body,
        )
      : null;

  return (
    <aside className={sidebarClassNames.join(' ')} ref={sidebarRef} style={sidebarStyle}>
      <button
        type="button"
        className={`sidebar__resize-handle${isResizingSidebar ? ' is-active' : ''}`}
        aria-label="Resize sidebar"
        {...sidebarHandleProps}
      >
        <span aria-hidden="true" />
      </button>
      <input
        type="file"
        ref={uploadInputRef}
        style={{ display: 'none' }}
        multiple
        onChange={handleUploadInputChange}
      />
      <PanelHeader
        className="sidebar__header"
        leading={(
          <>
            <button
              type="button"
              className={`sidebar__title-button${tenantMenuOpen ? ' is-open' : ''}`}
              onClick={toggleTenantMenu}
              aria-haspopup="true"
              aria-expanded={tenantMenuOpen}
              ref={tenantButtonRef}
            >
              <span className="sidebar__title">
                <LogoIcon className="sidebar__logo" />
                <span className="sidebar__title-text">Papercrate</span>
                {tenantName ? <span className="sidebar__tenant"> / {tenantName}</span> : null}
              </span>
              <ChevronDownIcon
                className={`sidebar__title-chevron${tenantMenuOpen ? ' is-open' : ''}`}
                size={16}
              />
            </button>
            {tenantMenuContent}
          </>
        )}
        actions={(
          <>
            {handleCollapse ? (
              <button
                type="button"
                className="icon-button sidebar__collapse-button"
                onClick={handleCollapse}
                aria-label="Collapse sidebar"
                title="Collapse sidebar"
              >
                <SidebarCollapseIcon />
              </button>
            ) : null}
            <button
              type="button"
              className="icon-button"
              onClick={handleUploadButtonClick}
              disabled={!onUploadFiles}
              title="Upload documents"
              aria-label="Upload documents"
            >
              <UploadIcon size={18} />
            </button>
          </>
        )}
      />
      <div className="panel-body sidebar__body">
        {status && (
          <div className="sidebar__status">
            <div className={`status-banner ${status.variant}`}>{status.message}</div>
          </div>
        )}
        {onSearchChange && (
          <form className="sidebar__search" onSubmit={handleSearchFormSubmit}>
            <input
              type="search"
              value={searchQuery}
              onChange={handleSearchInputChange}
              placeholder="Search documents"
              aria-label="Search documents"
            />
            {isFilterActive && (
              <button type="button" onClick={handleSearchClear}>
                Clear
              </button>
            )}
          </form>
        )}
        <div className="sidebar-section sidebar-section--folders">
          <div className="sidebar-section__header">
            <h3>Folders</h3>
            {onCreateFolder ? (
              <div className="sidebar-section__actions">
                <button
                  type="button"
                  className="icon-button"
                  onClick={handleCreateFolder}
                  aria-label="Create folder"
                  disabled={creatingFolder}
                >
                  <FolderPlusIcon size={16} />
                </button>
              </div>
            ) : null}
          </div>
          <ul className="folder-tree">
            {rootNode && renderNodes([rootNode.id], 0)}
          </ul>
        </div>
        <div className="sidebar-section">
          <div className="sidebar-section__header">
            <h3>Tags</h3>
            <div className="sidebar-section__actions">
              <button
                type="button"
                className="icon-button"
                onClick={handleCreateTag}
                aria-label="Create tag"
              >
                <PlusIcon size={16} />
              </button>
              <button
                type="button"
                className="icon-button"
                onClick={handleManageTags}
                aria-label="Manage tags"
              >
                <SettingsIcon size={16} />
              </button>
              <span className="meta">{tags.length}</span>
            </div>
          </div>
          <div
            className={`sidebar-tag-cloud${
              activeTagSet.size ? ' sidebar-tag-cloud--has-active' : ''
            }`}
            role="list"
          >
            {untaggedFilterId ? (
              <button
                type="button"
                role="listitem"
                className={`sidebar-tag-pill sidebar-tag-pill--untagged${
                  untaggedActive ? ' active' : ''
                }`}
                onClick={() => handleToggleTag(untaggedFilterId)}
                aria-pressed={untaggedActive}
                draggable={false}
              >
                No tag assigned
              </button>
            ) : null}
            {tags.map((tag) => {
              const isActive = activeTagSet.has(tag.id);
              const style = getTagColorStyle(tag.color);
              const className = `sidebar-tag-pill${isActive ? ' active' : ''}`;
              return (
                <button
                  key={tag.id}
                  type="button"
                  role="listitem"
                  className={className}
                  style={style || undefined}
                  onClick={() => handleToggleTag(tag.id)}
                  aria-pressed={isActive}
              draggable
              onDragStart={(event) => {
                try {
                  const payload = JSON.stringify({
                    id: tag.id,
                    label: tag.label,
                    color: tag.color || null,
                  });
                  event.dataTransfer.effectAllowed = 'copy';
                  event.dataTransfer.setData('application/x-papercrate-tag', payload);
                  event.dataTransfer.setData('text/papercrate-tag', payload);
                } catch (error) {
                  console.warn('[sidebar] Failed to set tag drag payload', error);
                }
              }}
            >
              {tag.label}
            </button>
          );
            })}
          </div>
        </div>
        <div className="sidebar-section">
          <div className="sidebar-section__header">
            <h3>Correspondents</h3>
            <div className="sidebar-section__actions">
              <button
                type="button"
                className="icon-button"
                onClick={handleCreateCorrespondent}
                aria-label="Create correspondent"
              >
                <PlusIcon size={16} />
              </button>
              <button
                type="button"
                className="icon-button"
                onClick={handleManageCorrespondents}
                aria-label="Manage correspondents"
              >
                <SettingsIcon size={16} />
              </button>
              <span className="meta">{correspondents.length}</span>
            </div>
          </div>
          <ul className="sidebar-correspondent-list">
            {sortedCorrespondents.map((correspondent) => {
              const isActive = activeCorrespondentSet.has(correspondent.id);
              const className = `sidebar-correspondent-item${isActive ? ' active' : ''}`;
              const handleSelect = () => {
                const nextId = isActive ? null : correspondent.id;
                onToggleCorrespondentFilter?.(nextId);
              };
              return (
                <li key={correspondent.id}>
                  <span
                    className={className}
                    role="button"
                    onClick={handleSelect}
                    onKeyDown={(event) => {
                      if (event.key === 'Enter' || event.key === ' ') {
                        event.preventDefault();
                        handleSelect();
                      }
                    }}
                  >
                    {correspondent.name}
                  </span>
                </li>
              );
            })}
          </ul>
        </div>
      </div>
      <div className="sidebar__footer">
        <a
          className="sidebar__footer-link"
          href="https://github.com/papercrate-dms/papercrate"
          target="_blank"
          rel="noreferrer"
          title="Open Papercrate on GitHub"
        >
          <GithubIcon size={16} stroke={1.5} />
          <span>GitHub</span>
        </a>
        <a
          className="sidebar__footer-link"
          href="https://matrix.to/#/#papercrate:matrix.org"
          target="_blank"
          rel="noreferrer"
          title="Join the Papercrate Matrix room"
        >
          <MatrixIcon size={16} stroke={1.5} />
          <span>Matrix</span>
        </a>
        <a
          className="sidebar__footer-link"
          href="https://papercrate.org"
          target="_blank"
          rel="noreferrer"
          title="Visit papercrate.org"
        >
          <WorldIcon size={16} stroke={1.5} />
          <span>Website</span>
        </a>
      </div>
    </aside>
  );
};

export default Sidebar;
export { FolderNode };
