<template>
    <div class="character-relationships">
        <div class="headerBox">
            <span
                v-for="(item, index) in list"
                :key="index"
                class="headerItem"
                :class="{ active: item.value === activeIndex }"
                @click="handleChangeLayout(item.value)"
            >
                <img :src="item.icon" :alt="item.label" />
            </span>
        </div>
        <div class="headerBtnBox">
            <img src="./assets/icon-expand.svg" alt="" class="btn-icon" />
            <img src="./assets/icon-download.svg" alt="" class="btn-icon" />
            <img src="./assets/icon-star.svg" alt="" class="btn-icon" />
        </div>
        <div class="mainBox">
            <div class="graph" id="relGraph"></div>
        </div>
    </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
import * as echarts from 'echarts'
import { getCharacterGlobalInfo, getCharacterRelation } from '@/api/characterPage/characterPage.js'
import { useRoute } from 'vue-router'

const route = useRoute()
const personId = ref(route.query.personId || 'Y000064')

const getProxyUrl = (url) => {
    if (!url) return ''
    const urlStr = String(url)
    if (
        !urlStr.startsWith('http') ||
        urlStr.includes('images.weserv.nl') ||
        urlStr.includes('localhost') ||
        urlStr.includes('127.0.0.1')
    ) {
        return url
    }
    const cleanUrl = urlStr.replace(/^https?:\/\//i, '')
    return `https://images.weserv.nl/?url=${encodeURIComponent(cleanUrl)}`
}

// 创建圆形图片的函数
const createCircularImage = (imageUrl, size) => {
    return new Promise((resolve) => {
        const img = new Image()
        img.crossOrigin = 'anonymous'
        img.onload = () => {
            const canvas = document.createElement('canvas')
            canvas.width = size
            canvas.height = size
            const ctx = canvas.getContext('2d')
            
            // 绘制圆形裁剪区域
            ctx.beginPath()
            ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2)
            ctx.closePath()
            ctx.clip()
            
            // 绘制图片
            ctx.drawImage(img, 0, 0, size, size)
            
            resolve(canvas.toDataURL('image/png'))
        }
        img.onerror = () => {
            resolve(imageUrl) // 失败时返回原图
        }
        img.src = imageUrl
    })
}

const nodes = ref([])
const links = ref([])
const characterInfo = ref({})
const CharacterRelation = ref([])

const list = ref([
    { 
        value: '圆形布局', 
        label: '圆形布局', 
        icon: new URL('./assets/icon-circle.svg', import.meta.url).href,
    },
    { 
        value: '力导向布局', 
        label: '力导向布局', 
        icon: new URL('./assets/icon-force.svg', import.meta.url).href,
    },
    { 
        value: '树形布局', 
        label: '树形布局', 
        icon: new URL('./assets/icon-tree.svg', import.meta.url).href,
    },
])
const activeIndex = ref('圆形布局')

let chart = null

const getCharacterGlobalInfoFn = async () => {
    const params = { personId: personId.value }
    try {
        const res = await getCharacterGlobalInfo(params)
        if (res.code === 200 && res.data) {
            characterInfo.value = res.data
        }
    } catch (error) {}
}

const getCharacterRelationFn = async () => {
    const params = { personId: personId.value }
    try {
        const res = await getCharacterRelation(params)
        if (res.code === 200 && res.data) {
            CharacterRelation.value = res.data
        }
    } catch (error) {}

    // 创建圆形头像
    const centerImageUrl = getProxyUrl(characterInfo.value.imageUrl)
    const centerCircularImage = await createCircularImage(centerImageUrl, 168)

    const centerNode = {
        id: 'c',
        name: characterInfo.value.name || '',
        category: 0,
        symbolSize: 84,
        symbol: `image://${centerCircularImage}`,
        itemStyle: {
            borderColor: 'rgba(174,214,255,1)',
            borderWidth: 3,
        },
        label: {
            show: true,
            position: 'bottom',
            distance: 8,
            formatter: '{b}',
            color: 'rgba(5,95,194,1)',
            fontSize: 24,
            fontWeight: 700,
            fontFamily: 'Microsoft YaHei',
        },
    }

    if (CharacterRelation.value.length > 0) {
        // 并行创建所有圆形头像
        const circularImages = await Promise.all(
            CharacterRelation.value.map((item) =>
                createCircularImage(getProxyUrl(item.imageUrl), 108)
            )
        )

        const newNodes = CharacterRelation.value.map((item, index) => ({
            id: index,
            name: item.name,
            category: 1,
            symbolSize: 54,
            symbol: `image://${circularImages[index]}`,
            itemStyle: {
                borderColor: 'rgba(174,214,255,1)',
                borderWidth: 2,
            },
            label: {
                show: true,
                position: 'bottom',
                distance: 5,
                color: 'rgba(59,65,75,1)',
                fontSize: 21,
                fontWeight: 400,
                fontFamily: 'Microsoft YaHei',
            },
            relationName: item.relation || '',
        }))
        newNodes.push(centerNode)

        const newLinks = CharacterRelation.value.map((item, index) => ({
            source: index,
            target: 'c',
            label: { show: true, formatter: item.relation },
            lineStyle: {
                color: 'rgba(174,214,255,1)',
                width: 1,
            },
        }))

        nodes.value = newNodes
        links.value = newLinks
    } else {
        nodes.value = [centerNode]
        links.value = []
    }
}

const handleChangeLayout = (value) => {
    activeIndex.value = value
    renderChart()
}

const renderChart = () => {
    const el = document.getElementById('relGraph')
    if (!el) return

    if (!chart) {
        chart = echarts.init(el)
    }

    const rect = el.getBoundingClientRect()
    const cx = rect.width / 2
    const cy = rect.height / 2
    const radius = Math.min(cx, cy) - 140

    const dataNodes = nodes.value.map((n, i) => {
        if (n.id === 'c') {
            return { ...n, x: cx, y: cy, fixed: true }
        }
        const total = nodes.value.length - 1
        const idx = nodes.value.slice(0, i).filter((nd) => nd.id !== 'c').length
        const angle = (idx / (total || 1)) * Math.PI * 2
        const rLocal = radius + (n.r || 0)
        const x = cx + rLocal * Math.cos(angle)
        const y = cy + rLocal * Math.sin(angle)
        return { ...n, x, y }
    })

    chart.setOption(
        {
            backgroundColor: 'transparent',
            tooltip: {},
            series: [
                {
                    type: 'graph',
                    layout: activeIndex.value === '圆形布局' ? 'none' : 'force',
                    circular: { rotateLabel: false },
                    force: { 
                        repulsion: activeIndex.value === '树形布局' ? 500 : 300, 
                        edgeLength: activeIndex.value === '树形布局' ? 250 : 200,
                        gravity: activeIndex.value === '树形布局' ? 0.05 : 0.1,
                    },
                    roam: true,
                    draggable: true,
                    data: activeIndex.value === '圆形布局' ? dataNodes : nodes.value,
                    links: links.value,
                    categories: [
                        { name: '核心节点' },
                        { name: '关联节点' },
                    ],
                    edgeSymbol: ['none', 'arrow'],
                    edgeSymbolSize: [0, 8],
                    lineStyle: { 
                        color: 'rgba(174,214,255,1)', 
                        width: 1, 
                        curveness: 0,
                    },
                    edgeLabel: {
                        show: true,
                        position: 'middle',
                        distance: -18,
                        formatter: ({ data }) => data?.label?.formatter || '',
                        color: 'rgba(5,95,194,1)',
                        fontSize: 14,
                        fontFamily: 'Microsoft YaHei',
                        backgroundColor: 'rgba(231, 243, 255, 1)',
                        borderRadius: 24,
                        padding: [0, 12],
                    },
                    label: { show: true },
                    emphasis: { 
                        focus: 'adjacency',
                        lineStyle: { width: 2 },
                    },
                },
            ],
        },
        true
    )
}

onMounted(async () => {
    await getCharacterGlobalInfoFn()
    await getCharacterRelationFn()
    renderChart()

    const onResize = () => chart && chart.resize()
    window.addEventListener('resize', onResize)

    watch(activeIndex, () => renderChart())

    onBeforeUnmount(() => {
        window.removeEventListener('resize', onResize)
        if (chart) {
            chart.dispose()
            chart = null
        }
    })
})
</script>

<style scoped lang="scss">
.character-relationships {
    width: 1600px;
    height: 738px;
    background-color: #fff;
    margin: 0 auto;
    position: relative;
    border-radius: 10px;
    border: 1px solid rgba(234, 236, 238, 1);
    box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
    overflow: hidden;
    background-image: url('./assets/bg-grid.png');
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}
.headerBox {
    position: absolute;
    top: 14px;
    left: 12px;
    display: flex;
    gap: 4px;
    z-index: 10;
}
.headerItem {
    width: 28px;
    height: 28px;
    display: flex;
    justify-content: center;
    align-items: center;
    cursor: pointer;
    border-radius: 4px;
    border: 1px solid rgba(234, 236, 238, 1);
    background: rgba(255, 255, 255, 1);
}
.headerItem img {
    width: 18px;
    height: 18px;
}
.headerItem.active {
    background: rgba(231, 243, 255, 1);
}
.headerBtnBox {
    position: absolute;
    top: 14px;
    right: 12px;
    display: flex;
    gap: 4px;
    z-index: 10;
}
.btn-icon {
    width: 28px;
    height: 28px;
    cursor: pointer;
}
.mainBox {
    width: 100%;
    height: 100%;
}
.graph {
    width: 100%;
    height: 100%;
}
</style>
