<template>
    <view class="container" id="pixi-box" ref="pixiContainerRef"></view>
</template>

<script setup>
import { onMounted, onUnmounted, ref, nextTick } from 'vue';
const emit = defineEmits(['tag-click']);

// DOM Ref
const pixiContainerRef = ref(null);

// PIXI 变量
let app = null;
let tagsContainer = null;
let activeTagInstances = [];

// 配置数据
const mockTags = [
    { name: '医生', bgColor: 0x0069fe, fontColor: 0xffffff, size: 17, opacity: 1.0, angle: 0, radius: 0 },
    {
        name: '工程师',
        bgColor: 0x87e2ec,
        fontColor: 0xffffff,
        size: 14,
        opacity: 1,
        angle: -Math.PI / 2,
        radius: 68,
        tailRotation: Math.PI / 2,
    },
    {
        name: '建筑师',
        bgColor: 0xffebeb,
        tailColor: 0xffe1e1,
        fontColor: 0xff6969,
        size: 11.5,
        opacity: 1,
        angle: -Math.PI / 4.2,
        radius: 125,
        tailRotation: (3 * Math.PI) / 4,
    },
    {
        name: '律师',
        bgColor: 0x21ea85,
        fontColor: 0xffffff,
        size: 15,
        opacity: 1,
        angle: -Math.PI / 10,
        radius: 130,
        tailRotation: (3 * Math.PI) / 4,
    },
    {
        name: '记者',
        bgColor: 0xebf3ff,
        tailColor: 0xb9d3ff,
        fontColor: 0x1d71ef,
        size: 12,
        opacity: 1,
        angle: Math.PI / 120,
        radius: 130,
        tailRotation: (3 * Math.PI) / 3.4,
    },
    {
        name: '程序员',
        bgColor: 0xffd4b6,
        fontColor: 0xffffff,
        size: 14,
        opacity: 1,
        angle: Math.PI / 7,
        radius: 120,
        tailRotation: (5 * Math.PI) / 4,
    },
    {
        name: '摄影师',
        bgColor: 0xd8e5fe,
        tailColor: 0xb9d3ff,
        fontColor: 0x1d71ef,
        size: 11,
        opacity: 1,
        angle: Math.PI / 3,
        radius: 79,
        tailRotation: (3 * Math.PI) / 2,
    },
    {
        name: '设计师',
        bgColor: 0xff9400,
        fontColor: 0xffffff,
        size: 14,
        opacity: 1,
        angle: (2 * Math.PI) / 3,
        radius: 92,
        tailRotation: (7 * Math.PI) / 4,
    },
    {
        name: '心理咨询师',
        bgColor: 0xebf3ff,
        tailColor: 0xb9d3ff,
        fontColor: 0x1d71ef,
        size: 10.5,
        opacity: 1,
        angle: (5.4 * Math.PI) / 6,
        radius: 110,
        tailRotation:(3 *  Math.PI) /1.78,
    },
    {
        name: '护士',
        bgColor: 0xff6969,
        fontColor: 0xffffff,
        size: 15,
        opacity: 1,
        angle: (6.3 * Math.PI) / 5.9,
        radius: 110,
        tailRotation: Math.PI / 4,
    },
    {
        name: '会计',
        bgColor: 0xfce9c9,
        fontColor: 0xfbc55f,
        size: 13,
        opacity: 1,
        angle: (7.2 * Math.PI) / 5.9,
        radius: 120,
        tailRotation: Math.PI / 4,
    },
];

onMounted(async () => {
    await nextTick();
    setTimeout(() => {
        initPixi();
    }, 100);
    window.addEventListener('resize', handleResize);
});

onUnmounted(() => {
    window.removeEventListener('resize', handleResize);
    if (app) {
        app.destroy(true, { children: true, texture: true, baseTexture: true });
        app = null;
    }
});

const getContainerDOM = () => {
    const refVal = pixiContainerRef.value;
    if (!refVal) return document.getElementById('pixi-box');
    if (refVal.$el) return refVal.$el;
    return refVal;
};

const clamp = (num, min, max) => Math.min(Math.max(num, min), max);

const initPixi = () => {
    const container = getContainerDOM();
    if (!container) return;

    const width = container.clientWidth || 300;
    const height = container.clientHeight || 300;

    if (app) return;

    app = new PIXI.Application({
        width: width,
        height: height,
        backgroundAlpha: 0,
        backgroundColor: 0xf5f7fa,
        antialias: true,
        resolution: window.devicePixelRatio || 1,
        autoDensity: true,
    });

    container.appendChild(app.view);

    tagsContainer = new PIXI.Container();
    app.stage.addChild(tagsContainer);

    renderScene(width, height);
};

const renderScene = (sw, sh) => {
    tagsContainer.removeChildren();
    activeTagInstances = [];

    const baseSize = 375;
    const scaleFactor = (Math.min(sw, sh) / baseSize) * 0.9;

    mockTags.forEach((data, index) => {
        const scaledRadius = data.radius * (scaleFactor < 1 ? 1 : scaleFactor * 0.8);

        let x = sw / 2 + scaledRadius * Math.cos(data.angle);
        let y = sh / 2 + scaledRadius * Math.sin(data.angle);

        const tag = createTag(data, index);

        tagsContainer.addChild(tag);

        const safeW = tag.width / 2 + 10;
        const safeH = tag.height / 2 + 10;

        // 强制修正 x 和 y，使其不超出屏幕
        x = clamp(x, safeW, sw - safeW);
        y = clamp(y, safeH, sh - safeH);

        tag.x = x;
        tag.y = y;

        // 4. 保存元数据
        tag.userData = {
            originalX: x,
            originalY: y,
            angle: data.angle,
            radius: scaledRadius,
            floatOffset: Math.random() * Math.PI * 2,
            floatSpeed: 0.01 + Math.random() * 0.02,
            floatRange: 2 + Math.random() * 2,
            safeH: safeH,
        };

        if (data.radius > 0) {
            const tail = createCometTail( data.tailColor || data.bgColor, data.tailRotation, tag.width);
            tag.addChildAt(tail, 0);
            tag.updateTail = () => tail.updateAnim();
        }

        activeTagInstances.push(tag);
    });

    // 动画循环
    app.ticker.add(() => {
        const screenH = app.screen.height;

        activeTagInstances.forEach((tag) => {
            const meta = tag.userData;
            if (meta) {
                // 计算新的浮动位置
                meta.floatOffset += meta.floatSpeed;
                let nextY = meta.originalY + Math.sin(meta.floatOffset) * meta.floatRange;

                // 再次进行边界检查
                if (nextY < meta.safeH) nextY = meta.safeH;
                if (nextY > screenH - meta.safeH) nextY = screenH - meta.safeH;

                tag.y = nextY;

                if (tag.updateTail) tag.updateTail();
            }
        });
    });
};

const createTag = (tagData, index) => {
    const tagGroup = new PIXI.Container();
    tagGroup.eventMode = 'static';
    tagGroup.cursor = 'pointer';

    tagGroup.on('pointertap', () => emit('tag-click', tagData));

    const text = new PIXI.Text(tagData.name, {
        fontFamily: ['PingFang SC', 'Microsoft YaHei', 'Arial'],
        fontSize: tagData.size,
        fill: tagData.fontColor,
        padding: 4,
        resolution: 2,
    });
    text.anchor.set(0.5);

    const paddingH = 26;
    const paddingV = 10;
    let bgWidth = text.width + paddingH;
    let bgHeight = text.height + paddingV;

    if (index === 0) bgWidth = Math.max(bgWidth, tagData.size * 4.5);

    const bg = new PIXI.Graphics();
    bg.beginFill(tagData.bgColor, tagData.opacity ?? 1);
    bg.drawRoundedRect(-bgWidth / 2, -bgHeight / 2, bgWidth, bgHeight, bgHeight / 2);
    bg.endFill();

    tagGroup.addChild(bg);
    tagGroup.addChild(text);

    return tagGroup;
};

const createCometTail = (bgColor, tailRotation, parentWidth) => {
    const tailGroup = new PIXI.Container();
    const graphics = new PIXI.Graphics();
    tailGroup.addChild(graphics);

    const baseLength = 45;
    const startWidth = parentWidth * 0.6;
    const endWidth = 20;

    let breathPhase = Math.random() * Math.PI * 2;
    const breathSpeed = 0.04;

    tailGroup.updateAnim = () => {
        breathPhase += breathSpeed;
        const breathScale = 0.85 + 0.15 * Math.sin(breathPhase);
        graphics.clear();
        const currentLength = baseLength * breathScale;

        const cos = Math.cos(tailRotation);
        const sin = Math.sin(tailRotation);
        const perpX = -sin;
        const perpY = cos;

        const p1 = { x: perpX * (startWidth / 2), y: perpY * (startWidth / 2) };
        const p2 = { x: -perpX * (startWidth / 2), y: -perpY * (startWidth / 2) };
        const endCX = cos * currentLength;
        const endCY = sin * currentLength;
        const p3 = { x: endCX - perpX * (endWidth / 2), y: endCY - perpY * (endWidth / 2) };
        const p4 = { x: endCX + perpX * (endWidth / 2), y: endCY + perpY * (endWidth / 2) };

        const segments = 8;
        for (let i = 0; i < segments; i++) {
            const t1 = i / segments;
            const t2 = (i + 1) / segments;
            const alpha = 0.4 * (1 - t1);
            const sp1 = { x: p1.x + (p4.x - p1.x) * t1, y: p1.y + (p4.y - p1.y) * t1 };
            const sp2 = { x: p2.x + (p3.x - p2.x) * t1, y: p2.y + (p3.y - p2.y) * t1 };
            const ep1 = { x: p1.x + (p4.x - p1.x) * t2, y: p1.y + (p4.y - p1.y) * t2 };
            const ep2 = { x: p2.x + (p3.x - p2.x) * t2, y: p2.y + (p3.y - p2.y) * t2 };
            graphics.beginFill(bgColor, alpha);
            graphics.moveTo(sp1.x, sp1.y);
            graphics.lineTo(sp2.x, sp2.y);
            graphics.lineTo(ep2.x, ep2.y);
            graphics.lineTo(ep1.x, ep1.y);
            graphics.endFill();
        }
    };
    tailGroup.updateAnim();
    return tailGroup;
};

const handleResize = () => {
    const container = getContainerDOM();
    if (!app || !container) return;

    const w = container.clientWidth || 300;
    const h = container.clientHeight || 300;

    app.renderer.resize(w, h);

    activeTagInstances.forEach((tag) => {
        const meta = tag.userData;
        if (!meta) return;

        let newX = w / 2 + meta.radius * Math.cos(meta.angle);
        let newY = h / 2 + meta.radius * Math.sin(meta.angle);

        const safeW = tag.width / 2 + 10;
        const safeH = tag.height / 2 + 10;

        meta.originalX = clamp(newX, safeW, w - safeW);
        meta.originalY = clamp(newY, safeH, h - safeH);
        meta.safeH = safeH; // 更新安全高度

        tag.x = meta.originalX;
    });
};
</script>

<style scoped>
.container {
    width: 100%;
    height: 500rpx;
    position: relative;
    overflow: hidden;
    color: #b9d3ff;
}
</style>
