<template>
  <div class="page-container">
    <a-row :gutter="20" style="height: 100%">
      <!-- Left: Topology Tree -->
      <a-col :span="6" class="left-panel">
        <div class="panel-header">
          <span>空间结构</span>
          <a-space>
             <a-tooltip content="导出完整空间结构">
                <a-button type="text" @click="handleExport">
                  <template #icon><icon-download /></template>
                </a-button>
             </a-tooltip>
             <a-tooltip content="导入空间 (需以园区为单位)">
                <a-button type="text" @click="handleImportClick">
                  <template #icon><icon-upload /></template>
                </a-button>
             </a-tooltip>
             <a-button type="text" @click="openLocDialog('add')">
                <template #icon><icon-plus /></template>
             </a-button>
          </a-space>
          <input type="file" ref="fileInput" accept=".xlsx, .xls" style="display: none" @change="handleImportFile" />
        </div>
        <a-input v-model="filterText" placeholder="搜索节点..." style="margin-bottom: 10px" allow-clear />
        <a-tree
          :data="treeData"
          :field-names="{ key: 'ID', title: 'Name', children: 'children' }"
          block-node
          default-expand-all
          @select="handleNodeClick"
        >
          <template #title="nodeData">
            <div class="custom-tree-node">
              <span class="node-title">
                <icon-command v-if="nodeData.Type === 'PARK'" />
                <icon-home v-else-if="nodeData.Type === 'BUILDING'" />
                <icon-layers v-else-if="nodeData.Type === 'FLOOR'" />
                <icon-compass v-else-if="nodeData.Type === 'ROAD'" />
                <icon-apps v-else-if="nodeData.Type === 'PUBLIC'" />
                <icon-common v-else />
                <span style="margin-left: 4px">{{ nodeData.Name }}</span>
              </span>
              <span class="node-actions" @click.stop>
                 <a-button type="text" size="mini" @click.stop="openLocDialog('edit', nodeData)">
                    <template #icon><icon-edit /></template>
                 </a-button>
                 <a-button type="text" size="mini" @click.stop="openLocDialog('add_child', nodeData)">
                    <template #icon><icon-plus /></template>
                 </a-button>
                 <a-button type="text" status="danger" size="mini" @click.stop="handleDeleteLoc(nodeData)">
                    <template #icon><icon-delete /></template>
                 </a-button>
              </span>
            </div>
          </template>
        </a-tree>
      </a-col>

      <!-- Right: Device List -->
      <a-col :span="18" class="right-panel">
         <div v-if="currentNode" class="content-wrapper">
             <div style="margin-bottom: 16px">
                <a-breadcrumb separator=">">
                   <a-breadcrumb-item>
                      <icon-location />
                   </a-breadcrumb-item>
                   <a-breadcrumb-item v-for="node in currentBreadcrumb" :key="node.ID">
                      {{ node.Name }}
                   </a-breadcrumb-item>
                </a-breadcrumb>
             </div>
             <div class="table-header">
               <a-typography-title :heading="5">{{ currentNode.Name }} - 关联设备</a-typography-title>
               <a-button type="primary" @click="openDeviceSelect">
                 <template #icon><icon-plus /></template> 添加设备
               </a-button>
             </div>
             <a-table 
                :data="nodeDevices" 
                :loading="loadingDevices" 
                :pagination="{ pageSize: 10 }" 
                :scroll="{ x: '100%', y: '100%' }"
                style="flex: 1; overflow: hidden"
             >
                <template #columns>
                  <a-table-column title="设备名称" data-index="Name" />
                  <a-table-column title="类型" data-index="DeviceType" :width="140">
                    <template #cell="{ record }">
                      {{ getDeviceTypeName(record.DeviceType) }}
                    </template>
                  </a-table-column>
                  <a-table-column title="状态" data-index="Status" :width="100">
                    <template #cell="{ record }">
                       <a-badge v-if="record.Status === 'ONLINE'" status="success" text="在线" />
                       <a-badge v-else status="normal" text="离线" />
                    </template>
                  </a-table-column>
                  <a-table-column title="实时数据" :width="180">
                    <template #cell="{ record }">
                       <div v-if="realtimeDataMap[record.ID]" style="font-size: 12px">
                          <div v-for="(item, idx) in getRealtimeInfo(record)" :key="idx" style="line-height: 1.5; display: flex; justify-content: space-between; width: 100%">
                             <span style="color: var(--color-text-3)">{{ item.label }}:</span>
                             <span style="font-weight: 500; margin-left: 8px">{{ item.value }}</span>
                          </div>
                       </div>
                       <span v-else style="color: var(--color-text-3)">-</span>
                    </template>
                  </a-table-column>
                  <a-table-column title="操作" :width="160">
                    <template #cell="{ record }">
                      <a-space>
                         <a-dropdown v-if="record.DeviceType === 'ELECTRIC' && record.AttributeMapping && record.AttributeMapping.switch">
                            <a-button type="primary" size="mini">
                                <template #icon><icon-thunderbolt /></template>
                            </a-button>
                            <template #content>
                                <a-doption @click="handleDeviceControl(record, 'turn_on')">开启</a-doption>
                                <a-doption @click="handleDeviceControl(record, 'turn_off')">关闭</a-doption>
                            </template>
                         </a-dropdown>
                         <a-button type="text" status="danger" size="mini" @click="removeDevice(record)">移出</a-button>
                      </a-space>
                    </template>
                  </a-table-column>
                </template>
             </a-table>
         </div>
         <a-empty v-else description="请选择左侧空间节点" />
      </a-col>
    </a-row>

    <!-- Location Dialog -->
    <a-modal v-model:visible="locDialogVisible" :title="locDialogTitle" @ok="saveLocation" @cancel="locDialogVisible = false">
      <a-form :model="locForm" layout="vertical">
        <a-form-item field="Name" label="名称">
          <a-input v-model="locForm.Name" allow-clear />
        </a-form-item>
        <a-form-item field="Type" label="类型">
           <a-select v-model="locForm.Type">
             <a-option 
               v-for="opt in typeOptions" 
               :key="opt.value" 
               :label="opt.label" 
               :value="opt.value" 
             />
           </a-select>
        </a-form-item>
      </a-form>
    </a-modal>

    <!-- Device Select Dialog -->
    <a-modal v-model:visible="deviceDialogVisible" title="选择设备加入" @ok="addDevicesToNode" @cancel="deviceDialogVisible = false" width="600px">
       <a-table :data="unassignedDevices" row-key="ID" :row-selection="rowSelection" v-model:selectedKeys="selectedDeviceKeys" :pagination="{ pageSize: 10 }">
          <template #columns>
            <a-table-column title="名称" data-index="Name" />
            <a-table-column title="标识" data-index="ExternalID" />
          </template>
       </a-table>
    </a-modal>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, onMounted, watch, computed } from 'vue';
import { Message, Modal } from '@arco-design/web-vue';
import * as XLSX from 'xlsx';
import { 
  IconPlus, IconEdit, IconDelete, IconCommand, IconHome, IconLayers, IconCommon,
  IconCompass, IconApps, IconDownload, IconUpload, IconLocation, IconThunderbolt
} from '@arco-design/web-vue/es/icon';
import { getLocations, createLocation, updateLocation, deleteLocation, getDevices, updateDevice, callSourceService, getDeviceRealtime } from '@/api/resource';
import type { Location, Device } from '@/api/resource';

// --- State ---
const filterText = ref('');
const treeData = ref<Location[]>([]);
const locationMap = ref<Map<string, Location>>(new Map());
const currentNode = ref<Location | null>(null);
const nodeDevices = ref<Device[]>([]);
const loadingDevices = ref(false);
const realtimeDataMap = ref<Record<string, any[]>>({});

const locDialogVisible = ref(false);
const locDialogTitle = ref('');
const locForm = reactive<Partial<Location>>({ Name: '', Type: 'PARK' });
let editNodeId = '';

const deviceDialogVisible = ref(false);
const unassignedDevices = ref<Device[]>([]);
const selectedDeviceKeys = ref<string[]>([]);
const rowSelection = { type: 'checkbox', showCheckedAll: true };
const fileInput = ref<HTMLInputElement | null>(null);

// --- Hierarchy Rules ---
const hierarchyRules: Record<string, string[]> = {
  'ROOT': ['PARK'],
  'PARK': ['BUILDING', 'ROAD', 'PUBLIC'],
  'BUILDING': ['FLOOR', 'PUBLIC'],
  'FLOOR': ['ROOM', 'PUBLIC'],
  'ROOM': [],
  'ROAD': [],
  'PUBLIC': []
};

const typePriority: Record<string, number> = {
  'PARK': 1,
  'BUILDING': 2,
  'FLOOR': 3,
  'ROOM': 4,
  'ROAD': 4,
  'PUBLIC': 5
};

const typeLabels: Record<string, string> = {
  'PARK': '园区',
  'BUILDING': '建筑',
  'FLOOR': '楼层',
  'ROOM': '房间',
  'ROAD': '道路',
  'PUBLIC': '公共区域'
};

const typeOptions = ref<{ label: string; value: string }[]>([]);

// --- Lifecycle ---
onMounted(() => {
  loadTree();
});

watch(filterText, (val) => {
    if (!val) {
        loadTree();
    } else {
        // Simple filter logic could be implemented here
    }
});

// --- Methods ---
const loadTree = async () => {
  const res = await getLocations();
  const list = (res as any).data || res;

  // Build map for easy parent lookup
  locationMap.value.clear();
  list.forEach((item: Location) => {
    locationMap.value.set(item.ID, item);
  });

  treeData.value = buildTree(list);
};

const currentBreadcrumb = computed(() => {
  if (!currentNode.value) return [];
  const path: Location[] = [];
  let curr: Location | undefined | null = currentNode.value;
  
  // Trace back to root using ParentID and locationMap
  while (curr) {
    path.unshift(curr);
    if (curr.ParentID && locationMap.value.has(curr.ParentID)) {
      curr = locationMap.value.get(curr.ParentID);
    } else {
      curr = null;
    }
  }
  return path;
});

// --- Device Type Helpers ---
const deviceTypeOptions = [
    { label: '电力设备', value: 'ELECTRIC' },
    { label: '水务设备', value: 'WATER' },
    { label: '燃气设备', value: 'GAS' },
    { label: '工业电力设备', value: 'INDUSTRIAL_ELECTRIC' },
];

const getDeviceTypeName = (type: string) => {
    const found = deviceTypeOptions.find(opt => opt.value === type);
    return found ? found.label : type;
};

// --- Export / Import Logic ---

const handleExport = async () => {
    try {
        const res = await getLocations();
        const list = (res as any).data || res;
        
        // 导出包含 ID 和 ParentID 以支持无缝还原
        const dataToExport = list.map((item: Location) => ({
            ID: item.ID,
            Name: item.Name,
            Type: item.Type,
            ParentID: item.ParentID || ''
        }));

        const ws = XLSX.utils.json_to_sheet(dataToExport);
        const wb = XLSX.utils.book_new();
        XLSX.utils.book_append_sheet(wb, ws, "Locations");
        XLSX.writeFile(wb, "space_export.xlsx");
        Message.success('导出成功');
    } catch (e) {
        console.error(e);
        Message.error('导出失败');
    }
};

const handleImportClick = () => {
    if (fileInput.value) fileInput.value.value = ''; // 重置以允许重复选择同名文件
    fileInput.value?.click();
};

const handleImportFile = (event: Event) => {
    const target = event.target as HTMLInputElement;
    if (!target.files || target.files.length === 0) return;
    
    const file = target.files[0];
    const reader = new FileReader();
    
    reader.onload = async (e) => {
        try {
            const data = e.target?.result;
            const workbook = XLSX.read(data, { type: 'array' });
            const firstSheetName = workbook.SheetNames[0];
            const worksheet = workbook.Sheets[firstSheetName];
            const jsonData = XLSX.utils.sheet_to_json(worksheet) as any[];

            if (jsonData.length === 0) {
                Message.warning('文件为空');
                return;
            }

            // --- 校验逻辑：必须以园区为单位 ---
            const fileIdMap = new Set(jsonData.map(item => item.ID));
            // 找出本次导入中没有父级（或父级不在文件内）的节点，这些就是本次导入的"根"
            const fileRoots = jsonData.filter(item => {
                return !item.ParentID || !fileIdMap.has(item.ParentID);
            });

            // 检查这些"根"是否都是 PARK 类型
            const invalidRoots = fileRoots.filter(root => root.Type !== 'PARK');
            if (invalidRoots.length > 0) {
                const names = invalidRoots.map(r => r.Name).join(', ');
                Message.error(`导入验证失败：检测到孤立节点 [${names}]。导入必须以园区 (PARK) 为完整单元，不能单独导入楼层或房间。`);
                return;
            }

            // --- 准备导入 ---
            // 获取现有数据，用于判断是 Create 还是 Update
            let existingIdMap = new Set<string>();
            try {
                 const res = await getLocations();
                 const list = (res as any).data || res;
                 list.forEach((l: Location) => existingIdMap.add(l.ID));
            } catch(e) { /* ignore */ }

            // 按层级排序 (确保先创建父节点)
            jsonData.sort((a, b) => {
                const pA = typePriority[a.Type] || 99;
                const pB = typePriority[b.Type] || 99;
                return pA - pB;
            });

            let successCount = 0;
            let failCount = 0;

            for (const item of jsonData) {
                if (!item.Name || !item.Type) continue;

                // 显式传递 ID 以保证数据一致性
                const payload: any = {
                    ID: item.ID,
                    Name: item.Name,
                    Type: item.Type,
                    ParentID: item.ParentID || null 
                };

                try {
                    if (existingIdMap.has(item.ID)) {
                        await updateLocation(item.ID, payload);
                    } else {
                        await createLocation(payload);
                    }
                    successCount++;
                } catch (err) {
                    console.error(`Import failed for ${item.Name}`, err);
                    failCount++;
                }
            }
            
            Message.success(`导入完成: 成功 ${successCount}，失败 ${failCount}`);
            loadTree();
            
        } catch (e) {
            console.error(e);
            Message.error('文件解析失败');
        }
    };
    reader.readAsArrayBuffer(file);
};

const buildTree = (list: Location[]) => {
    const map = new Map<string, Location>();
    const roots: Location[] = [];
    // Initialize map
    list.forEach(item => {
        map.set(item.ID, { ...item, children: [] });
    });
    // Link
    list.forEach(item => {
        const node = map.get(item.ID)!;
        if (item.ParentID && map.has(item.ParentID)) {
            map.get(item.ParentID)!.children!.push(node);
        } else {
            roots.push(node);
        }
    });
    return roots;
};

const getAllLocationIds = (node: Location): string[] => {
    let ids = [node.ID];
    if (node.children && node.children.length > 0) {
        node.children.forEach(child => {
            ids = ids.concat(getAllLocationIds(child));
        });
    }
    return ids;
};

const loadNodeDevices = async () => {
    if (!currentNode.value) return;
    loadingDevices.value = true;
    try {
        const targetIds = getAllLocationIds(currentNode.value);
        const res = await getDevices();
        const allDevices = (res as any).data || res;
        nodeDevices.value = allDevices.filter((d: Device) => d.LocationID && targetIds.includes(d.LocationID));
        fetchRealtimeData();
    } finally {
        loadingDevices.value = false;
    }
};

const fetchRealtimeData = async () => {
    if (nodeDevices.value.length === 0) return;
    const promises = nodeDevices.value.map(async (dev) => {
        try {
            const res = await getDeviceRealtime(dev.ID);
            realtimeDataMap.value[dev.ID] = (res as any).data || res || [];
        } catch (e) {
            // ignore error
        }
    });
    await Promise.all(promises);
};

const getRealtimeInfo = (record: Device) => {
    const data = realtimeDataMap.value[record.ID];
    if (!data || data.length === 0) return [];

    const getVal = (key: string) => data.find((i: any) => i.metric === key)?.value;
    const result: { label: string; value: string }[] = [];

    // Custom logic based on type
    if (record.DeviceType === 'ELECTRIC') {
        const p = getVal('power');
        const e = getVal('energy');
        if (p !== undefined) result.push({ label: '功率', value: `${p} W` });
        if (e !== undefined) result.push({ label: '电量', value: `${e} kWh` });
    } else if (record.DeviceType === 'WATER') {
        const v = getVal('usage') || getVal('water_consumption');
        if (v !== undefined) result.push({ label: '累计', value: `${v} m³` });
    } else if (record.DeviceType === 'GAS') {
        const v = getVal('usage') || getVal('gas_consumption');
        if (v !== undefined) result.push({ label: '累计', value: `${v} m³` });
    }
    
    // Fallback: show first 2 metrics if no custom logic matched or empty result
    if (result.length === 0) {
        data.slice(0, 2).forEach((i: any) => {
            // Simple mapping for common metrics if not handled above
            let label = i.metric;
            if (label === 'temperature') label = '温度';
            if (label === 'humidity') label = '湿度';
            result.push({ label: label, value: `${i.value}` });
        });
    }
    
    return result;
};

const handleNodeClick = async (_selectedKeys: string[], data: any) => {
  const nodeData = data.node.dataRef || data.node;
  currentNode.value = nodeData;
  await loadNodeDevices();
};

// --- Location CRUD ---
const openLocDialog = (mode: 'add' | 'edit' | 'add_child', data?: Location) => {
    // 1. Calculate allowed types
    let allowedTypes: string[] = [];

    if (mode === 'add') {
        allowedTypes = hierarchyRules['ROOT'];
    } else if (mode === 'add_child') {
        if (!data) return;
        allowedTypes = hierarchyRules[data.Type] || [];
        
        if (allowedTypes.length === 0) {
            Message.warning(`[${typeLabels[data.Type] || data.Type}] 下不允许添加子节点`);
            return;
        }
    } else if (mode === 'edit') {
        allowedTypes = Object.keys(typeLabels);
    }

    // 2. Generate options
    typeOptions.value = allowedTypes.map(t => ({ label: typeLabels[t], value: t }));

    // 3. Open dialog
    locDialogVisible.value = true;
    
    if (mode === 'add') {
        locDialogTitle.value = '新增根节点';
        locForm.Name = '';
        locForm.Type = allowedTypes[0];
        locForm.ParentID = undefined;
        editNodeId = '';
    } else if (mode === 'edit') {
        locDialogTitle.value = '编辑节点';
        Object.assign(locForm, data);
        editNodeId = data!.ID;
    } else if (mode === 'add_child') {
        locDialogTitle.value = `在 [${data!.Name}] 下新增`;
        locForm.Name = '';
        locForm.Type = allowedTypes[0];
        locForm.ParentID = data!.ID;
        editNodeId = '';
    }
};

const saveLocation = async () => {
    try {
        if (editNodeId) {
            await updateLocation(editNodeId, locForm);
        } else {
            await createLocation(locForm);
        }
        Message.success('保存成功');
        locDialogVisible.value = false;
        loadTree();
    } catch (e) {
        Message.error('保存失败');
    }
};

const handleDeleteLoc = (data: Location) => {
    Modal.confirm({
        title: '警告',
        content: `确定删除 [${data.Name}] 及其子节点?`,
        onOk: async () => {
            await deleteLocation(data.ID);
            Message.success('已删除');
            loadTree();
            currentNode.value = null;
        }
    });
};

// --- Device Assignment ---
const openDeviceSelect = async () => {
    deviceDialogVisible.value = true;
    const res = await getDevices();
    const all = (res as any).data || res;
    unassignedDevices.value = all.filter((d: Device) => !d.LocationID);
    selectedDeviceKeys.value = [];
};

const addDevicesToNode = async () => {
    if (!currentNode.value) return;
    for (const id of selectedDeviceKeys.value) {
        await updateDevice(id, { LocationID: currentNode.value.ID });
    }
    Message.success('添加成功');
    deviceDialogVisible.value = false;
    loadingDevices.value = true;
    await loadNodeDevices();
};

const removeDevice = (row: Device) => {
    Modal.confirm({
        title: '警告',
        content: `确定将设备 [${row.Name}] 移出当前空间节点吗?`,
        onOk: async () => {
            await updateDevice(row.ID, { LocationID: null });
            Message.success('已移出');
            if (currentNode.value) {
                await loadNodeDevices();
            }
        }
    });
};

const handleDeviceControl = async (device: Device, action: 'turn_on' | 'turn_off') => {
    if (!device.SourceID || !device.AttributeMapping || !device.AttributeMapping.switch) return;
    
    const entityId = device.AttributeMapping.switch;
    const domain = entityId.split('.')[0];
    
    try {
        await callSourceService(device.SourceID, {
            domain: domain,
            service: action,
            service_data: {
                entity_id: entityId
            }
        });
        Message.success('指令已发送');
    } catch (e) {
        console.error(e);
        Message.error('控制失败');
    }
};

</script>

<style scoped>
.page-container {
  padding: 20px;
  height: calc(100vh - 80px);
}
.left-panel {
    border-right: 1px solid var(--color-border);
    height: 100%;
    display: flex;
    flex-direction: column;
}
.panel-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 10px;
    font-weight: bold;
    color: var(--color-text-1);
}
.custom-tree-node {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-right: 8px;
    width: 100%;
}
.node-title {
    display: flex;
    align-items: center;
}
.node-actions {
    display: none;
    margin-left: 8px;
}
.custom-tree-node:hover .node-actions {
    display: inline-flex;
    gap: 4px;
}
.right-panel {
    padding-left: 20px;
    height: 100%;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.content-wrapper {
    display: flex;
    flex-direction: column;
    height: 100%;
    overflow: hidden;
}
.table-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 15px;
}
</style>
