<script lang="ts" setup>
import { ref, reactive, nextTick, computed } from "vue";
import type { PropType } from "vue";
import BasePageableTable from "../base-pageable-table/BasePageableTable.vue";
import type { SearcherProp } from "../search-bar/SearchBarType";
import type { ToolButtonProp } from "../table-tool-bar/TableToolBarType";
import { get } from "@/common/http/request";
import { Loading, ArrowRight } from "@element-plus/icons-vue";

const props = defineProps({
  searchers: Array as PropType<SearcherProp[]>,
  toolButtons: Array as PropType<ToolButtonProp[]>,
  data: Array,
  url: String,
  parse: Function,

  itemUrl: String,
  itemIdKey: String,
  itemIdName: String,
  itemFieldName: String,
  rowKey: { type: String, default: "id" },
  useAuth: { type: Boolean, default: true },
});

const buttonList = useRoute().meta.toolButtonAuth ?? [];

const hasShowItemAuth = computed(() => {
  if (!props.useAuth) return true;
  return buttonList.some(item => item.eventName === "showItem");
});

interface Emits {
  (e: "expand-change", row: any, expandedRows: any[]): void;
}
const emit = defineEmits<Emits>();

type LocalSlots = {
  columns(): any;
  "tool-button"?: any;
  "item-content": (props: { row: any; itemData: any }) => any;
};
defineSlots<LocalSlots>();

const baseTableRef = ref<InstanceType<typeof BasePageableTable>>();
const loadingItems = reactive<Record<string, boolean>>({});
const itemDataMap = reactive<Record<string, any>>({});
const currentExpandKeys = ref<(string | number)[]>([]);

const getRowKey = (row: any): string => {
  return String(row[props.rowKey]);
};

const loadItemData = async (row: any) => {
  if (props.itemIdKey === undefined) return;
  const rowKey = getRowKey(row);
  if (itemDataMap[rowKey] !== undefined) return;
  if (loadingItems[rowKey]) return;

  if (!props.itemUrl) {
    itemDataMap[rowKey] = null;
    return;
  }

  loadingItems[rowKey] = true;
  try {
    const params = new URLSearchParams();
    params.append(props.itemIdName!, String(row[props.itemIdKey]));
    const res = await get(props.itemUrl, params);
    const resultData = res.data?.records || res.data?.list || res.data || [];
    itemDataMap[rowKey] = resultData;
  } catch (error) {
    console.error(`Failed to load item for ${rowKey}:`, error);
    itemDataMap[rowKey] = [];
  } finally {
    loadingItems[rowKey] = false;
  }
};

/**
 * 核心修改：处理数据加载完成后的清理与同步逻辑
 */
const handleDataLoaded = (newData: any[]) => {
  if (!newData) return;

  // 1. 构建新数据的 Key 映射表，方便快速查找
  // Map<RowKey, RowObject>
  const newRowMap = new Map<string | number, any>();
  // 记录新数据中哪些 Key 是“有效且应展开”的（即拥有 itemFieldName 数据）
  const validExpandKeysInNewData = new Set<string | number>();

  newData.forEach(row => {
    const key = getRowKey(row);
    newRowMap.set(key, row);

    // 如果配置了 itemFieldName 且该行有此数据
    if (props.itemFieldName && row[props.itemFieldName]) {
      validExpandKeysInNewData.add(key);
      // 预加载/更新数据到缓存
      itemDataMap[key] = row[props.itemFieldName];
    }
  });

  // 2. 检查当前已展开的 Keys，找出需要关闭的
  const keysToClose: (string | number)[] = [];

  currentExpandKeys.value.forEach(key => {
    // 情况 A: 新数据中根本没有这一行 (行消失了)
    // 情况 B: 新数据中有这一行，但它不再包含 itemFieldName (标签没了)
    const existsInNewData = newRowMap.has(key);
    const hasValidItem = validExpandKeysInNewData.has(key);

    if (!existsInNewData || !hasValidItem) {
      keysToClose.push(key);

      // 清理缓存 (可选：如果想保留历史缓存可注释掉下一行，但通常建议清理以防数据陈旧)
      delete itemDataMap[key];
    }
  });

  // 3. 执行关闭操作
  if (keysToClose.length > 0) {
    // 3.1 更新响应式数组 (移除需要关闭的 key)
    currentExpandKeys.value = currentExpandKeys.value.filter(k => !keysToClose.includes(k));

    // 3.2 强制 UI 收缩
    nextTick(() => {
      if (!baseTableRef.value) return;

      keysToClose.forEach(key => {
        // 如果行还在新数据中（只是没了明细），我们可以直接拿到 row 对象关闭
        // 如果行不在了，toggleRowExpansion 可能找不到 row，但 UI 上该行已消失，所以不影响
        const rowToClose = newRowMap.get(key);

        if (rowToClose) {
          baseTableRef.value!.toggleRowExpansion(rowToClose, false);
        }
        // 注意：如果 rowToClose 为 undefined (行彻底消失)，el-table 内部会在渲染时自动处理移除，
        // 我们不需要（也无法）调用 toggleRowExpansion。
      });
    });
  }

  // 4. 处理自动展开 (针对新数据中首次出现明细的行)
  // 只有当之前没展开过，现在有了明细，才自动展开
  const keysToOpen: (string | number)[] = [];
  validExpandKeysInNewData.forEach(key => {
    if (!currentExpandKeys.value.includes(key)) {
      keysToOpen.push(key);
    }
  });

  if (keysToOpen.length > 0) {
    currentExpandKeys.value = [...currentExpandKeys.value, ...keysToOpen];

    nextTick(() => {
      if (!baseTableRef.value) return;
      keysToOpen.forEach(key => {
        const rowToOpen = newRowMap.get(key);
        if (rowToOpen) {
          baseTableRef.value!.toggleRowExpansion(rowToOpen, true);
        }
      });
    });
  }
};

const handleExpandChange = (row: any, expandedRows: any[]) => {
  if (!Array.isArray(expandedRows)) {
    console.warn("Unexpected expand-change format");
    return;
  }

  expandedRows.forEach((r: any) => {
    const rowKey = getRowKey(r);
    // 如果是手动展开且没有数据，则加载
    // 注意：这里要排除掉由 itemFieldName 直接提供的数据情况，避免重复逻辑，不过 loadItemData 内部有判重，所以直接调也没事
    if (itemDataMap[rowKey] === undefined && !loadingItems[rowKey]) {
      loadItemData(r);
    }
  });

  // 同步状态
  currentExpandKeys.value = expandedRows.map((r: any) => getRowKey(r));
  emit("expand-change", row, expandedRows);
};

const wrappedParse = (rawData: any) => {
  let result = rawData;
  if (props.parse) {
    result = props.parse(rawData);
  }
  return result;
};

defineExpose({
  reload: () => {
    baseTableRef.value?.reload();
  },
});
</script>

<template>
  <BasePageableTable
    ref="baseTableRef"
    :searchers="searchers"
    :tool-buttons="toolButtons"
    :url="url"
    :data="data"
    :parse="wrappedParse"
    :row-key="rowKey"
    :expand-row-keys="currentExpandKeys as (string | number)[]"
    @data-loaded="handleDataLoaded"
    @expand-change="handleExpandChange"
  >
    <template #tool-button>
      <slot name="tool-button"></slot>
    </template>

    <template #columns>
      <el-table-column v-if="(itemUrl || itemFieldName) && hasShowItemAuth" type="expand" fixed="left" width="50">
        <template #default="{ row, expanded }">
          <div :class="['item-container', { 'is-expanded': expanded }]">
            <div v-if="loadingItems[getRowKey(row)]" class="loading-state">
              <div class="loading-content">
                <el-icon class="is-loading loading-icon"><Loading /></el-icon>
                <span class="loading-text">加载中...</span>
              </div>
            </div>

            <div v-else-if="itemDataMap[getRowKey(row)] !== undefined">
              <slot name="item-content" :row="row" :item-data="itemDataMap[getRowKey(row)]">
                <div v-if="!itemDataMap[getRowKey(row)]" class="empty-state">
                  <el-empty description="暂无明细数据" :image-size="60" />
                </div>
                <pre v-else class="json-preview">{{ JSON.stringify(itemDataMap[getRowKey(row)], null, 2) }}</pre>
              </slot>
            </div>

            <div v-else class="expand-hint">
              <el-icon class="hint-icon"><ArrowRight /></el-icon>
              <span class="hint-text">点击展开查看详情</span>
            </div>
          </div>
        </template>
      </el-table-column>

      <slot name="columns"></slot>
    </template>
  </BasePageableTable>
</template>

<style scoped>
.item-container {
  min-height: 50px;
  padding: 16px;
  background-color: var(--el-fill-color-light);
  border-radius: 4px;
  transition: all 0.3s ease;
}

.item-container.is-expanded {
  background-color: var(--el-fill-color);
  box-shadow: inset 0 2px 8px rgb(0 0 0 / 8%);
}

.loading-state {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 80px;
}

.loading-content {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: center;
}

.loading-icon {
  font-size: 24px;
  color: var(--el-color-primary);
}

.loading-text {
  font-size: 13px;
  color: #909399;
}

.expand-hint {
  display: flex;
  gap: 8px;
  align-items: center;
  justify-content: center;
  min-height: 60px;
  font-size: 13px;
  color: var(--el-text-color-placeholder);
}

.hint-icon {
  font-size: 14px;
  transition: transform 0.3s ease;
}

.expand-hint:hover .hint-icon {
  color: var(--el-color-primary);
  transform: translateX(4px);
}

.hint-text {
  transition: color 0.3s ease;
}

.expand-hint:hover .hint-text {
  color: var(--el-color-primary);
}

.empty-state {
  padding: 20px 0;
}

.json-preview {
  max-height: 400px;
  padding: 12px;
  overflow: auto;
  font-size: 12px;
  line-height: 1.6;
  color: var(--el-text-color-regular);
  background-color: var(--el-fill-color-light);
  border: 1px solid var(--el-border-color-light);
  border-radius: 4px;
}

:deep(.el-table__expand-icon) {
  font-size: 16px;
  color: #909399;
  transition: all 0.2s ease;
}

:deep(.el-table__expand-icon:hover) {
  color: var(--el-color-primary);
  transform: scale(1.1);
}

:deep(.el-table__expand-icon--expanded) {
  transform: rotate(90deg);
}
</style>
