// Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. All rights reserved.
// 
// this file licensed under the Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
//
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
// PURPOSE.
// See the Mulan PSL v2 for more details.
import { ref, onMounted, onBeforeUnmount } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

interface ThreeViewerOptions {
    partMap: Record<string, THREE.Mesh>;
    categoryMap: Record<string, string[]>;
    getCategory: (n: string) => string;
    onHoverPart: (name: string, e: PointerEvent) => void;
    onHoverClear: () => void;
    onModelLoaded?: () => void;
    onClickPart?: (name: string, e: MouseEvent | PointerEvent) => void;
    onContextMenuPart?: (name: string, e: MouseEvent) => void;
}

interface ThreeViewerApi {
    canvasRef: Ref<HTMLCanvasElement | undefined>;
    mode: Ref<'camera' | 'model'>;
    explodeParts: () => void;
    resetParts: (ms?: number) => void;
    toggleBackground: () => void;
    setCameraControl: () => void;
    setModelControl: () => void;
    resetCameraView: () => void;
    getServerParts: () => Array<THREE.Mesh>;
    getCatTargetCenters: () => Map<string, THREE.Vector3> | null;
    projectToCanvas: (world: THREE.Vector3) => { x: number; y: number };
}

export function useThreeViewer(opts: ThreeViewerOptions): ThreeViewerApi {
    const canvasRef = ref<HTMLCanvasElement>();
    const mode = ref<'camera' | 'model'>('camera');

    // three.js 基础对象
    let scene: THREE.Scene;
    let camera: THREE.PerspectiveCamera;
    let renderer: THREE.WebGLRenderer;
    let controls: OrbitControls;

    // 模型与部件集合
    let modelRoot: THREE.Group | null = null;
    const modelRootInitialPos = new THREE.Vector3();
    const serverParts: Array<THREE.Mesh> = [];
    const initialPositions: Array<THREE.Vector3> = [];
    const originalPositions: Array<THREE.Vector3> = [];

    // 拖拽与 hover 拾取
    const raycaster = new THREE.Raycaster();
    const mouse = new THREE.Vector2();

    const DRAG_MULT = 1.0;
    let partDragPlane = new THREE.Plane();
    let partLastPoint = new THREE.Vector3();
    let selectedPart: THREE.Object3D | null = null;
    // 同类联动拖拽
    let selectedGroup: THREE.Object3D[] = [];

    // 模型整体平移（model 模式）
    let sceneDragPlane = new THREE.Plane();
    let sceneDragLastPoint = new THREE.Vector3();
    let isDraggingScene = false;

    // 抑制拖动后的 click
    let dragMoved = false;

    // 相机初始状态
    const initialCameraState = {
        position: new THREE.Vector3(),
        target: new THREE.Vector3(),
    };

    // explode（按大类）
    let explodeCache: Array<{ mesh: THREE.Object3D; from: THREE.Vector3; to: THREE.Vector3 }> | null =
        null;

    // —— 类标签相关 —— //
    let catTargetCenterMap: Map<string, THREE.Vector3> | null = null;
    let catMeshesMap: Map<string, THREE.Object3D[]> | null = null;
    let suppressClassLabels = false;

    // 渲染循环开关
    let animating = true;

    function animate(): void {
        if (!animating) {
            return;
        }
        requestAnimationFrame(animate);
        controls.update();
        renderer.render(scene, camera);
    }

    function onResize(): void {
        const cvs = canvasRef.value;
        if (!cvs) {
            return;
        }
        const { clientWidth, clientHeight } = cvs;
        if (clientWidth <= 0 || clientHeight <= 0) {
            return;
        }
        camera.aspect = clientWidth / clientHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(clientWidth, clientHeight);
        renderer.setPixelRatio(Math.min(2, window.devicePixelRatio || 1));
    }

    function updateMouse(e: PointerEvent | MouseEvent): void {
        const r = renderer.domElement.getBoundingClientRect();
        const nx = (e.clientX - r.left) / r.width;
        const ny = (e.clientY - r.top) / r.height;
        mouse.x = (nx * 2) - 1;
        mouse.y = (-(ny * 2)) + 1;
    }

    // 只命中当前可见部件
    function hitVisibleParts(): Array<THREE.Intersection> {
        return raycaster.intersectObjects(serverParts.filter((p) => p.visible));
    }

    /** 指针按下：命中部件则进入“同类联动”拖拽；否则 model 模式下整体平移 */
    function onPointerDown(e: PointerEvent): void {
        dragMoved = false;
        updateMouse(e);
        raycaster.setFromCamera(mouse, camera);
        const hits = hitVisibleParts();

        if (hits.length) {
            // 命中部件：记录拖拽参考平面
            selectedPart = hits[0].object;
            controls.enabled = false;
            partDragPlane.setFromNormalAndCoplanarPoint(
                camera.getWorldDirection(new THREE.Vector3()),
                hits[0].point,
            );
            partLastPoint.copy(hits[0].point);

            // 同类联动（按外部分类）
            selectedGroup = [];
            const name = (selectedPart as THREE.Object3D).name || '';
            const cat = opts.getCategory?.(name) || 'Chassis';
            if (cat) {
                const names = opts.categoryMap[cat] || [];
                for (const n of names) {
                    const m = opts.partMap[n];
                    if (m && m.visible !== false) {
                        selectedGroup.push(m);
                    }
                }
            } else {
                selectedGroup.push(selectedPart);
            }
            return;
        }

        // 未命中部件：如果是“model 模式”，开启整体平移
        if (mode.value === 'model' && modelRoot) {
            const planePoint = controls.target.clone();
            sceneDragPlane.setFromNormalAndCoplanarPoint(
                camera.getWorldDirection(new THREE.Vector3()),
                planePoint,
            );
            raycaster.ray.intersectPlane(sceneDragPlane, sceneDragLastPoint);
            isDraggingScene = true;
        }
    }

    /** 指针移动：部件拖拽 or 模型整体平移 or hover */
    function onPointerMove(e: PointerEvent): void {
        updateMouse(e);
        raycaster.setFromCamera(mouse, camera);

        // 拖拽选中部件（同类联动）
        if (selectedPart) {
            const curr = new THREE.Vector3();
            if (raycaster.ray.intersectPlane(partDragPlane, curr)) {
                const deltaWorld = curr.clone().sub(partLastPoint).multiplyScalar(DRAG_MULT);

                // 一旦发生位移，认定为拖动，暂时隐藏类标签
                if (deltaWorld.lengthSq() > 1e-6) {
                    dragMoved = true;
                    if (!suppressClassLabels) {
                        suppressClassLabels = true;
                        catTargetCenterMap?.clear();
                    }
                }

                // 整组移动（世界坐标对齐）
                for (const obj of selectedGroup) {
                    const nowWorld = (obj as THREE.Object3D).getWorldPosition(
                        new THREE.Vector3(),
                    );
                    const targetWorld = nowWorld.add(deltaWorld);
                    setWorldPosition(obj as THREE.Object3D, targetWorld);
                }
                partLastPoint.copy(curr);
            }
            return;
        }

        // 模型整体平移
        if (isDraggingScene && modelRoot) {
            const curr = new THREE.Vector3();
            if (raycaster.ray.intersectPlane(sceneDragPlane, curr)) {
                const delta = curr.clone().sub(sceneDragLastPoint).multiplyScalar(DRAG_MULT);
                if (delta.lengthSq() > 1e-6) {
                    dragMoved = true;
                    if (!suppressClassLabels) {
                        suppressClassLabels = true;
                        catTargetCenterMap?.clear();
                    }
                }
                modelRoot.position.add(delta);
                sceneDragLastPoint.copy(curr);
            }
            return;
        }
    }

    function onPointerUp(): void {
        selectedPart = null;
        selectedGroup = [];
        isDraggingScene = false;
        controls.enabled = mode.value === 'camera';
    }

    /** hover 命中：转给外部渲染悬浮信息 */
    function onPointerHover(e: PointerEvent): void {
        if (selectedPart || isDraggingScene) {
            return;
        }
        updateMouse(e);
        raycaster.setFromCamera(mouse, camera);
        const hits = hitVisibleParts();
        if (!hits.length) {
            opts.onHoverClear();
            return;
        }
        const m = hits[0].object as THREE.Mesh;
        opts.onHoverPart(m.name, e);
    }

    /** click：弹出“部件选择单”（如果刚发生拖动则忽略） */
    function onClick(e: MouseEvent): void {
        if (dragMoved) {
            return;
        }
        updateMouse(e);
        raycaster.setFromCamera(mouse, camera);
        const hits = hitVisibleParts();
        if (!hits.length) {
            return;
        }
        const m = hits[0].object as THREE.Mesh;
        opts.onClickPart?.(m.name, e);
    }

    /** 右键命中部件时，交给外部弹出上下文菜单 */
    function onContextMenu(e: MouseEvent): void {
        e.preventDefault();
        if (dragMoved) {
            return;
        }
        updateMouse(e);
        raycaster.setFromCamera(mouse, camera);
        const hits = hitVisibleParts();
        if (!hits.length) {
            return;
        }
        const m = hits[0].object as THREE.Mesh;
        opts.onContextMenuPart?.(m.name, e);
    }

    /** 简单补间器：0..1 */
    function animateTween(
        ms: number,
        onFrame: (t: number) => void,
        onDone?: () => void,
    ): void {
        const t0 = performance.now();
        const step = (now: number): void => {
            const t = Math.min(1, (now - t0) / Math.max(1, ms));
            onFrame(t);
            if (t < 1) {
                requestAnimationFrame(step);
            } else if (onDone) {
                onDone();
            }
        };
        requestAnimationFrame(step);
    }

    /** 以“世界坐标”设置对象位置（内部换算为 local position） */
    function setWorldPosition(obj: THREE.Object3D, worldPos: THREE.Vector3): void {
        const p = obj.parent;
        if (p) {
            const local = worldPos.clone();
            p.worldToLocal(local);
            obj.position.copy(local);
        } else {
            obj.position.copy(worldPos);
        }
    }

    // 每层间距（第一层→第二层、第二层→第三层...）
    const EXPLODE_Z_GAPS: number[] = [10, 20, 30];

    /**
     * “爆炸”布置（按大类平移到各自槽位）
     * - 首次计算生成 offset cache；后续复用
     * - 完成后回填“实际包围盒中心”，用于类标签绘制
     */
    function explodeParts(): void {
        if (!modelRoot) {
            return;
        }

        // 新一次爆炸：允许显示标签
        suppressClassLabels = false;

        if (!explodeCache) {
            const modelBox = new THREE.Box3().setFromObject(modelRoot);
            const modelCenter = modelBox.getCenter(new THREE.Vector3());
            const modelSize = modelBox.getSize(new THREE.Vector3());

            // 列间距沿 X；Z 为分层；Y 不分层（保持原高度）
            const X_GAP = Math.max(40, modelSize.x * 0.9);

            // 槽位排布（row=Z层，col=X列）
            const slots: Array<{ cat: string; row: number; col: number }> = [
                { cat: 'Cover', row: 0, col: 0 },
                { cat: 'Dimm', row: 1, col: -1 },
                { cat: 'CPU', row: 1, col: 1 },
                { cat: 'Disk', row: 2, col: -1 },
                { cat: 'Mainboard', row: 2, col: 0 },
                { cat: 'Fan', row: 2, col: 1 },
                { cat: 'Psu', row: 3, col: -1 },
                { cat: 'Chassis', row: 3, col: 0 },
            ];

            // 逐层间距
            const rowsUsed = 1 + Math.max(0, ...slots.map((s) => s.row));
            const defaultGap = Math.max(40, modelSize.z * 0.7);
            const gaps: number[] = Array.from(
                { length: Math.max(0, rowsUsed - 1) },
                (_, i) => EXPLODE_Z_GAPS[i] ?? defaultGap,
            );

            // 以模型中心为“中点”均分：row 越大 Z 越大 ⇒ Cover 最上，Chassis 最下
            const totalDepth = gaps.reduce((a, b) => a + b, 0);
            const topZ = modelCenter.z - (totalDepth / 2);
            const rowToZ = (row: number): number => {
                let off = 0;
                for (let i = 0; i < row; i++) {
                    off += gaps[i] || 0;
                }
                return topZ + off;
            };

            const colToX = (col: number): number => modelCenter.x + (col * X_GAP);
            const targetOf = (row: number, col: number): THREE.Vector3 =>
                new THREE.Vector3(colToX(col), modelCenter.y, rowToZ(row));

            // 计算每个类当前中心 & 记录该类 mesh 列表
            const catCurrentCenter = new Map<string, THREE.Vector3>();
            const catMeshes = new Map<string, THREE.Object3D[]>();
            const allCats = new Set([
                'Cover',
                'Dimm',
                'CPU',
                'Disk',
                'Mainboard',
                'Fan',
                'Psu',
                'Chassis',
            ]);
            allCats.forEach((cat) => {
                const names = opts.categoryMap[cat] || [];
                const meshes = names
                    .map((n) => opts.partMap[n])
                    .filter(Boolean) as THREE.Object3D[];
                if (!meshes.length) {
                    return;
                }
                catMeshes.set(cat, meshes);
                const box = new THREE.Box3();
                meshes.forEach((m) => box.expandByObject(m));
                catCurrentCenter.set(cat, box.getCenter(new THREE.Vector3()));
            });
            catMeshesMap = catMeshes;

            // 各类目标中心（用于动画期间的参考）
            const catTargetCenter = new Map<string, THREE.Vector3>();
            for (const { cat, row, col } of slots) {
                if (!catMeshes.has(cat)) {
                    continue;
                }
                catTargetCenter.set(cat, targetOf(row, col));
            }
            catTargetCenterMap = catTargetCenter;

            // 生成缓存：每个 mesh 的 from/to
            explodeCache = [];
            serverParts.forEach((m) => {
                const name = m.name || '';
                const cat = opts.getCategory(name) || 'Chassis';
                const curC = catCurrentCenter.get(cat);
                const tarC = catTargetCenter.get(cat);
                if (!curC || !tarC) {
                    const from = m.position.clone();
                    const to = from.clone().add(new THREE.Vector3(0, 0, defaultGap * 0.25));
                    explodeCache?.push({ mesh: m, from, to });
                    return;
                }
                const offset = tarC.clone().sub(curC);
                const from = m.position.clone();
                const to = from.clone().add(offset);
                explodeCache?.push({ mesh: m, from, to });
            });
        }

        // 动画执行
        animateTween(
            650,
            (t) => {
                const cache = explodeCache;
                if (!cache) {
                    return;
                }
                cache.forEach(({ mesh, from, to }) => {
                    (mesh as THREE.Mesh).position.lerpVectors(from, to, t);
                });
            },
            () => {
                // 爆炸完成后，根据“实际几何中心”再计算一次类标签中心
                if (catMeshesMap) {
                    const newCenters = new Map<string, THREE.Vector3>();
                    catMeshesMap.forEach((meshes, cat) => {
                        const box = new THREE.Box3();
                        meshes.forEach((m) => box.expandByObject(m));
                        newCenters.set(cat, box.getCenter(new THREE.Vector3()));
                    });
                    catTargetCenterMap = newCenters;
                }
            },
        );
    }

    /**
     * 重置部件与模型位置
     */
    function resetParts(ms = 500): void {
        const startRoot = modelRoot ? modelRoot.position.clone() : null;
        animateTween(
            ms,
            (t) => {
                serverParts.forEach((p, i) => {
                    const from = p.position;
                    const to = initialPositions[i];
                    const pos = new THREE.Vector3().lerpVectors(from, to, t);
                    p.position.copy(pos);
                });
                if (modelRoot && startRoot) {
                    const pr = new THREE.Vector3().lerpVectors(
                        startRoot,
                        modelRootInitialPos,
                        t,
                    );
                    modelRoot.position.copy(pr);
                }
            },
            () => {
                serverParts.forEach((p, i) => {
                    p.position.copy(initialPositions[i]);
                    originalPositions[i] = initialPositions[i].clone();
                });
                if (modelRoot) {
                    modelRoot.position.copy(modelRootInitialPos);
                }
                explodeCache = null;
                catTargetCenterMap = null;
                catMeshesMap = null;
                suppressClassLabels = false;
            },
        );
    }

    /** 背景色切换（白/深灰） */
    function toggleBackground(): void {
        const c = scene.background as THREE.Color;
        scene.background =
            c?.getHex() === 0xffffff ? new THREE.Color(0x202020) : new THREE.Color(0xffffff);
    }

    // 模式切换
    const setCameraControl = (): void => {
        mode.value = 'camera';
        controls.enabled = true;
    };
    const setModelControl = (): void => {
        mode.value = 'model';
        controls.enabled = false;
    };

    /** 相机重置到初始化视角 */
    function resetCameraView(): void {
        const sp = camera.position.clone();
        const st = controls.target.clone();
        const ep = initialCameraState.position.clone();
        const et = initialCameraState.target.clone();
        animateTween(500, (t) => {
            camera.position.lerpVectors(sp, ep, t);
            controls.target.lerpVectors(st, et, t);
            controls.update();
        });
    }

    // —— 生命周期：挂载时初始化 three —— //
    onMounted((): void => {
        const cvs = canvasRef.value;
        if (!cvs) {
            return;
        }

        scene = new THREE.Scene();
        scene.background = new THREE.Color(0xffffff);

        const { clientWidth, clientHeight } = cvs;
        camera = new THREE.PerspectiveCamera(
            45,
            Math.max(1, clientWidth) / Math.max(1, clientHeight),
            0.1,
            1000,
        );

        renderer = new THREE.WebGLRenderer({ canvas: cvs, antialias: true });
        renderer.setSize(clientWidth, clientHeight);
        renderer.setPixelRatio(Math.min(2, window.devicePixelRatio || 1));

        if ('outputColorSpace' in renderer) {
            (renderer as any).outputColorSpace = THREE.SRGBColorSpace;
        }
        renderer.toneMapping = THREE.ACESFilmicToneMapping;
        renderer.toneMappingExposure = 1.2;

        controls = new OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;

        // 光照
        scene.add(new THREE.AmbientLight(0xffffff, 0.6));
        const dirTop = new THREE.DirectionalLight(0xffffff, 1.0);
        dirTop.position.set(50, 100, 50);
        scene.add(dirTop);
        const dirBottom = new THREE.DirectionalLight(0xffffff, 0.7);
        dirBottom.position.set(0, -100, 0);
        scene.add(dirBottom);
        scene.add(new THREE.HemisphereLight(0xffffff, 0x888888, 1.0));

        // 加载 GLB 模型
        const MODEL_URL = '/UI/Static/assets/server_model.gltf';
        new GLTFLoader().load(
            MODEL_URL,
            (gltf) => {
                const model = gltf.scene;
                modelRoot = new THREE.Group();
                modelRoot.add(model);
                scene.add(modelRoot);
                modelRootInitialPos.copy(modelRoot.position);

                // 相机初始视角
                const box = new THREE.Box3().setFromObject(model);
                const center = new THREE.Vector3();
                const size = new THREE.Vector3();
                box.getCenter(center);
                box.getSize(size);
                const dist = Math.max(size.x, size.y, size.z) * 1.5;
                camera.position.copy(center.clone().add(new THREE.Vector3(0, size.y * 3, dist)));
                camera.lookAt(center);
                controls.target.copy(center);
                controls.update();
                initialCameraState.position.copy(camera.position);
                initialCameraState.target.copy(controls.target);

                // 遍历 Mesh，填充 serverParts/初始位置/分类映射
                model.traverse((c) => {
                    if ((c as THREE.Mesh).isMesh) {
                        const m = c as THREE.Mesh;
                        const n = m.name || '';
                        const mat = m.material as THREE.MeshStandardMaterial;

                        // 纹理色彩空间修正
                        [mat.map, mat.emissiveMap].forEach((tx: any) => {
                            if (tx && 'colorSpace' in tx) {
                                (tx as any).colorSpace = THREE.SRGBColorSpace;
                                tx.needsUpdate = true;
                            }
                        });
                        [mat.roughnessMap, mat.metalnessMap, mat.normalMap].forEach((tx: any) => {
                            if (tx) {
                                tx.needsUpdate = true;
                            }
                        });

                        serverParts.push(m);
                        initialPositions.push(m.position.clone());
                        originalPositions.push(m.position.clone());

                        // 对外暴露的 partMap/categoryMap
                        opts.partMap[n] = m;
                        const cat = opts.getCategory(n);
                        (opts.categoryMap[cat] = opts.categoryMap[cat] || []).push(n);
                    }
                });

                // 回调 + 启动渲染循环
                opts.onModelLoaded?.();
                animate();
            },
            undefined,
            () => {
            },
        );

        // 事件绑定
        window.addEventListener('resize', onResize);
        const dom = cvs;
        dom.addEventListener('pointerdown', onPointerDown);
        dom.addEventListener('pointermove', onPointerMove);
        dom.addEventListener('pointerup', onPointerUp);
        dom.addEventListener('pointermove', onPointerHover);
        dom.addEventListener('click', onClick);
        dom.addEventListener('contextmenu', onContextMenu);
    });

    // 卸载清理
    onBeforeUnmount((): void => {
        animating = false;
        window.removeEventListener('resize', onResize);
        if (renderer) {
            renderer.domElement?.removeEventListener('pointerdown', onPointerDown);
            renderer.domElement?.removeEventListener('pointermove', onPointerMove);
            renderer.domElement?.removeEventListener('pointerup', onPointerUp);
            renderer.domElement?.removeEventListener('pointermove', onPointerHover);
            renderer.domElement?.removeEventListener('click', onClick);
            renderer.domElement?.removeEventListener('contextmenu', onContextMenu);
        }
        serverParts.forEach((m) => {
            if (m.geometry) {
                m.geometry.dispose();
            }
            const mat = m.material;
            if (Array.isArray(mat)) {
                mat.forEach((mm) => mm.dispose());
            } else {
                (mat as any)?.dispose?.();
            }
        });
        controls?.dispose();
        renderer?.dispose();
    });

    /** 提供：当前可用于绘制“类标签”的世界坐标中心（爆炸后） */
    function getCatTargetCenters(): Map<string, THREE.Vector3> | null {
        if (suppressClassLabels || !catTargetCenterMap) {
            return null;
        }
        const cp = new Map<string, THREE.Vector3>();
        catTargetCenterMap.forEach((v, k) => cp.set(k, v.clone()));
        return cp;
    }

    /** 世界坐标投影到画布像素坐标 */
    function projectToCanvas(world: THREE.Vector3): { x: number; y: number } {
        const v = world.clone().project(camera);
        const dom = renderer.domElement;
        const sx = (v.x * 0.5) + 0.5;
        const sy = (-v.y * 0.5) + 0.5;
        const x = sx * dom.clientWidth;
        const y = sy * dom.clientHeight;
        return { x, y };
    }

    return {
        canvasRef,
        mode,
        explodeParts,
        resetParts,
        toggleBackground,
        setCameraControl,
        setModelControl,
        resetCameraView,
        getServerParts: (): Array<THREE.Mesh> => serverParts,
        getCatTargetCenters,
        projectToCanvas,
    };
}
