<script setup lang="ts">
import type { DocumentFileNode } from './types';

import { computed, ref, watch } from 'vue';

import { Plus, Upload } from '@easyflow/icons';

import { onClickOutside } from '@vueuse/core';

import FileTreeNode from './FileTreeNode.vue';

const props = withDefaults(
  defineProps<{
    busy?: boolean;
    currentPath?: string;
    dirtyPaths?: Set<string>;
    errorCounts?: Record<string, number>;
    nodes: DocumentFileNode[];
    readonly?: boolean;
    title?: string;
    warningCounts?: Record<string, number>;
  }>(),
  {
    busy: false,
    currentPath: '',
    dirtyPaths: () => new Set<string>(),
    errorCounts: () => ({}),
    readonly: false,
    title: 'Skill 文件',
    warningCounts: () => ({}),
  },
);

const emit = defineEmits<{
  create: [node?: DocumentFileNode];
  download: [node: DocumentFileNode];
  remove: [node: DocumentFileNode];
  rename: [node: DocumentFileNode];
  select: [node: DocumentFileNode];
  upload: [node?: DocumentFileNode];
}>();

const effectiveCurrentPath = computed(
  () => props.currentPath || props.nodes[0]?.path || '',
);
const focusedPath = ref('');
const createMenuOpen = ref(false);
const createMenuRef = ref<HTMLElement>();

onClickOutside(createMenuRef, () => {
  createMenuOpen.value = false;
});

watch(
  effectiveCurrentPath,
  (path) => {
    focusedPath.value = path;
  },
  { immediate: true },
);

function focusTreeItem(items: HTMLElement[], index: number) {
  const target = items[index];
  if (!target) return;
  items.forEach((item) => (item.tabIndex = -1));
  target.tabIndex = 0;
  focusedPath.value = target.dataset.treePath || '';
  target.focus();
}

function handleTreeKeydown(event: KeyboardEvent) {
  if (!['ArrowDown', 'ArrowUp', 'End', 'Home'].includes(event.key)) return;
  const tree = event.currentTarget as HTMLElement;
  const items = [...tree.querySelectorAll<HTMLElement>('[role="treeitem"]')];
  if (items.length === 0) return;
  const current = (event.target as HTMLElement).closest<HTMLElement>(
    '[role="treeitem"]',
  );
  const currentIndex = current ? items.indexOf(current) : -1;
  let nextIndex = currentIndex;
  if (event.key === 'ArrowDown')
    nextIndex = Math.min(currentIndex + 1, items.length - 1);
  if (event.key === 'ArrowUp')
    nextIndex =
      currentIndex < 0 ? items.length - 1 : Math.max(currentIndex - 1, 0);
  if (event.key === 'Home') nextIndex = 0;
  if (event.key === 'End') nextIndex = items.length - 1;
  if (nextIndex >= 0) {
    event.preventDefault();
    focusTreeItem(items, nextIndex);
  }
}

function runHeaderAction(action: 'create' | 'upload') {
  createMenuOpen.value = false;
  if (action === 'create') emit('create');
  else emit('upload');
}
</script>

<template>
  <section class="file-tree-panel">
    <header class="file-tree-panel__header">
      <strong>{{ title }}</strong>
      <div
        v-if="!readonly"
        ref="createMenuRef"
        class="file-tree-panel__actions"
      >
        <button
          type="button"
          aria-label="添加资源"
          title="添加资源"
          :disabled="busy"
          :aria-expanded="createMenuOpen"
          aria-haspopup="menu"
          @click="createMenuOpen = !createMenuOpen"
        >
          <Plus :size="15" aria-hidden="true" />
        </button>
        <div v-if="createMenuOpen" class="file-tree-panel__menu" role="menu">
          <button
            type="button"
            role="menuitem"
            @click="runHeaderAction('create')"
          >
            <Plus :size="15" aria-hidden="true" />
            新建文件
          </button>
          <button
            type="button"
            role="menuitem"
            @click="runHeaderAction('upload')"
          >
            <Upload :size="15" aria-hidden="true" />
            上传资源
          </button>
        </div>
      </div>
    </header>
    <nav class="file-tree-panel__body" aria-label="Skill 文件树">
      <ul
        v-if="nodes.length > 0"
        class="file-tree-panel__list"
        role="tree"
        aria-label="Skill 文件"
        @keydown="handleTreeKeydown"
      >
        <FileTreeNode
          v-for="node in nodes"
          :key="node.path"
          :node="node"
          :current-path="effectiveCurrentPath"
          :focused-path="focusedPath"
          :dirty-paths="dirtyPaths"
          :error-counts="errorCounts"
          :warning-counts="warningCounts"
          :readonly="readonly"
          @create="emit('create', $event)"
          @download="emit('download', $event)"
          @focus-path="focusedPath = $event"
          @remove="emit('remove', $event)"
          @rename="emit('rename', $event)"
          @select="emit('select', $event)"
          @upload="emit('upload', $event)"
        />
      </ul>
      <div v-else class="file-tree-panel__empty">暂无文件</div>
    </nav>
  </section>
</template>

<style scoped>
.file-tree-panel {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

.file-tree-panel__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 48px;
  padding: 0 var(--space-4);
  border-bottom: 1px solid hsl(var(--line-subtle));
}

.file-tree-panel__actions {
  position: relative;
  display: flex;
  gap: var(--space-1);
}

.file-tree-panel__actions button {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  color: hsl(var(--text-muted));
  cursor: pointer;
  background: transparent;
  border: 0;
  border-radius: var(--radius-control);
}

.file-tree-panel__actions button:hover {
  color: hsl(var(--foreground));
  background: hsl(var(--nav-item-hover));
}

.file-tree-panel__actions button:disabled {
  cursor: not-allowed;
  opacity: 0.45;
}

.file-tree-panel__actions button:focus-visible {
  outline: 2px solid hsl(var(--primary) / 72%);
  outline-offset: 1px;
}

.file-tree-panel__menu {
  position: absolute;
  top: calc(100% + var(--space-1));
  right: 0;
  z-index: 8;
  display: grid;
  width: 136px;
  padding: var(--space-1);
  background: hsl(var(--surface-elevated));
  border: 1px solid hsl(var(--line-subtle));
  border-radius: var(--radius-control);
  box-shadow: var(--shadow-float);
}

.file-tree-panel__menu button {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  justify-content: flex-start;
  width: 100%;
  padding: 0 var(--space-2);
}

.file-tree-panel__body {
  flex: 1;
  min-height: 0;
  padding: var(--space-2) 0;
  overflow: auto;
}

.file-tree-panel__list {
  padding: 0;
  margin: 0;
}

.file-tree-panel__empty {
  padding: var(--space-8) var(--space-4);
  color: hsl(var(--text-muted));
  text-align: center;
}
</style>
