<script lang="ts">
// Named export for recursive self-reference
export default { name: "TreeEntry" };
</script>

<script setup lang="ts">
import { computed, onBeforeUnmount, ref, watch } from "vue";
import ConfirmDialog from "./ConfirmDialog.vue";
import PromptDialog from "./PromptDialog.vue";

interface DirEntry {
  name: string;
  path: string;
  is_dir: boolean;
  children?: DirEntry[];
}

interface TreeExpansionCommand {
  id: number;
  expanded: boolean;
}

const TREE_CONTEXT_MENU_CLOSE_EVENT = "yurou-tree-context-menu-close";

const props = defineProps<{
  entry: DirEntry;
  depth: number;
  currentFilePath?: string | null;
  expansionCommand?: TreeExpansionCommand | null;
}>();

const emit = defineEmits<{
  toggleFolder: [path: string];
  openFile: [path: string];
  createFolder: [path: string];
  createFile: [path: string];
  deleteEntry: [path: string];
  renameEntry: [path: string, newName: string];
}>();

const expanded = ref(true);
const handledExpansionCommandId = ref(0);
const contextMenuOpen = ref(false);
const contextMenuPoint = ref<{ x: number; y: number } | null>(null);
const contextMenuStyle = computed(() => {
  if (!contextMenuPoint.value) return {};
  return {
    left: `${contextMenuPoint.value.x}px`,
    top: `${contextMenuPoint.value.y}px`,
  };
});

watch(contextMenuOpen, (open) => {
  if (!open) {
    contextMenuPoint.value = null;
    document.removeEventListener("click", closeContextMenu);
    document.removeEventListener("keydown", onContextMenuKeydown);
    document.removeEventListener(TREE_CONTEXT_MENU_CLOSE_EVENT, closeContextMenu);
  } else {
    setTimeout(() => document.addEventListener("click", closeContextMenu), 0);
    document.addEventListener("keydown", onContextMenuKeydown);
    document.addEventListener(TREE_CONTEXT_MENU_CLOSE_EVENT, closeContextMenu);
  }
});

onBeforeUnmount(() => {
  document.removeEventListener("click", closeContextMenu);
  document.removeEventListener("keydown", onContextMenuKeydown);
  document.removeEventListener(TREE_CONTEXT_MENU_CLOSE_EVENT, closeContextMenu);
});

function closeContextMenu() {
  contextMenuOpen.value = false;
}

function onContextMenuKeydown(event: KeyboardEvent) {
  if (event.key === "Escape") {
    closeContextMenu();
  }
}

// Auto-expand when children are loaded for the first time
watch(
  () => props.entry.children,
  (children, oldChildren) => {
    if (
      children !== undefined &&
      oldChildren === undefined &&
      props.expansionCommand?.expanded !== false
    ) {
      expanded.value = true;
    }
  },
);

watch(
  () => props.expansionCommand,
  (command) => {
    if (
      !command ||
      !props.entry.is_dir ||
      command.id <= handledExpansionCommandId.value
    ) return;

    handledExpansionCommandId.value = command.id;
    expanded.value = command.expanded;

    if (command.expanded && props.entry.children === undefined) {
      emit("toggleFolder", props.entry.path);
    }
  },
  { immediate: true },
);

const isActive = computed(() => {
  return props.currentFilePath != null && props.currentFilePath === props.entry.path;
});

// ── File type detection ─────────────────────────────────────
const fileType = computed(() => {
  if (props.entry.is_dir) return "folder";
  const ext = props.entry.name.split(".").pop()?.toLowerCase();
  if (ext === "md") return "markdown";
  if (["png", "jpg", "jpeg", "gif", "webp", "avif", "svg", "bmp", "ico"].includes(ext ?? "")) return "image";
  if (["mp4", "webm", "mov", "m4v", "ogv", "avi", "mkv"].includes(ext ?? "")) return "video";
  return "file";
});

const fileIcon = computed(() => {
  switch (fileType.value) {
    case "markdown": return "ri-markdown-line";
    case "image":     return "ri-image-line";
    case "video":     return "ri-film-line";
    default:          return "ri-file-line";
  }
});

// ── Prompt dialog state ──────────────────────────────────────
const promptShow = ref(false);
const promptTitle = ref("");
const promptMessage = ref("");
const promptPlaceholder = ref("");
const promptDefaultValue = ref("");
let promptCallback: ((value: string) => void) | null = null;

function openPrompt(
  title: string,
  message: string,
  placeholder: string,
  defaultValue: string,
  cb: (value: string) => void,
) {
  promptTitle.value = title;
  promptMessage.value = message;
  promptPlaceholder.value = placeholder;
  promptDefaultValue.value = defaultValue;
  promptCallback = cb;
  promptShow.value = true;
}

function onPromptConfirm(value: string) {
  promptShow.value = false;
  promptCallback?.(value);
  promptCallback = null;
}

function onPromptCancel() {
  promptShow.value = false;
  promptCallback = null;
}

// ── Actions ─────────────────────────────────────────────────
function handleCreateFolder() {
  closeContextMenu();
  openPrompt("新建文件夹", "", "输入文件夹名称", "", (name) => {
    emit("createFolder", props.entry.path + "/" + name);
  });
}

function handleCreateFile() {
  closeContextMenu();
  openPrompt("新建 MD 文件", "", "输入文件名称（无需 .md 后缀）", "", (name) => {
    emit("createFile", props.entry.path + "/" + name + ".md");
  });
}

function handleRename() {
  closeContextMenu();
  openPrompt("重命名", "", "输入新名称", props.entry.name, (newName) => {
    if (newName !== props.entry.name) {
      emit("renameEntry", props.entry.path, newName);
    }
  });
}

// ── Delete confirmation ─────────────────────────────────────
const confirmShow = ref(false);
const confirmTitle = ref("");
const confirmMessage = ref("");

function handleDelete() {
  closeContextMenu();
  confirmTitle.value = props.entry.is_dir ? "删除文件夹" : "删除文件";
  confirmMessage.value = props.entry.is_dir
    ? `确定要永久删除文件夹「${props.entry.name}」及其所有内容吗？此操作不可撤销。`
    : `确定要永久删除文件「${props.entry.name}」吗？此操作不可撤销。`;
  confirmShow.value = true;
}

function onConfirmDelete() {
  confirmShow.value = false;
  emit("deleteEntry", props.entry.path);
}

// ── Tree click ──────────────────────────────────────────────
function onClick() {
  if (props.entry.is_dir) {
    if (props.entry.children === undefined) {
      emit("toggleFolder", props.entry.path);
    } else {
      expanded.value = !expanded.value;
    }
  } else {
    emit("openFile", props.entry.path);
  }
}

function onContextMenu(event: MouseEvent) {
  event.preventDefault();
  event.stopPropagation();
  document.dispatchEvent(new Event(TREE_CONTEXT_MENU_CLOSE_EVENT));
  contextMenuPoint.value = { x: event.clientX, y: event.clientY };
  contextMenuOpen.value = true;
}
</script>

<template>
  <!-- ── Confirm dialog ── -->
  <ConfirmDialog
    :show="confirmShow"
    :title="confirmTitle"
    :message="confirmMessage"
    confirm-text="删除"
    cancel-text="取消"
    :danger="true"
    @confirm="onConfirmDelete"
    @cancel="confirmShow = false"
  />

  <!-- ── Prompt dialog ── -->
  <PromptDialog
    :show="promptShow"
    :title="promptTitle"
    :message="promptMessage"
    :placeholder="promptPlaceholder"
    :model-value="promptDefaultValue"
    @confirm="onPromptConfirm"
    @cancel="onPromptCancel"
  />

  <Teleport to="body">
    <div
      v-if="contextMenuOpen"
      class="tree-context-menu"
      :style="contextMenuStyle"
      role="menu"
      @click.stop
      @contextmenu.prevent.stop
    >
      <template v-if="entry.is_dir">
        <button
          class="tree-context-menu-item"
          type="button"
          role="menuitem"
          @click="handleCreateFolder"
        >
          <i class="ri-folder-add-line"></i>
          <span>新建文件夹</span>
        </button>
        <button
          class="tree-context-menu-item"
          type="button"
          role="menuitem"
          @click="handleCreateFile"
        >
          <i class="ri-file-add-line"></i>
          <span>新建 MD 文件</span>
        </button>
      </template>

      <button
        class="tree-context-menu-item"
        type="button"
        role="menuitem"
        @click="handleRename"
      >
        <i class="ri-edit-line"></i>
        <span>重命名</span>
      </button>

      <div class="tree-context-menu-separator"></div>

      <button
        class="tree-context-menu-item tree-context-menu-item-danger"
        type="button"
        role="menuitem"
        @click="handleDelete"
      >
        <i class="ri-delete-bin-line"></i>
        <span>{{ entry.is_dir ? "删除文件夹" : "删除" }}</span>
      </button>
    </div>
  </Teleport>

  <template v-if="entry.is_dir">
    <!-- Folder row -->
    <div
      data-tree-entry
      class="tree-folder flex items-center gap-1 px-2 py-1 rounded-md text-sm cursor-pointer select-none relative group"
      :style="{ paddingLeft: `${depth * 16 + 8}px` }"
      @click="onClick"
      @contextmenu="onContextMenu"
    >
      <i
        class="tree-arrow ri-arrow-right-s-line text-sm transition-transform shrink-0"
        :class="expanded && entry.children ? 'rotate-90' : ''"
      ></i>
      <i class="tree-entry-icon ri-folder-2-line text-base shrink-0"></i>
      <span class="truncate flex-1 min-w-0">{{ entry.name }}</span>
    </div>

    <!-- Children (recursive) -->
    <template v-if="expanded && entry.children">
      <TreeEntry
        v-for="child in entry.children"
        :key="child.path"
        :entry="child"
        :depth="depth + 1"
        :currentFilePath="currentFilePath"
        :expansion-command="expansionCommand"
        @toggle-folder="(path: string) => emit('toggleFolder', path)"
        @open-file="(path: string) => emit('openFile', path)"
        @create-folder="(path: string) => emit('createFolder', path)"
        @create-file="(path: string) => emit('createFile', path)"
        @delete-entry="(path: string) => emit('deleteEntry', path)"
        @rename-entry="(path: string, name: string) => emit('renameEntry', path, name)"
      />
    </template>
  </template>

  <!-- File -->
  <div
    data-tree-entry
    v-else
    :class="[
      'tree-file flex items-center gap-1.5 px-2 py-1 rounded-md text-sm cursor-pointer transition-colors relative group',
      isActive
        ? 'tree-file-active font-medium'
        : 'tree-file-idle',
    ]"
    :style="{ paddingLeft: `${depth * 16 + 8}px` }"
    @click="onClick"
    @contextmenu="onContextMenu"
  >
    <i :class="['tree-entry-icon', fileIcon, 'text-sm shrink-0']"></i>
    <span class="truncate flex-1 min-w-0">{{ entry.name }}</span>
  </div>
</template>

<style scoped>
.tree-folder {
  color: var(--app-text-soft);
}

.tree-folder:hover {
  background: var(--app-hover);
}

.tree-arrow {
  color: var(--app-subtle);
}

.tree-entry-icon {
  color: color-mix(in srgb, var(--app-accent) 68%, transparent);
}

.tree-file-idle {
  color: var(--app-muted);
}

.tree-file-idle:hover {
  background: var(--app-hover);
  color: var(--app-text);
}

.tree-file-active {
  background: var(--app-hover);
  color: var(--app-accent);
}

.tree-context-menu {
  position: fixed;
  z-index: 100;
  min-width: 128px;
  overflow: hidden;
  padding: 4px;
  border: 1px solid color-mix(in srgb, var(--app-border-strong) 76%, transparent);
  border-radius: 8px;
  background: var(--app-surface);
  color: var(--app-text-soft);
  box-shadow:
    0 14px 34px color-mix(in srgb, var(--app-shadow) 70%, transparent),
    0 1px 4px color-mix(in srgb, var(--app-shadow) 70%, transparent);
}

.tree-context-menu-item {
  display: flex;
  width: 100%;
  min-height: 30px;
  align-items: center;
  gap: 8px;
  padding: 0 8px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: inherit;
  cursor: pointer;
  font-size: 13px;
  text-align: left;
}

.tree-context-menu-item:hover {
  background: var(--app-hover);
  color: var(--app-text);
}

.tree-context-menu-item i {
  color: var(--app-muted);
  font-size: 15px;
}

.tree-context-menu-item-danger {
  color: #dc2626;
}

.tree-context-menu-item-danger i {
  color: #dc2626;
}

.tree-context-menu-item-danger:hover {
  background: color-mix(in srgb, #dc2626 10%, transparent);
  color: #dc2626;
}

.tree-context-menu-separator {
  height: 1px;
  margin: 4px -4px;
  background: var(--app-border);
}

</style>
