<template>
	<div class="character-relationships" :style="{ height: containerHeight + 'px' }">
		<div class="headerWrapper">
			<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>
		<el-empty v-if="CharacterRelation.length === 0" description="暂无数据" :image-size="80" class="empty-placeholder" />
		<template v-else>
			<div class="mainBox" :style="{ paddingTop: (mainPaddingTop + 30) + 'px' }">
				<div v-show="!showRelationChart || activeIndex !== thirdLayoutName" class="graph" id="relGraph"></div>
				<RelationChart v-if="showRelationChart && activeIndex === thirdLayoutName && graphData.rootId" :graph-data="graphData" />
			</div>
		</template>
	</div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
import * as echarts from 'echarts'
import { getCharacterGlobalInfo, getCharacterRelation } from '@/api/characterPage/characterPage.js'
import RelationChart from '@/components/base/RelationChart/index.vue'

const props = defineProps({
	containerHeight: { type: Number, default: 738 },
	mainPaddingTop: { type: Number, default: 0 },
	showRelationChart: { type: Boolean, default: true },
	thirdLayoutName: { type: String, default: '关系图' },
	centerImageSize: { type: Number, default: 168 },
	nodeImageSize: { type: Number, default: 108 },
	centerSymbolSize: { type: Number, default: 84 },
	nodeSymbolSize: { type: Number, default: 54 },
	nodeHasDetailStyle: { type: Boolean, default: true },
})

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) => {
		if (!imageUrl) {
			resolve('')
			return
		}
		const img = new Image()
		img.crossOrigin = 'anonymous'

		const drawCircular = (imageEl) => {
			try {
				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(imageEl, 0, 0, size, size)
				resolve(canvas.toDataURL('image/png'))
			} catch (error) {
				resolve(imageUrl)
			}
		}

		img.onload = () => drawCircular(img)
		img.onerror = () => {
			const img2 = new Image()
			img2.onload = () => drawCircular(img2)
			img2.onerror = () => resolve(imageUrl)
			img2.src = imageUrl
		}
		img.src = imageUrl
	})
}

const nodes = ref([])
const links = ref([])
const characterInfo = ref({})
const CharacterRelation = ref([])
const graphData = ref({ rootId: '', nodes: [], lines: [] })

const list = ref([
	{
		value: '力导向布局',
		label: '力导向布局',
		icon: new URL('./assets/icon-force.svg', import.meta.url).href,
	},
	{
		value: props.thirdLayoutName,
		label: props.thirdLayoutName,
		icon: new URL('./assets/icon-tree.svg', import.meta.url).href,
	},
	{
		value: '圆形布局',
		label: '圆形布局',
		icon: new URL('./assets/icon-circle.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 buildCenterNode = (centerCircularImage) => {
	const base = {
		id: 'c',
		name: characterInfo.value.name || '',
		category: 0,
		symbolSize: props.centerSymbolSize,
		symbol: `image://${centerCircularImage}`,
	}

	if (props.nodeHasDetailStyle) {
		// techLeader style: detailed label with rich text
		base.label = {
			show: true,
			position: 'bottom',
			distance: 8,
			formatter: '{b}',
			color: 'rgba(5,95,194,1)',
			fontSize: 24,
			fontWeight: 700,
			fontFamily: 'Microsoft YaHei',
		}
	} else {
		// MOC / thinkTank style: rich formatter
		base.label = {
			show: true,
			position: 'bottom',
			formatter: '{n|{b}}',
			rich: {
				n: {
					color: 'rgba(5,95,194,1)',
					fontSize: 24,
					fontWeight: 700,
					fontFamily: 'Microsoft YaHei',
					lineHeight: 36,
				},
			},
		}
	}

	return base
}

const buildChildNode = (item, index, circularImage) => {
	const base = {
		id: index,
		name: item.name,
		category: 1,
		symbolSize: props.nodeSymbolSize,
		symbol: `image://${circularImage}`,
	}

	if (props.nodeHasDetailStyle) {
		// techLeader style: with itemStyle and label
		base.itemStyle = {
			borderColor: 'rgba(174,214,255,1)',
			borderWidth: 2,
		}
		base.label = {
			show: true,
			position: 'bottom',
			distance: 5,
			color: 'rgba(59,65,75,1)',
			fontSize: 21,
			fontWeight: 400,
			fontFamily: 'Microsoft YaHei',
		}
		base.relationName = item.relation || ''
	}

	return base
}

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, props.centerImageSize)

	const centerNode = buildCenterNode(centerCircularImage)

	if (CharacterRelation.value.length > 0) {
		const circularImages = await Promise.all(
			CharacterRelation.value.map((item) =>
				createCircularImage(getProxyUrl(item.imageUrl), props.nodeImageSize)
			)
		)

		const newNodes = CharacterRelation.value.map((item, index) =>
			buildChildNode(item, index, circularImages[index])
		)
		newNodes.push(centerNode)

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

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

	// build RelationChart data
	const gNodes = [{ id: 'center', text: characterInfo.value.name || '' }]
	const gLines = []
	CharacterRelation.value.forEach((item, index) => {
		gNodes.push({ id: `node_${index}`, text: item.name })
		gLines.push({ from: 'center', to: `node_${index}`, text: item.relation || '' })
	})
	graphData.value = { rootId: 'center', nodes: gNodes, lines: gLines }
}

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 }
	})

	// echarts option varies by nodeHasDetailStyle
	let seriesBase
	if (props.nodeHasDetailStyle) {
		// techLeader variant
		seriesBase = {
			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 } },
		}
	} else {
		// MOC / thinkTank variant
		seriesBase = {
			type: 'graph',
			layout: activeIndex.value === '圆形布局' ? 'none' : 'force',
			circular: { rotateLabel: true },
			force: { repulsion: 800, edgeLength: [80, 160] },
			roam: true,
			data: activeIndex.value === '圆形布局' ? dataNodes : nodes.value,
			links: links.value,
			edgeSymbol: ['none', 'arrow'],
			edgeSymbolSize: [4, 10],
			lineStyle: { color: 'rgba(174,214,255,1)', width: 2, opacity: 0.8 },
			edgeLabel: {
				show: true,
				position: 'middle',
				distance: -18,
				formatter: ({ data }) => data?.label?.formatter || '',
				color: 'rgb(5, 95, 194)',
				fontSize: 12,
				fontWeight: 400,
				fontFamily: 'Microsoft YaHei',
				lineHeight: 24,
				backgroundColor: 'rgba(231, 243, 255, 1)',
				borderRadius: 24,
				padding: [0, 12],
			},
			label: { show: true, position: 'bottom', color: 'rgb(59,65,75)', fontSize: 16 },
			itemStyle: { color: 'rgba(5,95,194,1)' },
			emphasis: { focus: 'adjacency' },
		}
	}

	chart.setOption(
		{
			backgroundColor: 'transparent',
			tooltip: {},
			series: [seriesBase],
		},
		true
	)
}

onMounted(async () => {
	await getCharacterGlobalInfoFn()
	await getCharacterRelationFn()
	await nextTick()
	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 lang="scss" scoped>
* {
	padding: 0;
	margin: 0;
}
.character-relationships {
	width: 1600px;
	margin: 0 auto;
	position: relative;
	overflow: hidden;
	background-color: transparent;
	background-image: url('./assets/bg-grid.png');
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}
.headerWrapper {
	position: absolute;
	top: 0;
	left: 0;
	width: 465px;
	height: 54px;
	background: #fff;
	z-index: 10;
	border-radius: 10px;
	box-shadow: 0px 0px 20px rgba(25, 69, 130, 0.1);
}
.headerBox {
	position: absolute;
	top: 11px;
	left: 13px;
	display: flex;
	gap: 0;
	width: 96px;
	height: 32px;
	background: #fff;
	border-radius: 6px;
	box-shadow: 0px 0px 20px rgba(25, 69, 130, 0.1);
}
.headerItem {
	width: 32px;
	height: 32px;
	display: flex;
	justify-content: center;
	align-items: center;
	cursor: pointer;
	border-radius: 4px;
	border: 1px solid transparent;
	background: transparent;
}
.headerItem img {
	width: 18px;
	height: 18px;
}
.headerItem.active {
	background: rgba(231, 243, 255, 1);
	border-color: #055ec2;
}
.headerBtnBox {
	position: absolute;
	top: 13px;
	right: 12px;
	display: flex;
	gap: 12px;
}
.btn-icon {
	width: 24px;
	height: 24px;
	cursor: pointer;
	opacity: 0.5;
	transition: opacity 0.2s;
}
.btn-icon:hover {
	opacity: 0.8;
}
.mainBox {
	width: 100%;
	height: 100%;
}
.graph {
	width: 100%;
	height: 100%;
}
.empty-placeholder {
	width: 100%;
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
}
:deep(canvas) {
	background: transparent !important;
}
</style>
