<script setup lang="ts">
import type { TreeNode } from '../composables/use-connection-tree';

import { computed, onBeforeUnmount, onMounted, ref } from 'vue';

import { EasyFlowButton } from '@easyflow-core/shadcn-ui';

import { Plus, RefreshRight, Search } from '@element-plus/icons-vue';
import { ElIcon, ElInput, ElTooltip } from 'element-plus';

import { formatSourceType } from '../composables/datacenter-constants';
import SourceBrandIcon from './SourceBrandIcon.vue';

const props = defineProps<{
  selectedKey: string;
  treeData: TreeNode[];
}>();

const emit = defineEmits<{
  create: [];
  edit: [node: TreeNode];
  refresh: [];
  remove: [node: TreeNode];
  select: [node: TreeNode];
}>();

const searchText = ref('');
const expandedKeys = ref<Set<string>>(new Set());
const contextMenuVisible = ref(false);
const contextMenuNode = ref<null | TreeNode>(null);
const contextMenuX = ref(0);
const contextMenuY = ref(0);

const filteredTree = computed(() => {
  const keyword = searchText.value.trim().toLowerCase();
  if (!keyword) return props.treeData;
  return props.treeData.filter((node) =>
    node.label.toLowerCase().includes(keyword),
  );
});

function toggleExpand(nodeId: string) {
  if (expandedKeys.value.has(nodeId)) {
    expandedKeys.value.delete(nodeId);
  } else {
    expandedKeys.value.add(nodeId);
  }
}

function handleClick(node: TreeNode) {
  if (node.children && node.children.length > 0) {
    toggleExpand(node.id);
  }
  emit('select', node);
}

function handleContextMenu(event: MouseEvent, node: TreeNode) {
  event.preventDefault();
  if (node.type !== 'source' || node.meta?.builtinFlag) return;
  emit('select', node);
  contextMenuNode.value = node;
  contextMenuX.value = Math.min(event.clientX, window.innerWidth - 156);
  contextMenuY.value = Math.min(event.clientY, window.innerHeight - 108);
  contextMenuVisible.value = true;
}

function isExpanded(nodeId: string) {
  return expandedKeys.value.has(nodeId);
}

function closeContextMenu() {
  contextMenuVisible.value = false;
  contextMenuNode.value = null;
}

function handleEdit() {
  if (contextMenuNode.value) {
    emit('edit', contextMenuNode.value);
  }
  closeContextMenu();
}

function handleRemove() {
  if (contextMenuNode.value) {
    emit('remove', contextMenuNode.value);
  }
  closeContextMenu();
}

function getNodeIcon(node: TreeNode): string {
  if (node.type === 'source') {
    return node.icon === 'file'
      ? 'i-lucide-file-spreadsheet'
      : 'i-lucide-database';
  }
  return 'i-lucide-table';
}

function getSourceMetaLabel(node: TreeNode) {
  const sourceType = node.meta?.sourceType;
  const sourceName = (node.label || '').trim();
  const typeLabel = formatSourceType(sourceType);
  if (!typeLabel || typeLabel === sourceName) {
    return '';
  }
  return typeLabel;
}

function isSourceUnavailable(node: TreeNode) {
  return Boolean(node.type === 'source' && node.meta?.runtimeUnavailable);
}

onMounted(() => {
  window.addEventListener('click', closeContextMenu);
  window.addEventListener('resize', closeContextMenu);
  window.addEventListener('scroll', closeContextMenu, true);
});

onBeforeUnmount(() => {
  window.removeEventListener('click', closeContextMenu);
  window.removeEventListener('resize', closeContextMenu);
  window.removeEventListener('scroll', closeContextMenu, true);
});
</script>

<template>
  <div class="connection-tree">
    <div class="tree-search">
      <div class="search-actions">
        <EasyFlowButton size="sm" class="create-action" @click="emit('create')">
          <ElIcon class="action-icon"><Plus /></ElIcon>
          新增连接
        </EasyFlowButton>
        <EasyFlowButton
          size="sm"
          variant="outline"
          class="refresh-action"
          aria-label="刷新"
          @click="emit('refresh')"
        >
          <ElIcon class="action-icon"><RefreshRight /></ElIcon>
        </EasyFlowButton>
      </div>
      <div class="search-row">
        <ElInput v-model="searchText" placeholder="搜索连接..." clearable>
          <template #prefix>
            <ElIcon><Search /></ElIcon>
          </template>
        </ElInput>
      </div>
    </div>

    <div class="tree-scroll">
      <div v-if="filteredTree.length > 0" class="tree-list">
        <template v-for="source in filteredTree" :key="source.id">
          <div
            class="tree-node tree-node--source"
            :class="{ 'is-selected': selectedKey === source.id }"
            @click="handleClick(source)"
            @contextmenu="handleContextMenu($event, source)"
          >
            <span class="node-media">
              <SourceBrandIcon :source-type="source.meta?.sourceType" />
            </span>
            <span class="node-content">
              <span class="node-label">{{ source.label }}</span>
              <span v-if="getSourceMetaLabel(source)" class="node-meta">
                {{ getSourceMetaLabel(source) }}
              </span>
            </span>
            <ElTooltip
              v-if="isSourceUnavailable(source)"
              content="连接不可用"
              placement="top"
            >
              <span class="node-status-dot" aria-label="连接不可用"></span>
            </ElTooltip>
          </div>

          <!-- 表节点 -->
          <template v-if="isExpanded(source.id) && source.children">
            <div
              v-for="table in source.children"
              :key="table.id"
              class="tree-node tree-node--table"
              :class="{ 'is-selected': selectedKey === table.id }"
              @click="handleClick(table)"
              @contextmenu="handleContextMenu($event, table)"
            >
              <span class="node-icon" :class="[getNodeIcon(table)]"></span>
              <span class="node-label">{{ table.label }}</span>
            </div>
          </template>
        </template>
      </div>

      <div v-else class="tree-empty">
        {{ searchText ? '无匹配连接' : '还没有数据连接' }}
      </div>
    </div>

    <div
      v-if="contextMenuVisible && contextMenuNode"
      class="connection-context-menu"
      :style="{ left: `${contextMenuX}px`, top: `${contextMenuY}px` }"
      @click.stop
    >
      <button type="button" class="context-menu-item" @click="handleEdit">
        编辑
      </button>
      <button
        type="button"
        class="context-menu-item context-menu-item--danger"
        @click="handleRemove"
      >
        删除
      </button>
    </div>
  </div>
</template>

<style scoped>
.connection-tree {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: 0 12px 0 0;
  background: transparent;
}

.tree-search {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 0 0 10px;
}

.search-actions {
  display: flex;
  gap: 6px;
  align-items: center;
}

.search-row :deep(.el-input) {
  width: 100%;
}

.search-row :deep(.el-input__wrapper) {
  min-height: 36px;
}

.action-icon {
  flex-shrink: 0;
  width: 14px;
  height: 14px;
}

.create-action {
  flex: 1;
  padding-inline: 12px;
  box-shadow: none;
}

.refresh-action {
  width: 36px;
  min-width: 36px;
  height: 36px;
  padding-inline: 0;
  color: hsl(var(--text-strong));
  background: hsl(var(--card));
  border-color: hsl(var(--border) / 90%);
  box-shadow: none;
}

.refresh-action:hover {
  color: hsl(var(--foreground));
  background: hsl(var(--surface-subtle));
}

.tree-scroll {
  flex: 1;
  min-height: 0;
  padding-right: 4px;
  overflow: auto;
}

.tree-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 0;
}

.tree-node {
  display: flex;
  gap: 12px;
  align-items: center;
  min-height: 62px;
  padding: 12px 14px;
  color: hsl(var(--foreground));
  cursor: pointer;
  user-select: none;
  background: hsl(var(--card) / 92%);
  border: 1px solid hsl(var(--border) / 52%);
  border-radius: 14px;
  box-shadow: 0 10px 22px -22px hsl(var(--foreground) / 22%);
  transition:
    transform 0.16s,
    background-color 0.16s,
    color 0.16s,
    border-color 0.16s,
    box-shadow 0.16s;
}

.tree-node:hover {
  background: hsl(var(--card));
  border-color: hsl(var(--border) / 74%);
  box-shadow: 0 16px 30px -24px hsl(var(--foreground) / 22%);
  transform: translateY(-1px);
}

.tree-node.is-selected {
  color: hsl(var(--foreground));
  background: hsl(var(--primary) / 8%);
  border-color: hsl(var(--primary) / 24%);
  box-shadow:
    inset 0 0 0 1px hsl(var(--primary) / 12%),
    0 18px 34px -28px hsl(var(--primary) / 24%);
}

.tree-node--source {
  font-weight: 600;
}

.tree-node--table {
  align-items: center;
  margin-left: 20px;
}

.node-arrow {
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  transition: transform 0.15s;
}

.node-arrow.is-expanded {
  transform: rotate(90deg);
}

.arrow-icon {
  width: 12px;
  height: 12px;
  color: hsl(var(--text-muted));
}

.node-media {
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  margin-top: 1px;
  background: hsl(var(--surface-subtle) / 92%);
  border-radius: 11px;
}

.tree-node.is-selected .node-media {
  background: hsl(var(--primary) / 12%);
}

.node-media :deep(svg) {
  width: 22px;
  height: 22px;
}

.node-icon {
  flex-shrink: 0;
  width: 17px;
  height: 17px;
  color: hsl(var(--text-muted));
}

.tree-node.is-selected .node-icon {
  color: hsl(var(--primary));
}

.node-content {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 4px;
  align-items: flex-start;
  min-width: 0;
}

.node-label {
  display: -webkit-box;
  overflow: hidden;
  -webkit-line-clamp: 2;
  font-size: 14px;
  line-height: 1.3;
  letter-spacing: 0.01em;
  white-space: normal;
  -webkit-box-orient: vertical;
}

.node-meta {
  font-size: 12px;
  font-weight: 500;
  line-height: 1.2;
  color: hsl(var(--text-muted));
}

.node-status-dot {
  flex-shrink: 0;
  width: 8px;
  height: 8px;
  margin-left: auto;
  background: hsl(var(--destructive));
  border-radius: 999px;
  box-shadow: 0 0 0 3px hsl(var(--destructive) / 12%);
}

.tree-empty {
  padding: 32px 16px;
  font-size: 13px;
  color: hsl(var(--text-muted));
  text-align: center;
}

.connection-context-menu {
  position: fixed;
  z-index: 30;
  min-width: 136px;
  padding: 6px;
  background: hsl(var(--popover));
  border: 1px solid hsl(var(--border) / 70%);
  border-radius: 12px;
  box-shadow: 0 18px 34px -28px hsl(var(--foreground) / 26%);
  backdrop-filter: blur(10px);
}

.context-menu-item {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 34px;
  padding: 0 10px;
  font-size: 13px;
  color: hsl(var(--foreground));
  text-align: left;
  cursor: pointer;
  background: transparent;
  border: 0;
  border-radius: 8px;
}

.context-menu-item:hover {
  background: hsl(var(--accent) / 86%);
}

.context-menu-item--danger {
  color: hsl(var(--destructive));
}

.context-menu-item--danger:hover {
  background: hsl(var(--destructive) / 10%);
}
</style>
