<template>
    <div :class="['graph-container', { 'fullscreen': isFullscreen }]" ref="content">
        <div class="graph-toolbar">
            <button class="graph-btn" @click="zoomIn">放大</button>
            <button class="graph-btn" @click="zoomOut">缩小</button>
            <button class="graph-btn" @click="resetZoom">1:1</button>
            <button class="graph-btn" @click="toggleFullscreen">{{ isFullscreen ? '退出全屏' : '全屏' }}</button>
            <input v-model="searchText" @keyup.enter="searchNode" placeholder="搜索节点" class="graph-input" />
            <button class="graph-btn graph-btn-primary" @click="searchNode">搜索</button>
        </div>
        <div :style="{ height: isFullscreen ? '100vh' : '600px', position: 'relative' }" ref="container"></div>
    </div>
</template>

<script>
import G6 from '@antv/g6';

export default {
    props: ['Json'],
    data() {
        return {
            graph: null,
            searchText: '',
            isFullscreen: false,
        }
    },
    mounted() {
        this.$nextTick(() => {
            if (this.Json && this.Json.nodes) {
                this.showSeeksGraph(this.Json);
            }
        });
        window.addEventListener('resize', this.handleResize);
        // 添加全屏事件监听
        document.addEventListener('fullscreenchange', this.handleFullscreenChange);
        document.addEventListener('webkitfullscreenchange', this.handleFullscreenChange);
        document.addEventListener('mozfullscreenchange', this.handleFullscreenChange);
        document.addEventListener('MSFullscreenChange', this.handleFullscreenChange);
    },
    beforeDestroy() {
        window.removeEventListener('resize', this.handleResize);
        // 移除全屏事件监听
        document.removeEventListener('fullscreenchange', this.handleFullscreenChange);
        document.removeEventListener('webkitfullscreenchange', this.handleFullscreenChange);
        document.removeEventListener('mozfullscreenchange', this.handleFullscreenChange);
        document.removeEventListener('MSFullscreenChange', this.handleFullscreenChange);
        if (this.graph) {
            this.graph.destroy();
        }
    },
    watch: {
        Json: {
            immediate: true,
            deep: true,
            handler(newValue, oldValue) {
                this.$nextTick(() => {
                    if (newValue && newValue.nodes && this.$refs.container) {
                        this.showSeeksGraph(newValue);
                    }
                });
            }
        }
    },
    methods: {
        zoomIn() {
            if (this.graph) {
                const curZoom = this.graph.getZoom();
                this.graph.zoomTo(curZoom * 1.2, { x: 0, y: 0 });
            }
        },
        zoomOut() {
            if (this.graph) {
                const curZoom = this.graph.getZoom();
                this.graph.zoomTo(curZoom / 1.2, { x: 0, y: 0 });
            }
        },
        resetZoom() {
            if (this.graph) {
                this.graph.zoomTo(1, { x: 0, y: 0 });
            }
        },
        searchNode() {
            if (!this.graph || !this.searchText) return;
            const nodes = this.graph.getNodes();
            let found = false;
            nodes.forEach(node => {
                const model = node.getModel();
                // 先清除所有高亮
                this.graph.clearItemStates(node, 'searched');
                if (
                    (model.fullLabel && model.fullLabel.indexOf(this.searchText) !== -1) ||
                    (model.label && model.label.indexOf(this.searchText) !== -1) ||
                    (model.id && model.id.indexOf(this.searchText) !== -1)
                ) {
                    this.graph.focusItem(node, true, {
                        easing: 'easeCubic',
                        duration: 600
                    });
                    this.graph.setItemState(node, 'searched', true);
                    found = true;
                }
            });
            if (!found) {
                this.$message && this.$message.warning('未找到该节点');
            }
        },
        toggleFullscreen() {
            if (!this.isFullscreen) {
                this.enterFullscreen();
            } else {
                this.exitFullscreen();
            }
        },
        enterFullscreen() {
            const content = this.$refs.content;
            if (content.requestFullscreen) {
                content.requestFullscreen();
            } else if (content.webkitRequestFullscreen) {
                content.webkitRequestFullscreen();
            } else if (content.mozRequestFullScreen) {
                content.mozRequestFullScreen();
            } else if (content.msRequestFullscreen) {
                content.msRequestFullscreen();
            }
        },
        exitFullscreen() {
            if (document.exitFullscreen) {
                document.exitFullscreen();
            } else if (document.webkitExitFullscreen) {
                document.webkitExitFullscreen();
            } else if (document.mozCancelFullScreen) {
                document.mozCancelFullScreen();
            } else if (document.msExitFullscreen) {
                document.msExitFullscreen();
            }
        },
        handleFullscreenChange() {
            this.isFullscreen = !!(
                document.fullscreenElement ||
                document.webkitFullscreenElement ||
                document.mozFullScreenElement ||
                document.msFullscreenElement
            );
            
            // 全屏状态改变时重新调整图形大小
            this.$nextTick(() => {
                this.handleResize();
            });
        },
        // 自定义分组布局函数
        applyGroupLayout(data) {
            const nodes = data.nodes;
            // const edges = data.edges;
            
            // 按类别分组节点
            const categoryGroups = {};
            nodes.forEach(node => {
                const category = node.category || 'default';
                if (!categoryGroups[category]) {
                    categoryGroups[category] = [];
                }
                categoryGroups[category].push(node);
            });
            
            const categories = Object.keys(categoryGroups);
            const groupSpacing = 400; // 组间距离
            const nodeSpacing = 80; // 组内节点距离
            
            // 计算每个组的布局
            categories.forEach((category, categoryIndex) => {
                const groupNodes = categoryGroups[category];
                const groupSize = Math.ceil(Math.sqrt(groupNodes.length));
                
                // 计算组的中心位置
                const groupCenterX = (categoryIndex % 3) * groupSpacing - groupSpacing;
                const groupCenterY = Math.floor(categoryIndex / 3) * groupSpacing;
                
                // 为组内每个节点分配位置
                groupNodes.forEach((node, nodeIndex) => {
                    const row = Math.floor(nodeIndex / groupSize);
                    const col = nodeIndex % groupSize;
                    
                    const x = groupCenterX + (col - groupSize / 2) * nodeSpacing;
                    const y = groupCenterY + (row - groupSize / 2) * nodeSpacing;
                    
                    // 更新节点位置
                    const nodeData = data.nodes.find(n => n.id === node.id);
                    if (nodeData) {
                        nodeData.x = x;
                        nodeData.y = y;
                    }
                });
            });
            
            return data;
        },
        showSeeksGraph(data) {
            if (this.graph) {
                this.graph.destroy();
            }
            console.log(data);

            const container = this.$refs.container;
            if (!container) return;
            
            // 应用分组布局
            const layoutData = this.applyGroupLayout(JSON.parse(JSON.stringify(data)));
            
            this.graph = new G6.Graph({
                container: container,
                width: container.scrollWidth,
                height: container.scrollHeight || 600,
                fitView: true,
                background: { color: '#f7faff' },
                modes: {
                    default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
                },
                layout: {
                    // type: 'force',
                    // preventOverlap: true,
                    // nodeSpacing: 30,
                    // linkDistance: 200,
                    // nodeStrength: -60,
                    // edgeStrength: 0.1,
                    type: 'preset', // 使用预设布局，使用我们计算的位置
                },
                defaultNode: {
                    size: 40,
                    labelCfg: {
                        position: 'bottom',
                        style: {
                            fill: '#000',
                            fontSize: 14
                        }
                    },
                    stateStyles: {
                        highlight: {
                            opacity: 1,
                        },
                        inactive: {
                            opacity: 0.15,
                        },
                        searched: {
                            stroke: '#1890ff',
                            lineWidth: 2,
                            shadowColor: '#1890ff',
                            opacity: 1,
                            fill: '#f7faff'
                        }
                    }
                },
                defaultEdge: {
                    type: 'cubic',
                    style: {
                        endArrow: true,
                    },
                    labelCfg: {
                        autoRotate: true,
                    },
                    stateStyles: {
                        highlight: {
                            opacity: 1,
                        },
                        inactive: {
                            opacity: 0.08,
                        }
                    }
                },
                plugins: [
                    new G6.Tooltip({
                        offsetX: 50,
                        offsetY: -500,
                        itemTypes: ['node'],
                        getContent: (e) => {
                            const outDiv = document.createElement('div');
                            outDiv.style.maxWidth = '320px';
                            outDiv.style.wordBreak = 'break-all';
                            const model = e.item.getModel();
                            // 显示完整名称，不受字数限制
                            const fullLabel = model.fullLabel || model.label || model.id || '未命名节点';
                            outDiv.innerHTML = `
                            <div>
                                <div style="margin-bottom: 5px;font-weight: bold;">${fullLabel}</div>
                                <span style="color:#666;font-size:12px;">${model.description || '暂无描述'}</span>
                            </div>`;
                            return outDiv;
                        }
                    })
                ]
            });

            // 层级高亮
            this.graph.on('node:mouseenter', (e) => {
                const node = e.item;
                const graph = this.graph;
                // 先全部虚化
                graph.getNodes().forEach(n => graph.setItemState(n, 'inactive', true));
                graph.getEdges().forEach(edge => graph.setItemState(edge, 'inactive', true));
                // 当前节点高亮
                graph.setItemState(node, 'inactive', false);
                graph.setItemState(node, 'highlight', true);
                // 直接相连的边和节点高亮
                node.getEdges().forEach(edge => {
                    graph.setItemState(edge, 'inactive', false);
                    graph.setItemState(edge, 'highlight', true);
                    const source = edge.getSource();
                    const target = edge.getTarget();
                    [source, target].forEach(n => {
                        graph.setItemState(n, 'inactive', false);
                        graph.setItemState(n, 'highlight', true);
                    });
                });
            });
            
            this.graph.on('node:mouseleave', (e) => {
                const graph = this.graph;
                // 恢复所有状态
                graph.getNodes().forEach(n => {
                    graph.clearItemStates(n);
                });
                graph.getEdges().forEach(edge => {
                    graph.clearItemStates(edge);
                });
            });


            const nodeIds = new Set(layoutData.nodes.map(node => node.id));

            // 统计每个节点的连线数量
            const linkCount = {};
            layoutData.edges.forEach(edge => {
                if (edge.source) linkCount[edge.source] = (linkCount[edge.source] || 0) + 1;
                if (edge.target) linkCount[edge.target] = (linkCount[edge.target] || 0) + 1;
            });

            // 随机颜色函数
            const categoryColors = new Map();
            const getRandomColor = () => {
                const letters = '0123456789ABCDEF';
                let color = '#';
                for (let i = 0; i < 6; i++) {
                    color += letters[Math.floor(Math.random() * 16)];
                }
                return color;
            };

            const getCategoryColor = (category) => {
                if (!category) return '#1890ff'; // 默认蓝色
                if (!categoryColors.has(category)) {
                    categoryColors.set(category, getRandomColor());
                }
                return categoryColors.get(category);
            };

            // 将十六进制颜色转换为rgba格式
            const hexToRgba = (hex, alpha = 0.2) => {
                const r = parseInt(hex.slice(1, 3), 16);
                const g = parseInt(hex.slice(3, 5), 16);
                const b = parseInt(hex.slice(5, 7), 16);
                return `rgba(${r}, ${g}, ${b}, ${alpha})`;
            };

            const graphData = {
                nodes: layoutData.nodes.map(node => {
                    const count = linkCount[node.id] || 0;
                    const size = 20 + count * 10; // 不封顶
                    const categoryColor = getCategoryColor(node.category);
                    const fill = hexToRgba(categoryColor, 0.2);
                    console.log(categoryColor);
                    
                    // 处理标签文本截断
                    const fullLabel = node.label || node.id || '';
                    const maxLength = 8; // 最大显示字数
                    const displayLabel = fullLabel.length > maxLength 
                        ? fullLabel.substring(0, maxLength) + '...' 
                        : fullLabel;
                    
                    // 保留 comboId 字段和位置信息
                    const nodeData = {
                        id: node.id,
                        label: displayLabel, // 使用截断后的标签
                        fullLabel: fullLabel, // 保存完整标签用于tooltip
                        description: node.description,
                        size,
                        x: node.x, // 保留计算的位置
                        y: node.y, // 保留计算的位置
                        style: {
                            fill,
                            stroke: categoryColor,
                            lineWidth: 2
                        }
                    };
                    if (node.comboId) nodeData.comboId = node.comboId;
                    return nodeData;
                }),
                edges: layoutData.edges
                    .filter(edge => {
                        const sourceExists = nodeIds.has(edge.source);
                        const targetExists = nodeIds.has(edge.target);
                        if (!sourceExists || !targetExists) {
                            console.warn('Filtering out edge with non-existent node ID:', edge);
                        }
                        return sourceExists && targetExists;
                    })
                    .map(edge => ({
                        source: edge.source,
                        target: edge.target,
                        label: edge.predicate,
                })),
                combos: Array.isArray(layoutData.combos) ? layoutData.combos : undefined
            };
            console.log('graphData',graphData);
            
            this.graph.data(graphData);
            this.graph.render();

            this.graph.on('node:mouseenter', (e) => {
                this.graph.setItemState(e.item, 'focus', true);
            });

            this.graph.on('node:mouseleave', (e) => {
                this.graph.setItemState(e.item, 'focus', false);
            });
            this.handleResize();
        },
        handleResize() {
            if (!this.graph || this.graph.get('destroyed')) return;
            if (!this.$refs.container) return;
            
            const container = this.$refs.container;
            this.graph.changeSize(container.offsetWidth, container.scrollHeight || 600);
            this.graph.fitView();
        }
    }
}

</script>

<style scoped>
.graph-toolbar {
    display: flex;
    align-items: center;
    margin-bottom: 10px;
    gap: 10px;
}
.graph-btn {
    background: #fff;
    border: 1px solid #3370ff;
    color: #3370ff;
    border-radius: 4px;
    width: 60px;
    height: 30px;
    cursor: pointer;
    font-size: 14px;
    transition: background 0.2s, color 0.2s;
    outline: none;
}
.graph-btn:hover {
    background: #3370ff;
    color: #fff;
}
.graph-btn-primary {
    background: #3370ff;
    border-color: #3370ff;
    color: #fff;
}
.graph-btn-primary:hover {
    background: #3370ff;
    border-color: #3370ff;
    color: #fff;
}
.graph-input {
    padding: 0 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-size: 14px;
    height: 30px;
    outline: none;
}

.graph-container{
    width: 100%;
    height: 100%;
    background-color: #fff;
    padding: 10px;
    box-sizing: border-box;
}
</style>