import {
  DeesElement,
  property,
  html,
  customElement,
  type TemplateResult,
  css,
  cssManager,
  state,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import { themeDefaultStyles } from '../../00theme.js';
import type { IExecutionEnvironment, IFileEntry, IFileWatcher } from '../../00group-runtime/index.js';
import '../../dees-icon/dees-icon.js';
import '../../dees-contextmenu/dees-contextmenu.js';
import { DeesContextmenu } from '../../dees-contextmenu/dees-contextmenu.js';
import { DeesModal } from '../../dees-modal/dees-modal.js';
import '../../00group-input/dees-input-text/dees-input-text.js';
import { DeesInputText } from '../../00group-input/dees-input-text/dees-input-text.js';

declare global {
  interface HTMLElementTagNameMap {
    'dees-workspace-filetree': DeesWorkspaceFiletree;
  }
}

interface ITreeNode extends IFileEntry {
  children?: ITreeNode[];
  expanded?: boolean;
  level: number;
}

@customElement('dees-workspace-filetree')
export class DeesWorkspaceFiletree extends DeesElement {
  public static demo = () => html`
    <div style="width: 300px; height: 400px; position: relative;">
      <dees-workspace-filetree></dees-workspace-filetree>
    </div>
  `;
  public static demoGroup = 'Workspace';

  // INSTANCE
  @property({ type: Object })
  accessor executionEnvironment: IExecutionEnvironment | null = null;

  @property({ type: String })
  accessor rootPath: string = '/';

  @property({ type: String })
  accessor selectedPath: string = '';

  @state()
  accessor treeData: ITreeNode[] = [];

  @state()
  accessor isLoading: boolean = false;

  @state()
  accessor errorMessage: string = '';

  private expandedPaths: Set<string> = new Set();
  private loadTreeStarted: boolean = false;

  // Clipboard state for copy/paste operations
  private clipboardPath: string | null = null;
  private clipboardOperation: 'copy' | 'cut' | null = null;

  // File watcher for auto-refresh
  private fileWatcher: IFileWatcher | null = null;
  private refreshDebounceTimeout: ReturnType<typeof setTimeout> | null = null;
  private lastExecutionEnvironment: IExecutionEnvironment | null = null;

  public static styles = [
    themeDefaultStyles,
    cssManager.defaultStyles,
    css`
      :host {
        display: block;
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        overflow: auto;
        background: ${cssManager.bdTheme('hsl(0 0% 98%)', 'hsl(0 0% 9%)')};
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        font-size: 13px;
      }

      .tree-container {
        padding: 8px 0;
      }

      .tree-item {
        display: flex;
        align-items: center;
        padding: 4px 8px;
        cursor: pointer;
        user-select: none;
        border-radius: 4px;
        margin: 1px 4px;
        transition: background 0.1s ease;
      }

      .tree-item:hover {
        background: ${cssManager.bdTheme('hsl(0 0% 93%)', 'hsl(0 0% 14%)')};
      }

      .tree-item.selected {
        background: ${cssManager.bdTheme('hsl(210 100% 95%)', 'hsl(210 50% 20%)')};
        color: ${cssManager.bdTheme('hsl(210 100% 40%)', 'hsl(210 100% 70%)')};
      }

      .tree-item.selected:hover {
        background: ${cssManager.bdTheme('hsl(210 100% 92%)', 'hsl(210 50% 25%)')};
      }

      .indent {
        display: inline-block;
        width: 16px;
        flex-shrink: 0;
      }

      .expand-icon {
        width: 16px;
        height: 16px;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
        color: ${cssManager.bdTheme('hsl(0 0% 50%)', 'hsl(0 0% 60%)')};
        transition: transform 0.15s ease;
      }

      .expand-icon.expanded {
        transform: rotate(90deg);
      }

      .expand-icon.hidden {
        visibility: hidden;
      }

      .file-icon {
        width: 16px;
        height: 16px;
        margin-right: 6px;
        flex-shrink: 0;
        display: flex;
        align-items: center;
        justify-content: center;
      }

      .file-icon dees-icon {
        width: 16px;
        height: 16px;
      }

      .file-icon.folder {
        color: ${cssManager.bdTheme('hsl(45 80% 45%)', 'hsl(45 70% 55%)')};
      }

      .file-icon.file {
        color: ${cssManager.bdTheme('hsl(0 0% 50%)', 'hsl(0 0% 60%)')};
      }

      .file-icon.typescript {
        color: hsl(211 60% 48%);
      }

      .file-icon.javascript {
        color: hsl(53 93% 54%);
      }

      .file-icon.json {
        color: hsl(45 80% 50%);
      }

      .file-icon.html {
        color: hsl(14 77% 52%);
      }

      .file-icon.css {
        color: hsl(228 77% 59%);
      }

      .file-icon.markdown {
        color: hsl(0 0% 50%);
      }

      .file-name {
        flex: 1;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 85%)')};
      }

      .loading {
        padding: 16px;
        text-align: center;
        color: ${cssManager.bdTheme('hsl(0 0% 50%)', 'hsl(0 0% 60%)')};
      }

      .error {
        padding: 16px;
        text-align: center;
        color: hsl(0 70% 50%);
      }

      .empty {
        padding: 16px;
        text-align: center;
        color: ${cssManager.bdTheme('hsl(0 0% 50%)', 'hsl(0 0% 60%)')};
        font-style: italic;
      }

      .filetree-toolbar {
        display: flex;
        align-items: center;
        justify-content: space-between;
        height: 36px;
        padding: 0 12px;
        border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 85%)', 'hsl(0 0% 15%)')};
        background: ${cssManager.bdTheme('hsl(0 0% 96%)', 'hsl(0 0% 8%)')};
        position: sticky;
        top: 0;
        z-index: 1;
      }

      .toolbar-title {
        font-size: 11px;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: 0.5px;
        color: ${cssManager.bdTheme('hsl(0 0% 40%)', 'hsl(0 0% 60%)')};
      }

      .toolbar-actions {
        display: flex;
        gap: 4px;
      }

      .toolbar-button {
        padding: 4px;
        border-radius: 4px;
        cursor: pointer;
        opacity: 0.7;
        transition: opacity 0.15s, background 0.15s;
        display: flex;
        align-items: center;
        justify-content: center;
        color: ${cssManager.bdTheme('hsl(0 0% 30%)', 'hsl(0 0% 70%)')};
      }

      .toolbar-button:hover {
        opacity: 1;
        background: ${cssManager.bdTheme('hsl(0 0% 0% / 0.08)', 'hsl(0 0% 100% / 0.1)')};
      }
    `,
  ];

  public render(): TemplateResult {
    if (!this.executionEnvironment) {
      return html`
        <div class="empty">
          No execution environment provided.
        </div>
      `;
    }

    if (this.isLoading) {
      return html`
        <div class="loading">
          Loading files...
        </div>
      `;
    }

    if (this.errorMessage) {
      return html`
        <div class="error">
          ${this.errorMessage}
        </div>
      `;
    }

    return html`
      <div class="filetree-toolbar">
        <span class="toolbar-title">Explorer</span>
        <div class="toolbar-actions">
          <div class="toolbar-button" @click=${() => this.createNewFile('/')} title="New File">
            <dees-icon .icon=${'lucide:filePlus'} iconSize="16"></dees-icon>
          </div>
          <div class="toolbar-button" @click=${() => this.createNewFolder('/')} title="New Folder">
            <dees-icon .icon=${'lucide:folderPlus'} iconSize="16"></dees-icon>
          </div>
        </div>
      </div>
      ${this.treeData.length === 0
        ? html`<div class="empty">No files found.</div>`
        : html`
            <div class="tree-container" @contextmenu=${this.handleEmptySpaceContextMenu}>
              ${this.renderTree(this.treeData)}
            </div>
          `}
    `;
  }

  private renderTree(nodes: ITreeNode[]): TemplateResult[] {
    return nodes.map(node => this.renderNode(node));
  }

  private renderNode(node: ITreeNode): TemplateResult {
    const isDirectory = node.type === 'directory';
    const isExpanded = this.expandedPaths.has(node.path);
    const isSelected = node.path === this.selectedPath;
    const iconClass = this.getFileIconClass(node);

    return html`
      <div
        class="tree-item ${isSelected ? 'selected' : ''}"
        style="padding-left: ${8 + node.level * 16}px"
        @click=${(e: MouseEvent) => this.handleItemClick(e, node)}
        @contextmenu=${(e: MouseEvent) => this.handleContextMenu(e, node)}
      >
        <span class="expand-icon ${isExpanded ? 'expanded' : ''} ${!isDirectory ? 'hidden' : ''}">
          <dees-icon .icon=${'lucide:chevronRight'} iconSize="12"></dees-icon>
        </span>
        <span class="file-icon ${iconClass}">
          <dees-icon .icon=${this.getFileIcon(node)} iconSize="16"></dees-icon>
        </span>
        <span class="file-name">${node.name}</span>
      </div>
      ${isDirectory && isExpanded && node.children
        ? this.renderTree(node.children)
        : ''}
    `;
  }

  private getFileIcon(node: ITreeNode): string {
    if (node.type === 'directory') {
      return this.expandedPaths.has(node.path) ? 'lucide:folderOpen' : 'lucide:folder';
    }

    const ext = node.name.split('.').pop()?.toLowerCase();
    switch (ext) {
      case 'ts':
      case 'tsx':
        return 'lucide:fileCode';
      case 'js':
      case 'jsx':
        return 'lucide:fileCode';
      case 'json':
        return 'lucide:fileJson';
      case 'html':
        return 'lucide:fileCode';
      case 'css':
      case 'scss':
      case 'less':
        return 'lucide:fileCode';
      case 'md':
        return 'lucide:fileText';
      case 'png':
      case 'jpg':
      case 'jpeg':
      case 'gif':
      case 'svg':
        return 'lucide:image';
      default:
        return 'lucide:file';
    }
  }

  private getFileIconClass(node: ITreeNode): string {
    if (node.type === 'directory') return 'folder';

    const ext = node.name.split('.').pop()?.toLowerCase();
    switch (ext) {
      case 'ts':
      case 'tsx':
        return 'typescript';
      case 'js':
      case 'jsx':
        return 'javascript';
      case 'json':
        return 'json';
      case 'html':
        return 'html';
      case 'css':
      case 'scss':
      case 'less':
        return 'css';
      case 'md':
        return 'markdown';
      default:
        return 'file';
    }
  }

  private async handleItemClick(e: MouseEvent, node: ITreeNode) {
    e.stopPropagation();

    if (node.type === 'directory') {
      await this.toggleDirectory(node);
    } else {
      this.selectedPath = node.path;
      this.dispatchEvent(
        new CustomEvent('file-select', {
          detail: { path: node.path, name: node.name },
          bubbles: true,
          composed: true,
        })
      );
    }
  }

  private async toggleDirectory(node: ITreeNode) {
    if (this.expandedPaths.has(node.path)) {
      this.expandedPaths.delete(node.path);
    } else {
      this.expandedPaths.add(node.path);
      // Load children if not already loaded
      if (!node.children || node.children.length === 0) {
        await this.loadDirectoryContents(node);
      }
    }
    this.requestUpdate();
  }

  private async loadDirectoryContents(node: ITreeNode) {
    if (!this.executionEnvironment) return;

    try {
      const entries = await this.executionEnvironment.readDir(node.path);
      node.children = this.sortEntries(entries).map(entry => ({
        ...entry,
        level: node.level + 1,
        expanded: false,
        children: entry.type === 'directory' ? [] : undefined,
      }));
    } catch (error) {
      console.error(`Failed to load directory ${node.path}:`, error);
    }
  }

  private async handleContextMenu(e: MouseEvent, node: ITreeNode) {
    e.preventDefault();
    e.stopPropagation();

    const menuItems = [];

    if (node.type === 'directory') {
      // Directory-specific options
      menuItems.push(
        {
          name: 'New File',
          iconName: 'filePlus',
          action: async () => this.createNewFile(node.path),
        },
        {
          name: 'New Folder',
          iconName: 'folderPlus',
          action: async () => this.createNewFolder(node.path),
        },
        { divider: true }
      );
    }

    // Common options for both files and directories
    menuItems.push(
      {
        name: 'Rename',
        iconName: 'pencil',
        action: async () => this.renameItem(node),
      },
      {
        name: 'Duplicate',
        iconName: 'files',
        action: async () => this.duplicateItem(node),
      },
      {
        name: 'Copy',
        iconName: 'copy',
        action: async () => this.copyItem(node),
      }
    );

    // Paste option (only for directories and when clipboard has content)
    if (node.type === 'directory' && this.clipboardPath) {
      menuItems.push({
        name: 'Paste',
        iconName: 'clipboard',
        action: async () => this.pasteItem(node.path),
      });
    }

    menuItems.push(
      { divider: true },
      {
        name: 'Delete',
        iconName: 'trash2',
        action: async () => this.deleteItem(node),
      }
    );

    await DeesContextmenu.openContextMenuWithOptions(e, menuItems);
  }

  private async handleEmptySpaceContextMenu(e: MouseEvent) {
    // Only trigger if clicking on the container itself, not a tree item
    const target = e.target as HTMLElement;
    if (target.closest('.tree-item')) return;

    e.preventDefault();
    e.stopPropagation();

    const menuItems: any[] = [
      {
        name: 'New File',
        iconName: 'filePlus',
        action: async () => this.createNewFile('/'),
      },
      {
        name: 'New Folder',
        iconName: 'folderPlus',
        action: async () => this.createNewFolder('/'),
      },
    ];

    // Add Paste option if clipboard has content
    if (this.clipboardPath) {
      menuItems.push(
        { divider: true },
        {
          name: 'Paste',
          iconName: 'clipboard',
          action: async () => this.pasteItem('/'),
        }
      );
    }

    await DeesContextmenu.openContextMenuWithOptions(e, menuItems);
  }

  private async showInputModal(options: {
    heading: string;
    label: string;
    value?: string;
    buttonName?: string;
  }): Promise<string | null> {
    return new Promise(async (resolve) => {
      const modal = await DeesModal.createAndShow({
        heading: options.heading,
        width: 'small',
        content: html`
          <dees-input-text
            .label=${options.label}
            .value=${options.value || ''}
          ></dees-input-text>
        `,
        menuOptions: [
          {
            name: 'Cancel',
            action: async (modalRef) => {
              await modalRef.destroy();
              resolve(null);
            },
          },
          {
            name: options.buttonName || 'Create',
            action: async (modalRef) => {
              // Query the input element directly and read its value
              const contentEl = modalRef.shadowRoot?.querySelector('.modal .content');
              const inputElement = contentEl?.querySelector('dees-input-text') as DeesInputText | null;
              const inputValue = inputElement?.value?.trim() || '';

              await modalRef.destroy();
              resolve(inputValue || null);
            },
          },
        ],
      });

      // Focus the input after modal renders
      await modal.updateComplete;
      const contentEl = modal.shadowRoot?.querySelector('.modal .content');
      if (contentEl) {
        const inputElement = contentEl.querySelector('dees-input-text') as DeesInputText | null;
        if (inputElement) {
          await inputElement.updateComplete;
          inputElement.focus();
        }
      }
    });
  }

  private async createNewFile(parentPath: string) {
    const fileName = await this.showInputModal({
      heading: 'New File',
      label: 'File name',
    });
    if (!fileName || !this.executionEnvironment) return;

    const newPath = parentPath === '/' ? `/${fileName}` : `${parentPath}/${fileName}`;
    try {
      await this.executionEnvironment.writeFile(newPath, '');
      await this.refresh();
      this.dispatchEvent(
        new CustomEvent('file-created', {
          detail: { path: newPath },
          bubbles: true,
          composed: true,
        })
      );
    } catch (error) {
      console.error('Failed to create file:', error);
    }
  }

  private async createNewFolder(parentPath: string) {
    const folderName = await this.showInputModal({
      heading: 'New Folder',
      label: 'Folder name',
    });
    if (!folderName || !this.executionEnvironment) return;

    const newPath = parentPath === '/' ? `/${folderName}` : `${parentPath}/${folderName}`;
    try {
      await this.executionEnvironment.mkdir(newPath);
      await this.refresh();
      this.dispatchEvent(
        new CustomEvent('folder-created', {
          detail: { path: newPath },
          bubbles: true,
          composed: true,
        })
      );
    } catch (error) {
      console.error('Failed to create folder:', error);
    }
  }

  private async deleteItem(node: ITreeNode) {
    if (!this.executionEnvironment) return;

    const confirmed = confirm(`Delete ${node.name}?`);
    if (!confirmed) return;

    try {
      await this.executionEnvironment.rm(node.path, { recursive: node.type === 'directory' });
      await this.refresh();
      this.dispatchEvent(
        new CustomEvent('item-deleted', {
          detail: { path: node.path, type: node.type },
          bubbles: true,
          composed: true,
        })
      );
    } catch (error) {
      console.error('Failed to delete item:', error);
    }
  }

  /**
   * Rename a file or folder
   */
  private async renameItem(node: ITreeNode) {
    if (!this.executionEnvironment) return;

    const newName = await this.showInputModal({
      heading: 'Rename',
      label: 'New name',
      value: node.name,
      buttonName: 'Rename',
    });
    if (!newName || newName === node.name) return;

    // Calculate new path
    const parentPath = node.path.substring(0, node.path.lastIndexOf('/')) || '/';
    const newPath = parentPath === '/' ? `/${newName}` : `${parentPath}/${newName}`;

    try {
      if (node.type === 'file') {
        // For files: read content, write to new path, delete old
        const content = await this.executionEnvironment.readFile(node.path);
        await this.executionEnvironment.writeFile(newPath, content);
        await this.executionEnvironment.rm(node.path);
      } else {
        // For directories: recursively copy contents then delete old
        await this.copyDirectoryContents(node.path, newPath);
        await this.executionEnvironment.rm(node.path, { recursive: true });
      }
      await this.refresh();
      this.dispatchEvent(
        new CustomEvent('item-renamed', {
          detail: { oldPath: node.path, newPath, type: node.type },
          bubbles: true,
          composed: true,
        })
      );
    } catch (error) {
      console.error('Failed to rename item:', error);
    }
  }

  /**
   * Duplicate a file or folder
   */
  private async duplicateItem(node: ITreeNode) {
    if (!this.executionEnvironment) return;

    const parentPath = node.path.substring(0, node.path.lastIndexOf('/')) || '/';
    let newName: string;

    if (node.type === 'file') {
      // Add _copy before extension
      const lastDot = node.name.lastIndexOf('.');
      if (lastDot > 0) {
        const baseName = node.name.substring(0, lastDot);
        const ext = node.name.substring(lastDot);
        newName = `${baseName}_copy${ext}`;
      } else {
        newName = `${node.name}_copy`;
      }
    } else {
      newName = `${node.name}_copy`;
    }

    const newPath = parentPath === '/' ? `/${newName}` : `${parentPath}/${newName}`;

    try {
      if (node.type === 'file') {
        const content = await this.executionEnvironment.readFile(node.path);
        await this.executionEnvironment.writeFile(newPath, content);
      } else {
        await this.copyDirectoryContents(node.path, newPath);
      }
      await this.refresh();
      this.dispatchEvent(
        new CustomEvent('item-duplicated', {
          detail: { sourcePath: node.path, newPath, type: node.type },
          bubbles: true,
          composed: true,
        })
      );
    } catch (error) {
      console.error('Failed to duplicate item:', error);
    }
  }

  /**
   * Copy item path to clipboard
   */
  private async copyItem(node: ITreeNode) {
    this.clipboardPath = node.path;
    this.clipboardOperation = 'copy';
  }

  /**
   * Paste copied item to target directory
   */
  private async pasteItem(targetPath: string) {
    if (!this.executionEnvironment || !this.clipboardPath) return;

    // Get the name from clipboard path
    const name = this.clipboardPath.split('/').pop() || 'pasted';
    const newPath = targetPath === '/' ? `/${name}` : `${targetPath}/${name}`;

    try {
      // Check if source exists
      if (!(await this.executionEnvironment.exists(this.clipboardPath))) {
        console.error('Source file no longer exists');
        this.clipboardPath = null;
        this.clipboardOperation = null;
        return;
      }

      // Check if it's a file or directory by trying to read as file
      try {
        const content = await this.executionEnvironment.readFile(this.clipboardPath);
        await this.executionEnvironment.writeFile(newPath, content);
      } catch {
        // If reading fails, it's a directory
        await this.copyDirectoryContents(this.clipboardPath, newPath);
      }

      await this.refresh();
      this.dispatchEvent(
        new CustomEvent('item-pasted', {
          detail: { sourcePath: this.clipboardPath, targetPath: newPath },
          bubbles: true,
          composed: true,
        })
      );

      // Clear clipboard after paste
      this.clipboardPath = null;
      this.clipboardOperation = null;
    } catch (error) {
      console.error('Failed to paste item:', error);
    }
  }

  /**
   * Recursively copy directory contents to a new path
   */
  private async copyDirectoryContents(sourcePath: string, destPath: string) {
    if (!this.executionEnvironment) return;

    // Create destination directory
    await this.executionEnvironment.mkdir(destPath);

    // Read source directory contents
    const entries = await this.executionEnvironment.readDir(sourcePath);

    for (const entry of entries) {
      const srcEntryPath = sourcePath === '/' ? `/${entry.name}` : `${sourcePath}/${entry.name}`;
      const destEntryPath = destPath === '/' ? `/${entry.name}` : `${destPath}/${entry.name}`;

      if (entry.type === 'directory') {
        await this.copyDirectoryContents(srcEntryPath, destEntryPath);
      } else {
        const content = await this.executionEnvironment.readFile(srcEntryPath);
        await this.executionEnvironment.writeFile(destEntryPath, content);
      }
    }
  }

  public async firstUpdated() {
    await this.loadTree();
  }

  public async updated(changedProperties: Map<string, any>) {
    if (changedProperties.has('executionEnvironment')) {
      // Stop watching the old environment
      if (this.lastExecutionEnvironment !== this.executionEnvironment) {
        this.stopFileWatcher();
        this.lastExecutionEnvironment = this.executionEnvironment;
      }

      if (this.executionEnvironment) {
        await this.loadTree();
        this.startFileWatcher();
      }
    }
  }

  public async disconnectedCallback() {
    await super.disconnectedCallback();
    this.stopFileWatcher();
    if (this.refreshDebounceTimeout) {
      clearTimeout(this.refreshDebounceTimeout);
      this.refreshDebounceTimeout = null;
    }
  }

  private startFileWatcher() {
    if (!this.executionEnvironment || this.fileWatcher) return;

    try {
      this.fileWatcher = this.executionEnvironment.watch(
        '/',
        (_event, _filename) => {
          // Debounce refresh to avoid excessive updates
          if (this.refreshDebounceTimeout) {
            clearTimeout(this.refreshDebounceTimeout);
          }
          this.refreshDebounceTimeout = setTimeout(() => {
            this.refresh();
          }, 300);
        },
        { recursive: true }
      );
    } catch (error) {
      console.warn('File watching not supported:', error);
    }
  }

  private stopFileWatcher() {
    if (this.fileWatcher) {
      this.fileWatcher.stop();
      this.fileWatcher = null;
    }
  }

  private async loadTree() {
    if (!this.executionEnvironment) return;

    // Prevent double loading on initial render
    if (this.loadTreeStarted) return;
    this.loadTreeStarted = true;

    this.isLoading = true;
    this.errorMessage = '';

    try {
      // Wait for environment to be ready
      if (!this.executionEnvironment.ready) {
        await this.executionEnvironment.init();
      }

      const entries = await this.executionEnvironment.readDir(this.rootPath);
      this.treeData = this.sortEntries(entries).map(entry => ({
        ...entry,
        level: 0,
        expanded: false,
        children: entry.type === 'directory' ? [] : undefined,
      }));
    } catch (error) {
      this.errorMessage = `Failed to load files: ${error}`;
      console.error('Failed to load file tree:', error);
      // Reset flag to allow retry
      this.loadTreeStarted = false;
    } finally {
      this.isLoading = false;
    }
  }

  private sortEntries(entries: IFileEntry[]): IFileEntry[] {
    return entries.sort((a, b) => {
      // Directories first
      if (a.type !== b.type) {
        return a.type === 'directory' ? -1 : 1;
      }
      // Then alphabetically
      return a.name.localeCompare(b.name);
    });
  }

  public async refresh() {
    this.expandedPaths.clear();
    this.loadTreeStarted = false; // Reset to allow loading
    await this.loadTree();
  }

  public selectFile(path: string) {
    this.selectedPath = path;
  }
}
