<script setup lang="ts">
import { ref, computed, nextTick, watch } from 'vue'
import { useTableStore } from '#/store/zq-smart-table'
import { SmartItemType, type Table } from '#/types/zq-smart-table/table'
import { useI18n } from '@vben/locales'
import { useRouter, useRoute } from 'vue-router'
import {
  Grid,
  FileText,
  Search,
  ChevronDown,
  ChevronRight,
  Ellipsis,
  Pencil,
  Trash2,
  FilePlus2,
  BookOpen,
} from '@vben/icons'
import { ElMessageBox } from 'element-plus'

type TreeNode = Table & { children: TreeNode[] }
interface FlatNode {
  id: string
  name: string
  type: SmartItemType
  depth: number
  hasChildren: boolean
  parentId: string | null | undefined
}

const emit = defineEmits<{
  selectSpace: [spaceId: string]
  selectDoc: [tableId: string]
  showSpaces: []
}>()

const { t } = useI18n()
const tableStore = useTableStore()
const router = useRouter()
const route = useRoute()

const SIDEBAR_DEFAULT = 256
const SIDEBAR_MIN = 200
const SIDEBAR_MAX = 480

const sidebarWidth = ref(SIDEBAR_DEFAULT)
const isResizing = ref(false)
const searchText = ref('')

function onResizeStart(e: MouseEvent) {
  e.preventDefault()
  isResizing.value = true
  const startX = e.clientX
  const startW = sidebarWidth.value

  function onMove(ev: MouseEvent) {
    const newW = startW + (ev.clientX - startX)
    sidebarWidth.value = Math.max(SIDEBAR_MIN, Math.min(SIDEBAR_MAX, newW))
  }

  function onUp() {
    isResizing.value = false
    document.removeEventListener('mousemove', onMove)
    document.removeEventListener('mouseup', onUp)
  }

  document.addEventListener('mousemove', onMove)
  document.addEventListener('mouseup', onUp)
}

function onResizeDblClick() {
  sidebarWidth.value = SIDEBAR_DEFAULT
}

// Active state
const activeView = computed(() => {
  const tableId = route.params.tableId as string | undefined
  if (tableId) return { type: 'doc' as const, id: tableId }
  return { type: 'spaces' as const, id: null }
})

// ==================== My Documents Tree ====================
const expandedIds = ref<Set<string>>(new Set())

function toggleExpand(id: string) {
  const s = new Set(expandedIds.value)
  if (s.has(id)) s.delete(id)
  else s.add(id)
  expandedIds.value = s
}

function buildTree(items: Table[]): TreeNode[] {
  const map = new Map<string, TreeNode>()
  const roots: TreeNode[] = []
  for (const item of items) {
    map.set(item.id, { ...item, children: [] })
  }
  for (const node of map.values()) {
    const pid = node.parentId
    if (pid && map.has(pid)) {
      map.get(pid)!.children.push(node)
    } else {
      roots.push(node)
    }
  }
  return roots
}

function filterTree(nodes: TreeNode[], q: string): TreeNode[] {
  if (!q) return nodes
  const result: TreeNode[] = []
  for (const node of nodes) {
    const childMatch = filterTree(node.children, q)
    if (node.name.toLowerCase().includes(q) || childMatch.length > 0) {
      result.push({ ...node, children: childMatch })
    }
  }
  return result
}

function flattenTree(nodes: TreeNode[], depth = 0): FlatNode[] {
  const result: FlatNode[] = []
  for (const node of nodes) {
    result.push({
      id: node.id,
      name: node.name,
      type: node.type,
      depth,
      hasChildren: node.children.length > 0,
      parentId: node.parentId,
    })
    if (node.children.length > 0 && expandedIds.value.has(node.id)) {
      result.push(...flattenTree(node.children, depth + 1))
    }
  }
  return result
}

const tree = computed(() => buildTree(tableStore.tables))
const filteredTree = computed(() => {
  const q = searchText.value.trim().toLowerCase()
  return filterTree(tree.value, q)
})
const flatList = computed(() => flattenTree(filteredTree.value))
const hasSearchResults = computed(() => flatList.value.length > 0)

watch(() => tableStore.tables, () => {
  if (expandedIds.value.size === 0 && tableStore.tables.length > 0) {
    const ids = new Set<string>()
    function walk(nodes: TreeNode[]) {
      for (const n of nodes) {
        if (n.children.length > 0) {
          ids.add(n.id)
          walk(n.children)
        }
      }
    }
    walk(tree.value as TreeNode[])
    expandedIds.value = ids
  }
}, { immediate: true })

// Drag state
const dragId = ref<string | null>(null)
const dropTargetId = ref<string | null>(null)
const dropPosition = ref<'inside' | 'before' | 'after' | null>(null)

function onDragStart(e: DragEvent, id: string) {
  dragId.value = id
  if (e.dataTransfer) {
    e.dataTransfer.effectAllowed = 'move'
    e.dataTransfer.setData('text/plain', id)
  }
}

function onDragOver(e: DragEvent, id: string) {
  e.preventDefault()
  if (dragId.value === id) return
  dropTargetId.value = id
  const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()
  const y = e.clientY - rect.top
  const h = rect.height
  if (y < h * 0.25) dropPosition.value = 'before'
  else if (y > h * 0.75) dropPosition.value = 'after'
  else dropPosition.value = 'inside'
}

function onDragLeave() {
  dropTargetId.value = null
  dropPosition.value = null
}

function onDrop(e: DragEvent) {
  e.preventDefault()
  if (!dragId.value || !dropTargetId.value || dragId.value === dropTargetId.value) {
    resetDrag()
    return
  }
  const targetItem = tableStore.tables.find(t => t.id === dropTargetId.value)
  if (!targetItem) { resetDrag(); return }
  const draggedId = dragId.value
  if (dropPosition.value === 'inside') {
    const children = tableStore.tables.filter(
      (t) => (t.parentId ?? null) === dropTargetId.value && t.id !== draggedId,
    )
    const lastChild = children.length > 0 ? children[children.length - 1] : null
    tableStore.moveTable(draggedId, dropTargetId.value, lastChild?.id ?? null)
    expandedIds.value = new Set([...expandedIds.value, dropTargetId.value])
  } else {
    const newParentId = targetItem.parentId ?? null
    const siblings = tableStore.tables.filter(
      (t) => (t.parentId ?? null) === newParentId && t.id !== draggedId,
    )
    const targetIdx = siblings.findIndex((s) => s.id === dropTargetId.value)
    let afterId: string | null = null
    if (dropPosition.value === 'before') {
      afterId = targetIdx > 0 ? (siblings[targetIdx - 1]?.id ?? null) : null
    } else {
      afterId = dropTargetId.value
    }
    tableStore.moveTable(draggedId, newParentId, afterId)
  }
  resetDrag()
}

function resetDrag() {
  dragId.value = null
  dropTargetId.value = null
  dropPosition.value = null
}

function getItemDropClass(nodeId: string) {
  if (dropTargetId.value !== nodeId) return ''
  if (dropPosition.value === 'inside') return 'drop-inside'
  if (dropPosition.value === 'before') return 'drop-before'
  if (dropPosition.value === 'after') return 'drop-after'
  return ''
}

// Rename
const renamingId = ref<string | null>(null)
const renameValue = ref('')
const renameInputEl = ref<HTMLInputElement | null>(null)
function setRenameRef(el: any) { renameInputEl.value = el }

function startRename(tableId: string, currentName: string) {
  renamingId.value = tableId
  renameValue.value = currentName
  nextTick(() => {
    renameInputEl.value?.focus()
    renameInputEl.value?.select()
  })
}

async function confirmRename(tableId: string) {
  if (renameValue.value.trim()) {
    const table = tableStore.tables.find((item) => item.id === tableId)
    if (table) {
      table.name = renameValue.value.trim()
      const { updateTableApi } = await import('#/api/smart-table')
      updateTableApi(tableId, { name: table.name }).catch(() => {})
    }
  }
  renamingId.value = null
}

function handleDeleteTable(tableId: string, tableName: string) {
  ElMessageBox.confirm(
    t('sidebar.deleteTableConfirm', { name: tableName }),
    t('sidebar.deleteTable'),
    {
      confirmButtonText: t('common.confirm'),
      cancelButtonText: t('common.cancel'),
      type: 'warning',
    },
  ).then(async () => {
    await tableStore.deleteTable(tableId)
    if (activeView.value.type === 'doc' && activeView.value.id === tableId) {
      router.push('/wiki')
    }
  }).catch(() => {})
}

function handleContextCommand(command: string, tableId: string, tableName: string) {
  if (command === 'rename') startRename(tableId, tableName)
  else if (command === 'delete') handleDeleteTable(tableId, tableName)
  else if (command === 'addSubPage') handleAddSubPage(tableId)
}

// Actions
async function handleAddDocument() {
  const name = t('document.untitled')
  const newId = await tableStore.addDocument(name)
  if (newId) {
    await tableStore.loadTableFull(newId)
    router.push(`/wiki/doc/${newId}`)
    emit('selectDoc', newId)
  }
}

async function handleAddSubPage(parentId: string) {
  const name = t('document.untitled')
  const newId = await tableStore.addSubPage(parentId, name)
  if (newId) {
    expandedIds.value = new Set([...expandedIds.value, parentId])
    await tableStore.loadTableFull(newId)
    router.push(`/wiki/doc/${newId}`)
    emit('selectDoc', newId)
  }
}

function handleSelectDoc(tableId: string) {
  if (renamingId.value === tableId) return
  tableStore.activeTableId = tableId
  router.push(`/wiki/doc/${tableId}`)
  emit('selectDoc', tableId)
}

function handleShowSpaces() {
  router.push('/wiki')
  emit('showSpaces')
}

function getNodeIcon(type: SmartItemType) {
  return type === SmartItemType.Document ? FileText : Grid
}
</script>

<template>
  <div
    class="zq-sidebar"
    :class="{ 'is-resizing': isResizing }"
    :style="{ width: `${sidebarWidth}px` }"
  >
    <div class="zq-sidebar__inner" :style="{ width: `${sidebarWidth}px` }">
      <!-- Header -->
      <div class="zq-sidebar__header">
        <div class="zq-sidebar__brand">
          <div class="zq-sidebar__logo">
            <BookOpen class="w-4 h-4" />
          </div>
          <span class="zq-sidebar__brand-title">{{ t('wiki.title') }}</span>
        </div>
      </div>

      <!-- Search -->
      <div class="zq-sidebar__search">
        <Search class="zq-sidebar__search-icon" />
        <input
          v-model="searchText"
          class="zq-sidebar__search-input"
          :placeholder="t('wiki.searchDocPlaceholder')"
        />
      </div>

      <!-- Scrollable content -->
      <div class="zq-sidebar__content">
        <!-- Wiki Spaces entry -->
        <div class="zq-sidebar__section">
          <div
            class="zq-sidebar__section-header"
            :class="{ 'is-active': activeView.type === 'spaces' }"
            @click="handleShowSpaces"
          >
            <BookOpen class="zq-sidebar__section-icon" />
            <span class="zq-sidebar__section-title">{{ t('wiki.wikiSpaces') }}</span>
          </div>
        </div>

        <!-- Divider -->
        <div class="zq-sidebar__divider" />

        <!-- My Documents Section -->
        <div class="zq-sidebar__section">
          <div class="zq-sidebar__section-header zq-sidebar__section-header--docs">
            <FileText class="zq-sidebar__section-icon" />
            <span class="zq-sidebar__section-title">{{ t('wiki.myDocuments') }}</span>
          </div>

          <!-- Document tree -->
          <template v-if="flatList.length > 0">
            <div
              v-for="node in flatList"
              :key="node.id"
              class="zq-sidebar__item"
              :style="{ '--depth': node.depth }"
              :class="[
                {
                  'is-active': activeView.type === 'doc' && activeView.id === node.id,
                  'is-dragging': dragId === node.id,
                },
                getItemDropClass(node.id),
              ]"
              draggable="true"
              @dragstart="onDragStart($event, node.id)"
              @dragover="onDragOver($event, node.id)"
              @dragleave="onDragLeave"
              @drop="onDrop($event)"
              @dragend="resetDrag"
              @click="handleSelectDoc(node.id)"
            >
              <button
                v-if="node.hasChildren"
                class="zq-sidebar__expand-toggle"
                @click.stop="toggleExpand(node.id)"
              >
                <component
                  :is="expandedIds.has(node.id) ? ChevronDown : ChevronRight"
                  class="w-3.5 h-3.5"
                />
              </button>
              <span v-else class="zq-sidebar__expand-spacer" />

              <component :is="getNodeIcon(node.type)" class="zq-sidebar__item-icon" />

              <input
                v-if="renamingId === node.id"
                :ref="setRenameRef"
                v-model="renameValue"
                class="zq-sidebar__rename-input"
                @blur="confirmRename(node.id)"
                @keyup.enter="confirmRename(node.id)"
                @keyup.escape="renamingId = null"
                @click.stop
              />
              <span v-else class="zq-sidebar__item-name">{{ node.name }}</span>

              <el-dropdown
                v-if="renamingId !== node.id"
                trigger="click"
                class="zq-sidebar__item-actions"
                @command="(cmd: string) => handleContextCommand(cmd, node.id, node.name)"
              >
                <button class="zq-sidebar__more-btn" @click.stop>
                  <Ellipsis class="w-3.5 h-3.5" />
                </button>
                <template #dropdown>
                  <el-dropdown-menu>
                    <el-dropdown-item command="addSubPage">
                      <div class="flex items-center gap-2">
                        <FilePlus2 class="w-3.5 h-3.5" />
                        <span>{{ t('sidebar.newSubPage') }}</span>
                      </div>
                    </el-dropdown-item>
                    <el-dropdown-item command="rename">
                      <div class="flex items-center gap-2">
                        <Pencil class="w-3.5 h-3.5" />
                        <span>{{ t('common.rename') }}</span>
                      </div>
                    </el-dropdown-item>
                    <el-dropdown-item command="delete" divided>
                      <div class="flex items-center gap-2" style="color: var(--zq-danger-color)">
                        <Trash2 class="w-3.5 h-3.5" />
                        <span>{{ t('common.delete') }}</span>
                      </div>
                    </el-dropdown-item>
                  </el-dropdown-menu>
                </template>
              </el-dropdown>
            </div>
          </template>

          <!-- Empty my docs -->
          <div v-if="!searchText && flatList.length === 0 && !tableStore.loading" class="zq-sidebar__empty">
            {{ t('wiki.emptyMyDocs') }}
          </div>

          <!-- Search no results -->
          <div v-if="searchText && !hasSearchResults" class="zq-sidebar__no-results">
            <Search class="w-6 h-6" style="color: var(--zq-text-placeholder)" />
            <span>{{ t('wiki.noResults') }}</span>
          </div>
        </div>
      </div>

      <!-- Bottom actions -->
      <div class="zq-sidebar__footer">
        <button class="zq-sidebar__footer-btn" @click="handleAddDocument">
          <FilePlus2 class="w-4 h-4" />
          <span>{{ t('sidebar.newDocument') }}</span>
        </button>
      </div>
    </div>

    <!-- Resize handle -->
    <div
      class="zq-sidebar__resize-handle"
      @mousedown="onResizeStart"
      @dblclick="onResizeDblClick"
    />

  </div>
</template>

<style scoped>
.zq-sidebar {
  position: relative;
  flex-shrink: 0;
  transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1);
  overflow: visible;
  z-index: 20;
}

.zq-sidebar.is-resizing {
  transition: none;
}

.zq-sidebar__inner {
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--zq-bg-sidebar);
  border-right: 1px solid var(--zq-border-color-light);
}

/* Resize handle */
.zq-sidebar__resize-handle {
  position: absolute;
  top: 0;
  right: -3px;
  width: 3px;
  height: 100%;
  cursor: col-resize;
  z-index: 30;
  transition: background-color 0.15s;
}

.zq-sidebar__resize-handle:hover,
.zq-sidebar.is-resizing .zq-sidebar__resize-handle {
  background-color: var(--zq-brand-color);
}

/* Header */
.zq-sidebar__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 12px 8px;
  flex-shrink: 0;
}

.zq-sidebar__brand {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.zq-sidebar__logo {
  width: 30px;
  height: 30px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  background: var(--zq-brand-color);
  flex-shrink: 0;
}

.zq-sidebar__brand-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--zq-text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Search */
.zq-sidebar__search {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 4px 12px 8px;
  padding: 7px 10px;
  border-radius: 8px;
  background: var(--zq-bg-primary);
  border: 1px solid transparent;
  transition: all 0.15s;
}

.zq-sidebar__search:focus-within {
  border-color: var(--zq-brand-color);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--zq-brand-color) 15%, transparent);
}

.zq-sidebar__search-icon {
  width: 14px;
  height: 14px;
  color: var(--zq-text-placeholder);
  flex-shrink: 0;
}

.zq-sidebar__search-input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font-size: 13px;
  color: var(--zq-text-primary);
  line-height: 1;
}

.zq-sidebar__search-input::placeholder {
  color: var(--zq-text-placeholder);
}

/* Scrollable content */
.zq-sidebar__content {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 4px 0 16px;
}

.zq-sidebar__content::-webkit-scrollbar {
  width: 4px;
}

.zq-sidebar__content::-webkit-scrollbar-thumb {
  background: var(--zq-border-color);
  border-radius: 4px;
}

.zq-sidebar__content::-webkit-scrollbar-thumb:hover {
  background: var(--zq-text-placeholder);
}

/* Section */
.zq-sidebar__section {
  padding: 0 8px;
}

.zq-sidebar__section-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  margin: 2px 0;
  border-radius: 6px;
  cursor: pointer;
  font-size: 13px;
  font-weight: 500;
  color: var(--zq-text-secondary);
  transition: all 0.15s;
}

.zq-sidebar__section-header:hover {
  background: var(--zq-bg-cell-hover);
  color: var(--zq-text-primary);
}

.zq-sidebar__section-header.is-active {
  background: var(--zq-brand-color-light);
  color: var(--zq-brand-color);
}

.zq-sidebar__section-header--docs {
  cursor: default;
}

.zq-sidebar__section-header--docs:hover {
  background: transparent;
  color: var(--zq-text-secondary);
}

.zq-sidebar__section-icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}

.zq-sidebar__section-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.zq-sidebar__section-add {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 4px;
  border: none;
  background: transparent;
  color: var(--zq-text-placeholder);
  cursor: pointer;
  transition: all 0.15s;
  flex-shrink: 0;
}

.zq-sidebar__section-add:hover {
  background: var(--zq-bg-cell-hover);
  color: var(--zq-brand-color);
}


/* Divider */
.zq-sidebar__divider {
  height: 1px;
  margin: 6px 12px;
  background: var(--zq-border-color-light);
}

/* Tree items (same as ZqTableSidebar) */
.zq-sidebar__item {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 5px 8px 5px calc(8px + var(--depth, 0) * 16px);
  margin: 1px 0;
  border-radius: 6px;
  cursor: pointer;
  font-size: 13px;
  color: var(--zq-text-primary);
  transition: background-color 0.15s, color 0.15s;
  position: relative;
  user-select: none;
}

.zq-sidebar__item:hover {
  background: var(--zq-bg-cell-hover);
}

.zq-sidebar__item.is-active {
  background: var(--zq-brand-color-light);
  color: var(--zq-brand-color);
}

.zq-sidebar__item.is-active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 6px;
  bottom: 6px;
  width: 3px;
  border-radius: 0 2px 2px 0;
  background: var(--zq-brand-color);
}

.zq-sidebar__item.is-dragging {
  opacity: 0.4;
}

.zq-sidebar__item.drop-inside {
  background: color-mix(in srgb, var(--zq-brand-color) 12%, transparent);
  outline: 2px solid var(--zq-brand-color);
  outline-offset: -2px;
  border-radius: 6px;
}

.zq-sidebar__item.drop-before {
  box-shadow: inset 0 2px 0 0 var(--zq-brand-color);
}

.zq-sidebar__item.drop-after {
  box-shadow: inset 0 -2px 0 0 var(--zq-brand-color);
}

.zq-sidebar__expand-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 4px;
  border: none;
  background: transparent;
  color: var(--zq-text-placeholder);
  cursor: pointer;
  flex-shrink: 0;
  transition: all 0.15s;
}

.zq-sidebar__expand-toggle:hover {
  background: var(--zq-bg-cell-hover);
  color: var(--zq-text-secondary);
}

.zq-sidebar__expand-spacer {
  width: 18px;
  flex-shrink: 0;
}

.zq-sidebar__item-icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  opacity: 0.7;
}

.zq-sidebar__item.is-active .zq-sidebar__item-icon {
  opacity: 1;
}

.zq-sidebar__item-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  line-height: 1.4;
}

.zq-sidebar__rename-input {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  line-height: 1.4;
  padding: 1px 6px;
  border-radius: 4px;
  border: 1px solid var(--zq-brand-color);
  outline: none;
  background: var(--zq-bg-primary);
  color: var(--zq-text-primary);
}

.zq-sidebar__item-actions {
  flex-shrink: 0;
  margin-left: auto;
}

.zq-sidebar__more-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 4px;
  border: none;
  background: transparent;
  color: var(--zq-text-placeholder);
  cursor: pointer;
  opacity: 0;
  transition: all 0.15s;
  flex-shrink: 0;
}

.zq-sidebar__item:hover .zq-sidebar__more-btn {
  opacity: 1;
}

.zq-sidebar__more-btn:hover {
  background: var(--zq-bg-secondary);
  color: var(--zq-text-secondary);
}

/* Empty & No Results */
.zq-sidebar__empty {
  padding: 16px;
  font-size: 12px;
  color: var(--zq-text-placeholder);
  text-align: center;
}

.zq-sidebar__no-results {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 24px 16px;
  font-size: 13px;
  color: var(--zq-text-placeholder);
}

/* Footer */
.zq-sidebar__footer {
  flex-shrink: 0;
  border-top: 1px solid var(--zq-border-color-light);
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.zq-sidebar__footer-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 7px 12px;
  border-radius: 6px;
  border: none;
  background: transparent;
  color: var(--zq-text-secondary);
  font-size: 13px;
  cursor: pointer;
  transition: all 0.15s;
}

.zq-sidebar__footer-btn:hover {
  background: var(--zq-bg-cell-hover);
  color: var(--zq-text-primary);
}

</style>
