<x-filament::page>
    <div
        class="space-y-4"
        x-data="knowledgeMindmap()"
        x-init="initMindmap()"
    >
        <div class="rounded-lg border border-gray-200 bg-white p-4 shadow-sm">
            <div class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
                <div class="flex-1">
                    <h2 class="text-lg font-semibold text-gray-900">初中数学知识图谱</h2>
                    <div class="mt-2 flex gap-4 text-xs text-gray-500">
                        <div>知识点总数：<span x-text="stats.nodes"></span></div>
                        <div>已选中学生：<span x-text="$wire.selectedStudentName || '未选择'"></span></div>
                    </div>

                    <!-- 学生选择器 -->
                    <div class="mt-3 grid grid-cols-2 gap-3 max-w-md">
                        <div>
                            <label class="block text-xs font-medium text-gray-700 mb-1">选择老师</label>
                            <select
                                wire:model.live="selectedTeacherId"
                                class="w-full text-sm border border-gray-300 rounded-md px-3 py-1.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
                            >
                                <option value="">请选择老师...</option>
                                @foreach($teachers as $teacher)
                                    <option value="{{ $teacher['teacher_id'] }}">{{ $teacher['name'] }}</option>
                                @endforeach
                            </select>
                        </div>
                        <div>
                            <label class="block text-xs font-medium text-gray-700 mb-1">选择学生</label>
                            <select
                                wire:model.live="selectedStudentId"
                                class="w-full text-sm border border-gray-300 rounded-md px-3 py-1.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
                                {{ empty($selectedTeacherId) ? 'disabled' : '' }}
                            >
                                <option value="">
                                    {{ empty($selectedTeacherId) ? '请先选择老师...' : '请选择学生...' }}
                                </option>
                                @foreach($students as $student)
                                    <option value="{{ $student['student_id'] }}">{{ $student['name'] }}</option>
                                @endforeach
                            </select>
                        </div>
                    </div>
                </div>
                <div class="flex flex-wrap gap-3 text-xs text-gray-600">
                    <span class="inline-flex items-center gap-1">
                        <span class="h-2 w-4 rounded-full bg-blue-500"></span> 前置
                    </span>
                    <span class="inline-flex items-center gap-1">
                        <span class="h-2 w-4 rounded-full bg-red-500"></span> 后继
                    </span>
                    <span class="inline-flex items-center gap-1">
                        <span class="h-2 w-4 rounded-full border border-dashed border-gray-500"></span> 兄弟
                    </span>
                    <span class="inline-flex items-center gap-1">
                        <span class="h-2 w-4 rounded-full bg-yellow-400"></span> 联合考查
                    </span>
                    <span class="inline-flex items-center gap-1 ml-2 border-l border-gray-300 pl-2">
                        <span class="h-2 w-4 rounded-full" style="background: linear-gradient(90deg, #ef4444 0%, #22c55e 100%);"></span> 掌握度
                    </span>
                    <span class="inline-flex items-center gap-1 text-xs">
                        <span class="text-red-500">●</span> < 60% (薄弱)
                    </span>
                    <span class="inline-flex items-center gap-1 text-xs">
                        <span class="text-yellow-500">●</span> 60-85% (良好)
                    </span>
                    <span class="inline-flex items-center gap-1 text-xs">
                        <span class="text-green-500">●</span> > 85% (优秀)
                    </span>
                    <span class="inline-flex items-center gap-1 text-xs">
                        <span class="text-yellow-400 text-sm">★</span> 大师级
                    </span>
                </div>
            </div>
        </div>

        <div
            wire:ignore
            id="knowledge-mindmap"
            class="knowledge-mindmap-container h-[80vh] min-h-[720px] w-full rounded-lg border border-gray-200 overflow-hidden relative"
        ></div>

        <!-- Drawer Component -->
        <div 
            x-show="$wire.drawerOpen" 
            x-transition:enter="transition ease-out duration-300"
            x-transition:enter-start="translate-x-full"
            x-transition:enter-end="translate-x-0"
            x-transition:leave="transition ease-in duration-200"
            x-transition:leave-start="translate-x-0"
            x-transition:leave-end="translate-x-full"
            class="fixed inset-y-0 right-0 w-96 bg-white shadow-2xl z-50 overflow-y-auto"
            style="display: none;"
        >
            <div class="p-6">
                <!-- Header -->
                <div class="flex items-center justify-between mb-6">
                    <h3 class="text-lg font-bold text-gray-900">知识点详情</h3>
                    <button 
                        wire:click="closeDrawer"
                        class="text-gray-400 hover:text-gray-600 transition"
                    >
                        <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
                        </svg>
                    </button>
                </div>

                @if(!empty($nodeDetails))
                    <!-- Node Info -->
                    <div class="mb-6">
                        <h4 class="text-xl font-semibold text-gray-800 mb-2">{{ $nodeDetails['name'] ?? '' }}</h4>
                        <p class="text-sm text-gray-500">编号: {{ $nodeDetails['code'] ?? '' }}</p>
                    </div>

                    <!-- Mastery Progress -->
                    <div class="mb-6 p-4 bg-gradient-to-br from-blue-50 to-indigo-50 rounded-lg">
                        <div class="flex justify-between items-center mb-2">
                            <span class="text-sm font-medium text-gray-700">掌握度</span>
                            <span class="text-lg font-bold text-indigo-600">{{ number_format(($nodeDetails['mastery_level'] ?? 0) * 100, 1) }}%</span>
                        </div>
                        <div class="w-full bg-gray-200 rounded-full h-3 overflow-hidden">
                            <div 
                                class="h-full rounded-full transition-all duration-500"
                                style="width: {{ ($nodeDetails['mastery_level'] ?? 0) * 100 }}%; 
                                       background: linear-gradient(90deg, #ef4444 0%, #eab308 50%, #22c55e 100%);"
                            ></div>
                        </div>
                        <div class="mt-3 grid grid-cols-2 gap-2 text-xs">
                            <div>
                                <span class="text-gray-600">练习次数:</span>
                                <span class="font-semibold ml-1">{{ $nodeDetails['total_attempts'] ?? 0 }}</span>
                            </div>
                            <div>
                                <span class="text-gray-600">错误率:</span>
                                <span class="font-semibold ml-1 text-red-600">{{ number_format(($nodeDetails['error_rate'] ?? 0) * 100, 1) }}%</span>
                            </div>
                        </div>
                    </div>

                    <!-- Prerequisites -->
                    @if(!empty($nodeDetails['prerequisites']))
                        <div class="mb-6">
                            <h5 class="text-sm font-semibold text-gray-700 mb-3">前置知识</h5>
                            <div class="space-y-2">
                                @foreach($nodeDetails['prerequisites'] as $prereq)
                                    <div class="flex items-center justify-between p-2 bg-gray-50 rounded hover:bg-gray-100 cursor-pointer transition">
                                        <span class="text-sm text-gray-700">{{ $prereq['name'] }}</span>
                                        <span class="text-xs px-2 py-1 rounded-full {{ $prereq['mastery'] >= 0.6 ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700' }}">
                                            {{ number_format($prereq['mastery'] * 100, 0) }}%
                                        </span>
                                    </div>
                                @endforeach
                            </div>
                        </div>
                    @endif

                    <!-- Successors -->
                    @if(!empty($nodeDetails['successors']))
                        <div class="mb-6">
                            <h5 class="text-sm font-semibold text-gray-700 mb-3">后续知识</h5>
                            <div class="space-y-2">
                                @foreach($nodeDetails['successors'] as $succ)
                                    <div class="flex items-center justify-between p-2 bg-gray-50 rounded hover:bg-gray-100 cursor-pointer transition">
                                        <span class="text-sm text-gray-700">{{ $succ['name'] }}</span>
                                        <span class="text-xs px-2 py-1 rounded-full {{ $succ['mastery'] >= 0.6 ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700' }}">
                                            {{ number_format($succ['mastery'] * 100, 0) }}%
                                        </span>
                                    </div>
                                @endforeach
                            </div>
                        </div>
                    @endif

                    <!-- Recommendations -->
                    @if(!empty($nodeDetails['recommendations']))
                        <div class="mb-6">
                            <h5 class="text-sm font-semibold text-gray-700 mb-3">推荐练习</h5>
                            <div class="space-y-3">
                                @foreach($nodeDetails['recommendations'] as $rec)
                                    <div class="p-3 border border-gray-200 rounded-lg hover:border-indigo-300 transition">
                                        <div class="flex items-start justify-between mb-1">
                                            <span class="text-sm font-medium text-gray-800">{{ $rec['title'] }}</span>
                                            <span class="text-xs px-2 py-0.5 rounded-full 
                                                {{ $rec['difficulty'] === '简单' ? 'bg-green-100 text-green-700' : '' }}
                                                {{ $rec['difficulty'] === '中等' ? 'bg-yellow-100 text-yellow-700' : '' }}
                                                {{ $rec['difficulty'] === '困难' ? 'bg-red-100 text-red-700' : '' }}
                                            ">{{ $rec['difficulty'] }}</span>
                                        </div>
                                        <span class="text-xs text-gray-500">{{ $rec['type'] }}</span>
                                    </div>
                                @endforeach
                            </div>
                        </div>
                    @endif

                    <!-- Action Button -->
                    <button class="w-full py-3 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold rounded-lg transition shadow-md">
                        开始练习
                    </button>
                @endif
            </div>
        </div>

        <!-- Drawer Overlay -->
        <div 
            x-show="$wire.drawerOpen" 
            @click="$wire.closeDrawer()"
            x-transition:enter="transition ease-out duration-300"
            x-transition:enter-start="opacity-0"
            x-transition:enter-end="opacity-100"
            x-transition:leave="transition ease-in duration-200"
            x-transition:leave-start="opacity-100"
            x-transition:leave-end="opacity-0"
            class="fixed inset-0 bg-black bg-opacity-30 z-40"
            style="display: none;"
        ></div>
    </div>

    <style>
        .knowledge-mindmap-container {
            background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 50%, #60a5fa 100%);
            position: relative;
        }
        
        .knowledge-mindmap-container::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: 
                radial-gradient(ellipse at 20% 30%, rgba(255,255,255,0.1) 0%, transparent 50%),
                radial-gradient(ellipse at 80% 70%, rgba(255,255,255,0.08) 0%, transparent 50%);
            pointer-events: none;
        }
    </style>
</x-filament::page>

@push('scripts')
    <script src="https://gw.alipayobjects.com/os/lib/antv/g6/5.0.18/dist/g6.min.js"></script>
    <script src="{{ asset('js/g6-custom-node.js') }}"></script>
    <script src="{{ asset('js/knowledge-mindmap-graph.js') }}"></script>
    <script>
        document.addEventListener('alpine:init', () => {
            window.knowledgeMindmap = () => ({
                graphInstance: null,
                stats: { nodes: 0, extraEdges: 0 },

                async initMindmap() {
                    try {
                        if (!window.G6) {
                            console.error('G6 未加载');
                            return;
                        }

                        this.graphInstance = new KnowledgeMindmapGraph();
                        await this.graphInstance.init();
                        
                        this.stats = this.graphInstance.stats;
                    } catch (err) {
                        console.error('初始化思维导图失败', err);
                    }
                },
            });
        });
    </script>
@endpush

                graph: null,
                treeData: null,
                relationEdges: [],
                stats: { nodes: 0, extraEdges: 0 },

                // 学生选择相关
                // 学生选择相关 - 现在由Livewire管理
                masteryData: {}, // 存储掌握度数据 { 'F01': 80, 'F02': 65, ... }


                arrow(w = 12, h = 14, r = 5) {
                    if (window.G6?.Arrow?.triangle) {
                        return G6.Arrow.triangle(w, h, r);
                    }
                    return [
                        ['M', 0, 0],
                        ['L', w, h / 2],
                        ['L', 0, h],
                        ['Z'],
                    ];
                },
                levelStyles: [
                    {
                        fill: '#0ea5e9',
                        stroke: '#0369a1',
                        labelColor: '#0f172a',
                        fontSize: 17,
                        fontWeight: 700,
                        size: 34,
                    },
                    {
                        fill: '#e0f2fe',
                        stroke: '#38bdf8',
                        labelColor: '#0f172a',
                        fontSize: 16,
                        fontWeight: 700,
                        size: 30,
                    },
                    {
                        fill: '#f1f5f9',
                        stroke: '#cbd5e1',
                        labelColor: '#0f172a',
                        fontSize: 14,
                        fontWeight: 600,
                        size: 26,
                    },
                ],
                relationStyles: {
                    prerequisite: {
                        type: 'quadratic',
                        curveOffset: 60,
                        style: {
                            stroke: '#2563eb',
                            lineWidth: 3.4,
                            lineDash: [8, 6],
                            endArrow: {
                                path: null,
                                fill: '#2563eb',
                                d: 12,
                            },
                            startArrow: false,
                        },
                        label: '前置',
                    },
                    successor: {
                        type: 'quadratic',
                        curveOffset: 60,
                        style: {
                            stroke: '#dc2626',
                            lineWidth: 3.4,
                            lineDash: [8, 6],
                            endArrow: {
                                path: null,
                                fill: '#dc2626',
                                d: 12,
                            },
                            startArrow: false,
                        },
                        label: '后继',
                    },
                    sibling: {
                        type: 'quadratic',
                        curveOffset: 50,
                        style: {
                            stroke: '#64748b',
                            lineDash: [6, 6],
                            lineWidth: 3,
                            endArrow: {
                                path: null,
                                fill: '#64748b',
                                d: 10,
                            },
                        },
                        label: '兄弟',
                    },
                    joint: {
                        type: 'quadratic',
                        curveOffset: 50,
                        style: {
                            stroke: '#fcd34d',
                            lineWidth: 3,
                            lineDash: [10, 8],
                            endArrow: {
                                path: null,
                                fill: '#fbbf24',
                                d: 10,
                            },
                        },
                        label: '联合',
                    },
                    default: {
                        type: 'quadratic',
                        curveOffset: 50,
                        style: {
                            stroke: '#94a3b8',
                            lineWidth: 3,
                            lineDash: [10, 8],
                            endArrow: {
                                path: null,
                                fill: '#94a3b8',
                                d: 10,
                            },
                        },
                        label: '',
                    },
                },
                async initMindmap() {
                    try {
                        if (this.$nextTick) {
                            await this.$nextTick();
                        }
                        if (!window.G6) {
                            console.error('G6 未加载');
                            return;
                        }
                        Object.keys(this.relationStyles).forEach((key) => {
                            const rel = this.relationStyles[key];
                            if (rel?.style && rel.style.endArrow && !rel.style.endArrow.path) {
                                rel.style.endArrow.path = this.arrow(rel.style.endArrow.d || 10, (rel.style.endArrow.d || 10) + 2, 4);
                            }
                        });
                        await Promise.all([
                            this.loadData(),
                        ]);
                        this.applyInitialCollapse(this.treeData);
                        this.renderGraph();
                        window.addEventListener('resize', () => this.resizeGraph());

                        // 监听 Livewire 事件
                        window.addEventListener('mastery-updated', (event) => {
                            console.log('Mastery updated:', event.detail.data);
                            this.masteryData = event.detail.data || {};
                            this.refreshGraph();
                        });
                    } catch (err) {
                        console.error('初始化思维导图失败', err);
                    }
                },
                // loadTeachers, loadStudents, loadMasteryData 已移除，由 Livewire 处理
                async loadData() {
                    const [treeResp, edgesResp] = await Promise.all([
                        fetch('/data/tree.json'),
                        fetch('/data/edges.json'),
                    ]);
                    const rawTree = await treeResp.json();
                    const edges = await edgesResp.json();
                    const rawEdges = Array.isArray(edges) ? edges : edges?.edges || [];
                    this.treeData = this.transformNode(rawTree);
                    this.relationEdges = this.normalizeEdges(rawEdges);
                    this.stats = {
                        nodes: this.countNodes(this.treeData),
                        extraEdges: this.relationEdges.length,
                    };
                },
                refreshGraph() {
                    if (this.graph && this.treeData) {
                        // 重新装饰树数据以应用掌握度
                        const decoratedData = this.decorateTree(this.treeData);
                        this.graph.changeData(decoratedData);
                        this.graph.render();
                        this.graph.fitView(24);
                    }
                },
                transformNode(node, depth = 0) {
                    if (!node) {
                        return null;
                    }
                    const id = node.code || node.id || node.label || `node-${Math.random().toString(36).slice(2, 8)}`;
                    const label = node.name || node.label || node.code || node.id || '未命名节点';

                    // 优先使用动态掌握度数据，其次回退到静态数据
                    const dynamicMastery = this.masteryData[id] || this.masteryData[node.code] || 0;
                    const staticMastery = node.mastery_level || 0;

                    const meta = {
                        code: node.code || node.id || '',
                        name: label,
                        direct_score: node.direct_score || [],
                        related_score: node.related_score || [],
                        skills: node.skills || [],
                        mastery_level: dynamicMastery || staticMastery, // 动态掌握度优先
                    };
                    return {
                        id,
                        label,
                        meta,
                        depth,
                        children: (node.children || []).map((child) => this.transformNode(child, depth + 1)).filter(Boolean),
                    };
                },
                applyInitialCollapse(node, depth = 0) {
                    if (!node) {
                        return;
                    }
                    if (depth >= 2 && node.children.length > 0) {
                        node.collapsed = true;
                    }
                    node.children.forEach((child) => this.applyInitialCollapse(child, depth + 1));
                },
                countNodes(node) {
                    if (!node) {
                        return 0;
                    }
                    return 1 + node.children.reduce((sum, child) => sum + this.countNodes(child), 0);
                },
                normalizeEdges(rawEdges) {
                    const seen = new Set();
                    const normalized = [];
                    (rawEdges || []).forEach((edge, index) => {
                        if (!edge?.source || !edge?.target) {
                            return;
                        }
                        const key = `${edge.source}-${edge.target}-${edge.type}`;
                        if (seen.has(key)) {
                            return;
                        }
                        seen.add(key);
                        const relationStyle = this.relationStyles[edge.type] || this.relationStyles.default;
                        normalized.push({
                            id: `rel-${index}-${edge.source}-${edge.target}`,
                            source: edge.source,
                            target: edge.target,
                            type: relationStyle.type || 'quadratic',
                            curveOffset: relationStyle.curveOffset || 50,
                            style: relationStyle.style,
                            label: relationStyle.label,
                            comment: edge.comment || edge.note || '',
                        });
                    });
                    return normalized;
                },
                renderGraph(containerEl = null) {
                    if (!this.treeData) {
                        return;
                    }
                    const container = containerEl || document.getElementById('knowledge-mindmap');
                    if (!container) {
                        console.error('容器未找到');
                        return;
                    }
                    const ensuredId = container.id || 'knowledge-mindmap';
                    if (!container.id) {
                        container.id = ensuredId;
                    }
                    const bounds = container.getBoundingClientRect();
                    const width = Math.max(bounds.width, 600);
                    const height = Math.max(bounds.height, 600);
                    const tooltipEl = document.createElement('div');
                    tooltipEl.className = 'fixed z-50 pointer-events-none hidden';
                    document.body.appendChild(tooltipEl);
                    const showTooltip = (html, x, y) => {
                        tooltipEl.innerHTML = html;
                        tooltipEl.style.left = `${x + 12}px`;
                        tooltipEl.style.top = `${y + 12}px`;
                        tooltipEl.classList.remove('hidden');
                    };
                    const hideTooltip = () => {
                        tooltipEl.classList.add('hidden');
                        tooltipEl.innerHTML = '';
                    };
                    const G6Lib = window.G6?.default || window.G6;
                    const TreeGraphClass = G6Lib?.TreeGraph || null;
                    if (!TreeGraphClass) {
                        console.error('G6 TreeGraph 不可用');
                        return;
                    }
                    const graphData = this.decorateTree(this.treeData);
                    const graphConfig = {
                        container: ensuredId,
                        width,
                        height,
                        data: graphData,
                        linkCenter: true,
                        modes: {
                            default: [
                                'drag-canvas',
                                'zoom-canvas',
                                {
                                    type: 'collapse-expand',
                                    trigger: 'click',
                                    onChange: function onChange(item, collapsed) {
                                        if (!item) return;
                                        item.getModel().collapsed = collapsed;
                                        return true;
                                    },
                                },
                            ],
                        },
                        layout: {
                            type: 'mindmap',
                            direction: 'H',
                            getHeight: () => 32,
                            getWidth: () => 140,
                            getVGap: () => 32,
                            getHGap: () => 110,
                        },
                        defaultNode: {
                            size: 22,
                            style: {
                                stroke: '#94a3b8',
                                fill: '#fff',
                                radius: 4,
                                shadowColor: undefined,
                                shadowBlur: 0,
                                lineWidth: 3,
                            },
                            labelCfg: {
                                style: {
                                    fontSize: 13,
                                    fill: '#0f172a',
                                    fontWeight: 500,
                                },
                                position: 'right',
                                offset: 12,
                            },
                        },
                        defaultEdge: {
                            type: 'cubic-horizontal',
                            style: {
                                stroke: '#cbd5f5',
                                lineWidth: 3,
                                shadowBlur: 0,
                                shadowColor: undefined,
                            },
                        },
                        nodeStateStyles: {
                            selected: {
                                lineWidth: 3.2,
                                stroke: '#2563eb',
                                fill: '#e0f2fe',
                            },
                        },
                        edgeStateStyles: {
                            highlight: {
                                lineWidth: 3.4,
                                stroke: '#fb923c',
                            },
                        },
                        plugins: [],
                    };
                    this.graph = new TreeGraphClass(graphConfig);
                    if (typeof this.graph.data === 'function') {
                        this.graph.data(graphData);
                    } else if (typeof this.graph.changeData === 'function') {
                        this.graph.changeData(graphData);
                    }
                    if (typeof this.graph.render === 'function') {
                        this.graph.render();
                    }
                    this.graph.data(this.decorateTree(this.treeData));
                    this.graph.render();
                    this.graph.fitView(24);
                    this.drawRelationEdges();
                    this.bindEvents();
                    this.graph.on('node:mouseenter', (evt) => {
                        const { clientX, clientY } = evt;
                        showTooltip(this.buildTooltip(evt?.item?.getModel()), clientX, clientY);
                    });
                    this.graph.on('node:mouseleave', hideTooltip);
                    this.graph.on('edge:mouseenter', (evt) => {
                        const model = evt?.item?.getModel() || {};
                        const relation = model.label || '关联关系';
                        const text = `${model.source || ''} → ${model.target || ''}`;
                        const comment = model.comment ? `<div class="text-[11px] text-gray-600 mt-1 whitespace-pre-line">${model.comment}</div>` : '';
                        const html = `
                            <div class="rounded-md border border-gray-200 bg-white px-3 py-2 text-xs text-gray-700 shadow-md">
                                <div class="font-semibold text-gray-900 mb-1">${relation}</div>
                                <div>${text}</div>
                                ${comment}
                            </div>
                        `;
                        const { clientX, clientY } = evt;
                        showTooltip(html, clientX, clientY);
                    });
                    this.graph.on('edge:mouseleave', hideTooltip);
                    const canvas = this.graph && typeof this.graph.get === 'function' ? this.graph.get('canvas') : null;
                    if (canvas && typeof canvas.set === 'function') {
                        canvas.set('localRefresh', false);
                        const ctx = typeof canvas.get === 'function' ? canvas.get('context') : null;
                        if (ctx) {
                            ctx.shadowColor = 'transparent';
                            ctx.shadowBlur = 0;
                        }
                    }
                },
                decorateTree(node) {
                    if (!node) {
                        return null;
                    }

                    // 动态获取最新掌握度数据对象
                    const id = node.id;
                    const code = node.meta?.code;
                    // masteryData 现在是对象 { 'KP_CODE': { mastery_level: 0.8, total_attempts: 5, ... } }
                    const masteryInfo = this.masteryData[id] || (code && this.masteryData[code]) || null;
                    
                    const masteryLevel = masteryInfo ? (masteryInfo.mastery_level || 0) : (node.meta.mastery_level || 0);
                    const totalAttempts = masteryInfo ? (masteryInfo.total_attempts || 0) : 0;

                    const { nodeStyle, labelCfg, size, icon } = this.getNodeLevelStyle(node.depth, masteryLevel, totalAttempts);
                    
                    // 构建带图标的标签
                    let label = `${node.meta.code ? `${node.meta.code} · ` : ''}${node.label}`;
                    if (icon) {
                        label += ` ${icon}`;
                    }

                    // 更新meta中的掌握度，以便tooltip使用
                    const meta = { 
                        ...node.meta, 
                        mastery_level: masteryLevel,
                        total_attempts: totalAttempts,
                        mastery_info: masteryInfo 
                    };

                    return {
                        id: node.id,
                        label: label,
                        meta: meta,
                        collapsed: node.collapsed,
                        depth: node.depth,
                        size,
                        style: nodeStyle,
                        labelCfg,
                        children: node.children.map((child) => this.decorateTree(child)).filter(Boolean),
                    };
                },
                getNodeLevelStyle(depth = 0, masteryLevel = 0, totalAttempts = 0) {
                    const style = this.levelStyles[depth] || this.levelStyles[this.levelStyles.length - 1];

                    // 根据掌握度调整颜色和样式
                    let fillColor, strokeColor, shadowColor, shadowBlur, icon;
                    
                    // 只要有答题记录（totalAttempts > 0），即使掌握度为0，也视为"薄弱"（红色）
                    // 如果没有答题记录，则保持默认样式（白色）
                    const hasAttempts = totalAttempts > 0;

                    if (masteryLevel >= 85) {
                        // 85%以上：大师级（绿色 + 光晕 + 星星）
                        fillColor = '#dcfce7'; // green-100
                        strokeColor = '#16a34a'; // green-600
                        shadowColor = 'rgba(34, 197, 94, 0.6)';
                        shadowBlur = 10;
                        icon = '★';
                    } else if (masteryLevel >= 60) {
                        // 60-85%：良好（黄色）
                        fillColor = '#fef9c3'; // yellow-100
                        strokeColor = '#ca8a04'; // yellow-600
                        shadowColor = undefined;
                        shadowBlur = 0;
                        icon = '';
                    } else if (masteryLevel > 0 || hasAttempts) {
                        // 1-60% 或 掌握度为0但有答题记录：薄弱（红色）
                        fillColor = '#fee2e2'; // red-100
                        strokeColor = '#dc2626'; // red-600
                        shadowColor = undefined;
                        shadowBlur = 0;
                        icon = '';
                    } else {
                        // 未掌握且无记录：默认
                        fillColor = style.fill || '#fff';
                        strokeColor = style.stroke || '#cbd5f5';
                        shadowColor = undefined;
                        shadowBlur = 0;
                        icon = '';
                    }

                    return {
                        size: style.size || 22,
                        icon,
                        nodeStyle: {
                            fill: fillColor,
                            stroke: strokeColor,
                            lineWidth: masteryLevel > 0 ? 3 : 3, // 保持一致线条宽度，靠颜色区分
                            radius: 6,
                            shadowColor: shadowColor,
                            shadowBlur: shadowBlur,
                            cursor: 'pointer',
                        },
                        labelCfg: {
                            position: 'right',
                            offset: 12,
                            style: {
                                fontSize: style.fontSize || 13,
                                fontWeight: style.fontWeight || 600,
                                fill: style.labelColor || '#0f172a',
                            },
                        },
                    };
                },
                drawRelationEdges() {
                    if (!this.graph || !this.relationEdges.length) {
                        return;
                    }
                    this.relationEdges.forEach((edge, index) => {
                        const style = { ...(edge.style || {}), lineAppendWidth: 14 };
                        if (style.endArrow && !style.endArrow.path) {
                            style.endArrow = {
                                ...style.endArrow,
                                path: this.arrow(style.endArrow.d || 10, (style.endArrow.d || 10) + 2, 4),
                            };
                        }
                        this.graph.addItem('edge', {
                            id: `extra-${index}`,
                            source: edge.source,
                            target: edge.target,
                            type: edge.type || 'quadratic',
                            curveOffset: edge.curveOffset || 50,
                            style,
                            label: edge.label,
                            comment: edge.comment || '',
                            labelCfg: {
                                autoRotate: true,
                                style: {
                                    fill: '#475569',
                                    fontSize: 11,
                                    background: {
                                        fill: 'rgba(255,255,255,0.85)',
                                        padding: [2, 4],
                                        radius: 4,
                                    },
                                },
                            },
                        });
                    });
                },
                buildTooltip(model) {
                    const meta = model?.meta;
                    if (!meta) {
                        return '<div class="text-xs text-gray-600">无数据</div>';
                    }
                    const range = (value) => (value?.length ? `${value[0]}-${value[1]}` : '未配置');
                    const mastery = meta.mastery_level || 0;
                    const attempts = meta.total_attempts || 0;
                    
                    // 进度条颜色
                    let progressColorClass = 'bg-gray-300';
                    let masteryColor = '#9ca3af';
                    
                    if (mastery >= 85) {
                        progressColorClass = 'bg-green-500';
                        masteryColor = '#22c55e';
                    } else if (mastery >= 60) {
                        progressColorClass = 'bg-yellow-500';
                        masteryColor = '#eab308';
                    } else if (mastery > 0 || attempts > 0) {
                        progressColorClass = 'bg-red-500';
                        masteryColor = '#ef4444';
                    }

                    const skills = (meta.skills || []).map(s => `<li class="text-[10px] text-gray-600">• ${s}</li>`).join('') || '<li class="text-[10px] text-gray-400 italic">暂无技能要点</li>';

                    // 下一级所需经验（模拟）
                    const nextLevel = mastery >= 100 ? '已满级' : `距离下一级还需 ${Math.max(0, 100 - mastery)} 点`;

                    return `
                        <div class="min-w-[260px] max-w-sm rounded-lg border border-gray-200 bg-white p-4 text-xs text-gray-700 shadow-xl">
                            <div class="flex items-center justify-between mb-2">
                                <div class="text-sm font-bold text-gray-900">${meta.code || model.id} · ${meta.name}</div>
                                ${mastery >= 85 ? '<span class="px-2 py-0.5 rounded-full bg-yellow-100 text-yellow-700 text-[10px] font-bold border border-yellow-200">★ 大师</span>' : ''}
                            </div>
                            
                            <!-- 掌握度进度条 -->
                            <div class="mb-3">
                                <div class="flex justify-between text-[10px] text-gray-500 mb-1">
                                    <span>掌握度 Lv.${Math.floor(mastery / 10)} <span class="text-gray-400 ml-1">(${attempts}次练习)</span></span>
                                    <span class="font-medium" style="color: ${masteryColor}">${mastery}%</span>
                                </div>
                                <div class="h-2 w-full rounded-full bg-gray-100 overflow-hidden">
                                    <div class="h-full rounded-full ${progressColorClass} transition-all duration-500" style="width: ${mastery}%"></div>
                                </div>
                                <div class="mt-1 text-[10px] text-gray-400 text-right">${nextLevel}</div>
                            </div>

                            <div class="grid grid-cols-2 gap-2 mb-3 bg-gray-50 p-2 rounded border border-gray-100">
                                <div>
                                    <div class="text-[10px] text-gray-500">直接得分</div>
                                    <div class="font-medium">${range(meta.direct_score)}</div>
                                </div>
                                <div>
                                    <div class="text-[10px] text-gray-500">关联得分</div>
                                    <div class="font-medium">${range(meta.related_score)}</div>
                                </div>
                            </div>

                            <div>
                                <div class="font-medium mb-1 flex items-center gap-1">
                                    <svg class="w-3 h-3 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>
                                    技能要点
                                </div>
                                <ul class="list-none space-y-1 pl-1">
                                    ${skills}
                                </ul>
                            </div>
                        </div>
                    `;
                },
                bindEvents() {
                    if (!this.graph) {
                        return;
                    }
                    this.graph.on('node:click', (evt) => {
                        const nodeId = evt?.item?.getID();
                        if (nodeId) {
                            this.highlightEdges(nodeId);
                        }
                    });
                    this.graph.on('canvas:click', () => this.resetHighlight());
                },
                highlightEdges(nodeId) {
                    this.graph.getNodes().forEach((node) => {
                        this.graph.clearItemStates(node);
                        if (node.getID() === nodeId) {
                            this.graph.setItemState(node, 'selected', true);
                        }
                    });
                    this.graph.getEdges().forEach((edge) => {
                        const { source, target } = edge.getModel();
                        const linked = source === nodeId || target === nodeId;
                        if (linked) {
                            this.graph.setItemState(edge, 'highlight', true);
                        } else {
                            this.graph.clearItemStates(edge);
                        }
                    });
                },
                resetHighlight() {
                    if (!this.graph) return;
                    this.graph.getNodes().forEach((node) => this.graph.clearItemStates(node));
                    this.graph.getEdges().forEach((edge) => this.graph.clearItemStates(edge));
                },
                resizeGraph() {
                    if (!this.graph) return;
                    const container = document.getElementById('knowledge-mindmap');
                    if (!container) return;
                    this.graph.changeSize(container.clientWidth, container.clientHeight);
                    this.graph.fitView(24);
                },
            });
        });
    </script>
@endpush
