<template>
    <div style="display: flex; align-items: flex-start; gap: 32px;">
        <div ref="svgContainer" class="diagram-container"></div>
        <div class="diagram-info-panel">
            <div style="display: flex; justify-content: space-between; align-items: center;">
                <div v-if="infoNodeId && state.dataView.get(infoNodeId)" class="item-status" :class="state.dataView.get(infoNodeId)?.status || 'waiting'">
                    {{ state.dataView.get(infoNodeId)?.status || 'waiting' }}
                </div>
                <div v-else>
                    {{ "Unknown Status" }}
                </div>
                <el-button
                    circle
                    size="small"
                    :type="state.pinnedNodeId ? 'primary' : 'default'"
                    @click="togglePin"
                    style="margin-bottom: 4px;"
                    :disabled="!infoNodeId"
                >
                    <span class="iconfont icon-pin"></span>
                </el-button>
            </div>
            <template v-if="infoNodeId && state.dataView.get(infoNodeId)">
                <el-scrollbar height="500px" width="300px">
                    <DiagramItemRecord :data-view="state.dataView.get(infoNodeId)" />
                </el-scrollbar>
            </template>
            <template v-else>
                <div class="diagram-info-empty">
                    <div style="color: #bbb; font-size: 15px;">
                        {{ t('diagram-node-empty') }}
                    </div>
                </div>
            </template>
        </div>
    </div>
</template>

<script setup lang="ts">
import { ref, onMounted, nextTick, reactive, inject, computed } from 'vue';
import * as d3 from 'd3';
import ELK from 'elkjs/lib/elk.bundled.js';
import { mcpClientAdapter } from '@/views/connect/core';
import { invalidConnectionDetector, type Edge, type Node, type NodeDataView } from './diagram';
import { ElMessage } from 'element-plus';


import DiagramItemRecord from './diagram-item-record.vue';
import { useI18n } from 'vue-i18n';
import type { ToolStorage } from '../tools';
import { tabs } from '../../panel';

const { t } = useI18n();

const props = defineProps({
    tabId: {
        type: Number,
        required: true
    }
});

const svgContainer = ref<HTMLDivElement | null>(null);
let prevNodes: any[] = [];
let prevEdges: any[] = [];

const state = reactive({
    nodes: [] as Node[],
    edges: [] as Edge[],
    selectedNodeId: null as string | null,
    draggingNodeId: null as string | null,
    hoverNodeId: null as string | null,
    pinnedNodeId: null as string | null, // 新增
    offset: { x: 0, y: 0 },
    dataView: new Map<string, NodeDataView>
});

const tab = tabs.content[props.tabId];
const tabStorage = tab.storage as ToolStorage;
const autoDetectDiagram = tabStorage.autoDetectDiagram;

if (autoDetectDiagram) {
    // 将 tabStorage.autoDetectDiagram 中的 dataView 保存到 state 中
    autoDetectDiagram.views?.forEach(item => {
        state.dataView.set(item.tool.name, {
            tool: item.tool,
            function: item.function, 
            status: item.status || 'waiting',
            result: item.result || null,
            createAt: item.createAt,
            finishAt: item.finishAt,
            llmTimecost: item.llmTimecost,
            toolcallTimecost: item.toolcallTimecost
        });
    });
} else {
    tabStorage.autoDetectDiagram = {
        edges: [],
        views: []
    };
}


let cancelHoverHandler: NodeJS.Timeout | undefined = undefined;

const setHoverItem = (id: string) => {
    if (state.pinnedNodeId) return; // 如果已pin，不响应hover
    if (cancelHoverHandler) {
        clearTimeout(cancelHoverHandler);
    }
    state.hoverNodeId = id;
};

const clearHoverItem = () => {
    if (state.pinnedNodeId) return; // 如果已pin，不响应hover
    cancelHoverHandler = setTimeout(() => {
        if (cancelHoverHandler) {
            clearTimeout(cancelHoverHandler);
        }
        if (state.hoverNodeId) {
            state.hoverNodeId = null;
        }
    }, 300);
};

// pin 按钮点击事件
function togglePin() {
    if (state.pinnedNodeId) {
        state.pinnedNodeId = null;
    } else if (state.hoverNodeId) {
        state.pinnedNodeId = state.hoverNodeId;
    }
}

// 让面板内容始终显示 pinnedNodeId 优先
const infoNodeId = computed(() => state.pinnedNodeId || state.hoverNodeId);


const getAllTools = async () => {
    const items = [];
    for (const client of mcpClientAdapter.clients) {
        const clientTools = await client.getTools();
        items.push(...clientTools.values());
    }
    return items;
};

const recomputeLayout = async () => {
    const elk = new ELK();
    const elkGraph = {
        id: 'root',
        layoutOptions: {
            'elk.direction': 'DOWN',
            'elk.spacing.nodeNode': '40',
            'elk.layered.spacing.nodeNodeBetweenLayers': '40'
        },
        children: state.nodes,
        edges: state.edges
    };
    const layout = await elk.layout(elkGraph) as unknown as Node;

    state.nodes.forEach((n, i) => {
        const ln = layout.children?.find(c => c.id === n.id);
        if (ln) {
            n.x = ln.x;
            n.y = ln.y;
            n.width = ln.width || 200; // 默认宽度
            n.height = ln.height || 64; // 默认高度
        }
    });
    state.edges = layout.edges || [];

    // 保存拓扑信息到 tabStorage
    tabStorage.autoDetectDiagram!.edges = state.edges.map(edge => ({
        id: edge.id,
        sources: edge.sources || [],
        targets: edge.targets || []
    }));

    return layout;
};

const drawDiagram = async () => {
    const tools = await getAllTools();

    // 默认按照链表进行串联
    const nodes = [] as Node[];
    const edges = [] as Edge[];

    // 如果保存了 edges 信息，则需要进行同步
    const reservedEdges = autoDetectDiagram?.edges;
    if (reservedEdges && reservedEdges.length > 0) {
        for (const edge of reservedEdges) {
            if (edge.sources && edge.targets && edge.sources.length > 0 && edge.targets.length > 0) {
                edges.push({
                    id: edge.id,
                    sources: edge.sources || [],
                    targets: edge.targets || [],
                });
            }
        }
    } else {
        for (let i = 0; i < tools.length - 1; ++i) {
            const prev = tools[i];
            const next = tools[i + 1];
            edges.push({
                id: prev.name + '-' + next.name,
                sources: [prev.name],
                targets: [next.name]
            })
        }
    }

    for (const tool of tools) {
        nodes.push({
            id: tool.name,
            width: 200,
            height: 64, // 增加高度
            labels: [{ text: tool.name || 'Tool' }]
        });

        if (!state.dataView.has(tool.name)) {
            // 如果 dataView 中没有该工具，则初始化
            state.dataView.set(tool.name, {
                tool,
                status: 'waiting',
            });
        }
    }

    state.edges = edges;
    state.nodes = nodes;

    // 重新计算布局
    await recomputeLayout();

    // 绘制 svg
    renderSvg();
};

function renderSvg() {
    const prevNodeMap = new Map(prevNodes.map(n => [n.id, n]));
    const prevEdgeMap = new Map(prevEdges.map(e => [e.id, e]));

    // 计算所有节点的最小x和最大x
    const xs = state.nodes.map(n => (n.x || 0));
    const minX = Math.min(...xs);
    const maxX = Math.max(...xs.map((x, i) => x + (state.nodes[i].width || 160)));
    const contentWidth = maxX - minX;
    const svgWidth = Math.max(contentWidth + 120, 400); // 120为两侧留白
    const offsetX = (svgWidth - contentWidth) / 2 - minX;

    const height = Math.max(...state.nodes.map(n => (n.y || 0) + (n.height || 48)), 300) + 60;

    // 不再全量清空，只清空 svg 元素
    let svg = d3.select(svgContainer.value).select('svg');
    if (svg.empty()) {
        svg = d3
            .select(svgContainer.value)
            .append('svg')
            .attr('width', svgWidth)
            .attr('height', height)
            .style('user-select', 'none') as any;
    } else {
        svg.attr('width', svgWidth).attr('height', height);
        svg.selectAll('defs').remove();
    }

    // Arrow marker
    svg
        .append('defs')
        .append('marker')
        .attr('id', 'arrow')
        .attr('viewBox', '0 0 8 8')
        .attr('refX', 6)
        .attr('refY', 4)
        .attr('markerWidth', 5)
        .attr('markerHeight', 5)
        .attr('orient', 'auto')
        .append('path')
        .attr('d', 'M 0 0 L 8 4 L 0 8 z')
        .attr('fill', 'var(--main-color)');

    // 1. 创建/获取 main group
    let mainGroup = svg.select('g.main-group');
    if (mainGroup.empty()) {
        mainGroup = svg.append('g').attr('class', 'main-group') as any;
    }
    mainGroup
        .transition()
        .duration(600)
        .attr('transform', `translate(${offsetX}, 0)`);

    // Draw edges with enter animation
    const allSections: { id: string, section: any }[] = [];
    (state.edges || []).forEach(edge => {
        const sections = edge.sections || [];
        sections.forEach((section: any, idx: number) => {
            allSections.push({
                id: (edge.id || '') + '-' + (section.id || idx),
                section
            });
        });
    });

    const edgeSelection = mainGroup.selectAll<SVGLineElement, any>('.edge')
        .data(allSections, d => d.id);

    edgeSelection.exit().remove();

    const edgeEnter = edgeSelection.enter()
        .append('line')
        .attr('class', 'edge')
        .attr('x1', d => {
            const prev = prevEdgeMap.get(d.id);
            return prev && prev.sections && prev.sections[0]
                ? prev.sections[0].startPoint.x + 30
                : d.section.startPoint.x + 30;
        })
        .attr('y1', d => {
            const prev = prevEdgeMap.get(d.id);
            return prev && prev.sections && prev.sections[0]
                ? prev.sections[0].startPoint.y + 30
                : d.section.startPoint.y + 30;
        })
        .attr('x2', d => {
            const prev = prevEdgeMap.get(d.id);
            return prev && prev.sections && prev.sections[0]
                ? prev.sections[0].endPoint.x + 30
                : d.section.endPoint.x + 30;
        })
        .attr('y2', d => {
            const prev = prevEdgeMap.get(d.id);
            return prev && prev.sections && prev.sections[0]
                ? prev.sections[0].endPoint.y + 30
                : d.section.endPoint.y + 30;
        })
        .attr('stroke', 'var(--main-color)')
        .attr('stroke-width', 2.5)
        .attr('marker-end', 'url(#arrow)')
        .attr('opacity', 0);

    edgeEnter
        .transition()
        .duration(600)
        .attr('opacity', 1)
        .attr('x1', d => d.section.startPoint.x + 30)
        .attr('y1', d => d.section.startPoint.y + 30)
        .attr('x2', d => d.section.endPoint.x + 30)
        .attr('y2', d => d.section.endPoint.y + 30);

    // update + 动画（注意这里不再 transition opacity）
    edgeSelection.merge(edgeEnter)
        .transition()
        .duration(600)
        .ease(d3.easeCubicInOut)
        .attr('x1', d => d.section.startPoint.x + 30)
        .attr('y1', d => d.section.startPoint.y + 30)
        .attr('x2', d => d.section.endPoint.x + 30)
        .attr('y2', d => d.section.endPoint.y + 30)
        .attr('opacity', 1);

    // --- 节点动画部分 ---
    const nodeGroup = mainGroup.selectAll<SVGGElement, any>('.node')
        .data(state.nodes, d => d.id);

    nodeGroup.exit().remove();

    // 节点 enter
    const nodeGroupEnter = nodeGroup.enter()
        .append('g')
        .attr('class', 'node')
        .attr('transform', d => {
            const prev = prevNodeMap.get(d.id);
            if (prev) {
                return `translate(${(prev.x || 0) + 30}, ${(prev.y || 0) + 30})`;
            }
            return `translate(${(d.x || 0) + 30}, ${(d.y || 0) + 30})`;
        })
        .style('cursor', 'pointer')
        .attr('opacity', 0)
        .on('mousedown', null)
        .on('mouseup', function (event, d) {
            event.stopPropagation();
            if (state.selectedNodeId) {

                const { canConnect, reason } = invalidConnectionDetector(state, d);
                console.log(reason);

                if (reason) {
                    ElMessage.warning(reason);
                }

                if (canConnect) {
                    state.edges.push({
                        id: `e${state.selectedNodeId}_${d.id}_${Date.now()}`,
                        sources: [state.selectedNodeId],
                        targets: [d.id]
                    });
                    state.selectedNodeId = null;
                    recomputeLayout().then(renderSvg);
                } else {
                    // 已存在则只取消选中
                    state.selectedNodeId = null;
                    renderSvg();
                }
                context.setCaption('');

            } else {
                state.selectedNodeId = d.id;
                renderSvg();
                context.setCaption(t('select-node-define-test-tomo'));
            }
            state.draggingNodeId = null;
        })
        .on('mouseover', function (event, d) {
            setHoverItem(d.id);
            d3.select(this).select('rect')
                .transition()
                .duration(200)
                .attr('stroke', 'var(--main-color)')
                .attr('stroke-width', 2);
        })
        .on('mouseout', function (event, d) {
            // clearHoverItem();
            if (state.selectedNodeId === d.id) return;
            d3.select(this).select('rect')
                .transition()
                .duration(200)
                .attr('stroke', 'var(--main-light-color-10)')
                .attr('stroke-width', 1);
        });

    nodeGroupEnter.append('rect')
        .attr('width', (d: any) => d.width)
        .attr('height', (d: any) => d.height)
        .attr('rx', 16)
        .attr('fill', 'var(--main-light-color-20)')
        .attr('stroke', d => state.selectedNodeId === d.id ? 'var(--main-color)' : 'var(--main-light-color-10)')
        .attr('stroke-width', 2);

    // 节点文字
    nodeGroupEnter.append('text')
        .attr('x', d => d.width / 2)
        .attr('y', d => d.height / 2 - 6) // 上移一点
        .attr('text-anchor', 'middle')
        .attr('font-size', 16)
        .attr('fill', 'var(--main-color)')
        .attr('font-weight', 600)
        .text(d => d.labels?.[0]?.text || 'Tool');

    nodeGroupEnter.append('g').attr('class', 'node-status');

    // 合并 enter+update
    const nodeStatusGroup = nodeGroup.merge(nodeGroupEnter).select('.node-status');

    // 先清空再重绘
    nodeStatusGroup.each(function (d) {
        const g = d3.select(this);
        g.selectAll('*').remove(); // 清空旧内容

        const status = state.dataView.get(d.id)?.status || 'waiting';
        if (status === 'running') {
            g.append('circle')
                .attr('cx', d.width / 2 - 32)
                .attr('cy', d.height - 16)
                .attr('r', 6)
                .attr('fill', 'none')
                .attr('stroke', 'var(--main-color)')
                .attr('stroke-width', 3)
                .attr('stroke-dasharray', 20)
                .attr('stroke-dashoffset', 0)
                .append('animateTransform')
                .attr('attributeName', 'transform')
                .attr('attributeType', 'XML')
                .attr('type', 'rotate')
                .attr('from', `0 ${(d.width / 2 - 32)} ${(d.height - 16)}`)
                .attr('to', `360 ${(d.width / 2 - 32)} ${(d.height - 16)}`)
                .attr('dur', '1s')
                .attr('repeatCount', 'indefinite');
            g.append('text')
                .attr('x', d.width / 2 - 16)
                .attr('y', d.height - 12)
                .attr('font-size', 13)
                .attr('fill', 'var(--main-color)')
                .text('running');
        } else if (status === 'waiting') {
            g.append('circle')
                .attr('cx', d.width / 2 - 32)
                .attr('cy', d.height - 16)
                .attr('r', 6)
                .attr('fill', 'none')
                .attr('stroke', '#bdbdbd')
                .attr('stroke-width', 3);
            g.append('text')
                .attr('x', d.width / 2 - 16)
                .attr('y', d.height - 12)
                .attr('font-size', 13)
                .attr('fill', '#bdbdbd')
                .text('waiting');
        } else if (status === 'success') {
            g.append('circle')
                .attr('cx', d.width / 2 - 32)
                .attr('cy', d.height - 16)
                .attr('r', 6)
                .attr('fill', 'none')
                .attr('stroke', '#4caf50')
                .attr('stroke-width', 3);
            g.append('text')
                .attr('x', d.width / 2 - 16)
                .attr('y', d.height - 12)
                .attr('font-size', 13)
                .attr('fill', '#4caf50')
                .text('success');
        } else if (status === 'error') {
            g.append('circle')
                .attr('cx', d.width / 2 - 32)
                .attr('cy', d.height - 16)
                .attr('r', 6)
                .attr('fill', 'none')
                .attr('stroke', '#f44336')
                .attr('stroke-width', 3);
            g.append('text')
                .attr('x', d.width / 2 - 16)
                .attr('y', d.height - 12)
                .attr('font-size', 13)
                .attr('fill', '#f44336')
                .text('error');
        }
    });
    // 节点 enter 动画
    nodeGroupEnter
        .transition()
        .duration(600)
        .attr('opacity', 1)
        .attr('transform', d => `translate(${(d.x || 0) + 30}, ${(d.y || 0) + 30})`);

    // 节点 update 动画
    nodeGroup
        .transition()
        .duration(600)
        .ease(d3.easeCubicInOut)
        .attr('transform', d => `translate(${(d.x || 0) + 30}, ${(d.y || 0) + 30})`);

    // 高亮选中节点动画
    nodeGroup.select('rect')
        .transition()
        .duration(400)
        .attr('stroke', d => state.selectedNodeId === d.id ? 'var(--main-color)' : 'var(--main-light-color-10)');

    // 边高亮
    svg.selectAll<SVGLineElement, any>('.edge')
        .on('mouseover', function () {
            d3.select(this)
                .transition()
                .duration(200)
                .attr('stroke', 'var(--main-color)')
                .attr('stroke-width', 4.5);

            context.setCaption(t('click-edge-to-delete'));

        })
        .on('mouseout', function () {
            d3.select(this)
                .transition()
                .duration(200)
                .attr('stroke', 'var(--main-color)')
                .attr('stroke-width', 2.5);

            context.setCaption('');
        })
        .on('click', function (event, d) {
            // 只删除当前 edge
            state.edges = state.edges.filter(e => {
                // 多段 edge 情况
                if (e.sections) {
                    // 只保留不是当前 section 的
                    return !e.sections.some((section: any, idx: number) =>
                        ((e.id || '') + '-' + (section.id || idx)) === d.id
                    );
                }
                // 单段 edge 情况
                return e.id !== d.id && e.id !== d.section?.id;
            });
            recomputeLayout().then(renderSvg);
            event.stopPropagation();
        });

    // 渲染结束后保存当前快照
    prevNodes = state.nodes.map(n => ({ ...n }));
    prevEdges = (state.edges || []).map(e => ({ ...e, sections: e.sections ? e.sections.map((s: any) => ({ ...s })) : [] }));
}

// 重置连接为链表结构
function serialConnection() {
    if (!state.nodes.length) return;
    const edges = [];
    for (let i = 0; i < state.nodes.length - 1; ++i) {
        const prev = state.nodes[i];
        const next = state.nodes[i + 1];
        edges.push({
            id: prev.id + '-' + next.id,
            sources: [prev.id],
            targets: [next.id]
        });
    }
    state.edges = edges;
    recomputeLayout().then(renderSvg);
}

function parallelConnection() {
    if (!state.nodes.length) return;
    const edges = [] as Edge[];
    state.edges = edges;
    recomputeLayout().then(renderSvg);
}

const context = inject('context') as any;
context.preset = (type: string) => {
    if (type === 'serial') {
        serialConnection();
    } else if (type === 'parallel') {
        parallelConnection();
    }
};
context.state = state;
context.render = renderSvg;

onMounted(() => {
    nextTick(drawDiagram);
});

// 4. 计算窗口位置
function getNodePopupStyle(node: any): any {
    // 节点的 svg 坐标转为容器内绝对定位
    // 注意：这里假设 offsetX、node.x、node.y 已经是最新的
    const marginX = 50;
    const marginY = 80;
    const popupWidth = 300;
    const popupHeight = 500;

    let left = (node.x || 0) + (node.width || 160) + 100;
    let top = (node.y || 0) + 30;

    // 获取容器宽高
    const container = svgContainer.value;
    let containerWidth = 1200, containerHeight = 800; // 默认值
    if (container) {
        const rect = container.getBoundingClientRect();
        containerWidth = rect.width;
        containerHeight = rect.height;
    }

    // 限制 left 和 top 不超出容器
    left = Math.max(marginX, Math.min(left, containerWidth - popupWidth - marginX));
    top = Math.max(marginY, Math.min(top, containerHeight - popupHeight - marginY));

    return {
        position: 'absolute',
        left: `${left}px`,
        top: `${top}px`,
        width: `${popupWidth}px`,
        height: `${popupHeight}px`
    };
}
</script>

<style>
.diagram-container {
    width: 600px;
    min-height: 200px;
    border-radius: 8px;
    padding: 24px 0;
    display: flex;
    justify-content: flex-start;
    overflow-x: auto;
}

.node-popup {
    position: absolute;
    background: var(--background);
    border: 1px solid var(--main-color);
    border-radius: 8px;
    padding: 8px 12px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    white-space: nowrap;
    z-index: 10;
}

/* 旋转动画 */
.status-running-circle {
    animation: spin 1s linear infinite;
    transform-origin: center;
}

@keyframes spin {
    100% {
        transform: rotate(360deg);
    }
}

.diagram-container {
    width: 600px;
    min-height: 200px;
    display: flex;
    align-items: flex-start;
    border-radius: 8px;
    padding: 24px 0;
    overflow-x: auto;
}

.diagram-info-panel {
    position: absolute;
    right: 30px;
    top: 10px;
    width: 300px;
    min-height: 180px;
    border: 1px solid var(--main-color);
    border-radius: 12px;
    box-shadow: 0 2px 12px 0 rgba(0,0,0,0.06);
    padding: 10px;
    display: flex;
    flex-direction: column;
    margin-top: 8px;
    transition: box-shadow 0.2s;
}

.diagram-info-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;
    min-height: 120px;
    opacity: 0.85;
    font-size: 15px;
}

.item-status.running {
    color: var(--main-color);
}

.item-status.success {
    color: #43a047;
}

.item-status.error {
    color: #e53935;
}

.item-status.waiting {
    color: #aaa;
}

.item-status.default {
    color: #888;
}

</style>