<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 "default-passive-events";

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, borderWidth = 2, borderColor = "rgba(174,214,255,1)") => {
	return new Promise((resolve) => {
		const img = new Image();
		img.crossOrigin = "anonymous";
		img.onload = () => {
			const canvas = document.createElement("canvas");
			// 增加大小以容纳边框
			const totalSize = size + borderWidth * 2;
			canvas.width = totalSize;
			canvas.height = totalSize;
			const ctx = canvas.getContext("2d");

			// 清空背景
			ctx.clearRect(0, 0, totalSize, totalSize);

			// 绘制圆形图片
			ctx.beginPath();
			ctx.arc(totalSize / 2, totalSize / 2, size / 2, 0, Math.PI * 2);
			ctx.closePath();
			ctx.clip();

			// 绘制图片到圆形区域
			ctx.drawImage(img, borderWidth, borderWidth, size, size);

			// 恢复上下文并绘制边框
			ctx.restore();
			ctx.beginPath();
			ctx.arc(totalSize / 2, totalSize / 2, size / 2, 0, Math.PI * 2);
			ctx.strokeStyle = borderColor;
			ctx.lineWidth = borderWidth;
			ctx.stroke();

			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 handleChangeLayout = (value) => {
	activeIndex.value = value;
	renderChart();
};

// ========== 接口：人物全局信息 ==========
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) {
		console.error("获取人物信息失败", 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) {
		console.error("获取人物关系失败", error);
	}

	// 创建圆形头像
	const centerImageUrl = getProxyUrl(characterInfo.value.imageUrl);
	const centerCircularImage = await createCircularImage(centerImageUrl, 76, 3, "rgba(174,214,255,1)");

	// ---------- 组装节点和连线 ----------
	const centerNode = {
		id: "c",
		name: characterInfo.value.name || "",
		category: 0,
		symbolSize: 80,
		symbol: `image://${centerCircularImage}`,
		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,
				},
			},
		},
	};

	if (CharacterRelation.value.length > 0) {
		// 并行创建所有圆形头像
		const circularImages = await Promise.all(
			CharacterRelation.value.map((item) =>
				createCircularImage(getProxyUrl(item.imageUrl), 76, 2, "rgba(174,214,255,1)")
			)
		);

		const newNodes = CharacterRelation.value.map((item, index) => ({
			id: index,
			name: item.name,
			category: 1,
			symbolSize: 80,
			symbol: `image://${circularImages[index]}`,
		}));
		newNodes.push(centerNode);

		const newLinks = CharacterRelation.value.map((item, index) => ({
			source: index,
			target: "c",
			label: { show: true, formatter: item.relation },
		}));

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

// ========== 渲染图表 ==========
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(
		{
			tooltip: {},
			series: [
				{
					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: 0,
						formatter: ({ data }) => data?.label?.formatter || "",
						color: "rgb(5, 95, 194)",
						fontSize: 14,
						fontWeight: 400,
						fontFamily: "Microsoft YaHei",
						backgroundColor: "#E7F3FF",
						borderRadius: 14,
						padding: [4, 12],
						borderWidth: 1,
						borderColor: "rgba(174,214,255,1)",
					},
					label: { show: true, position: "bottom", color: "rgb(59,65,75)", fontSize: 16 },
					itemStyle: { color: "rgba(5,95,194,1)" },
					emphasis: { focus: "adjacency" },
				},
			],
		},
		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 lang="scss" scoped>
* {
	padding: 0;
	margin: 0;
}
.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>
