<script setup lang="ts">
import type { Material, Mesh } from 'three';
import type { SectionBlock } from '~/types/site';
import { resolveAssetUrl } from '~/utils/resolveAssetUrl';

/**
 * 3D 模型展示布局（model3d）
 *
 * config 字段：
 * - model_url：GLB 模型路径（素材库上传，.glb/.gltf）
 * - model_height：展示区高度 px（默认 480）
 * - auto_rotate：是否自动旋转（默认开）
 *
 * three.js 通过动态 import 懒加载：滚动到视口内才初始化，
 * 未命中 3D 区块的页面零开销。渲染器透明背景，跟随区块背景/主题。
 */
const props = defineProps<{ section: SectionBlock }>();

const { overline, title, subtitle, config, isDark, overlineStyle, titleStyle, bodyStyle } = useSectionContent(props.section);

const modelUrl = computed(() => resolveAssetUrl((config.value.model_url as string) || ''));
const viewerHeight = computed(() => {
  const h = Number(config.value.model_height);
  return `${Number.isFinite(h) && h >= 200 ? h : 480}px`;
});
const autoRotate = computed(() => (config.value.auto_rotate as boolean) !== false);

const containerRef = ref<HTMLDivElement | null>(null);
const status = ref<'idle' | 'loading' | 'ready' | 'error'>('idle');

// 可见后才动态加载 three 并初始化，避免拖慢首屏
const { stop: stopObserve } = useIntersectionObserver(
  containerRef,
  entries => {
    const entry = entries[0];
    if (entry?.isIntersecting && status.value === 'idle' && modelUrl.value) {
      status.value = 'loading';
      stopObserve();
      initViewer();
    }
  },
  { rootMargin: '200px' }
);

async function initViewer() {
  if (!containerRef.value || !modelUrl.value) return;
  try {
    // 动态 import：three 相关代码拆分为独立 chunk，按需加载
    const [THREE, { GLTFLoader }, { OrbitControls }] = await Promise.all([
      import('three'),
      import('three/addons/loaders/GLTFLoader.js'),
      import('three/addons/controls/OrbitControls.js')
    ]);

    const container = containerRef.value;
    const width = container.clientWidth;
    const height = container.clientHeight;

    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    renderer.setSize(width, height);
    renderer.toneMapping = THREE.ACESFilmicToneMapping;
    container.appendChild(renderer.domElement);

    const scene = new THREE.Scene();

    const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);

    // 基础布光：半球光 + 主方向光 + 补光，无环境贴图也能呈现合理质感
    scene.add(new THREE.HemisphereLight(0xffffff, 0x333333, 2.2));
    const keyLight = new THREE.DirectionalLight(0xffffff, 2.5);
    keyLight.position.set(3, 5, 4);
    scene.add(keyLight);
    const fillLight = new THREE.DirectionalLight(0xffffff, 0.8);
    fillLight.position.set(-4, 1, -3);
    scene.add(fillLight);

    const loader = new GLTFLoader();
    const gltf = await loader.loadAsync(modelUrl.value);
    const model = gltf.scene;
    scene.add(model);

    // 按包围球自动居中并适配相机距离
    const box = new THREE.Box3().setFromObject(model);
    const sphere = box.getBoundingSphere(new THREE.Sphere());
    const radius = sphere.radius || 1;
    model.position.sub(sphere.center);
    const dist = radius * 2.8;
    camera.position.set(dist * 0.6, dist * 0.4, dist);
    camera.near = radius / 100;
    camera.far = radius * 100;
    camera.updateProjectionMatrix();

    const controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.dampingFactor = 0.08;
    controls.enablePan = false;
    controls.minDistance = radius * 1.4;
    controls.maxDistance = radius * 6;
    controls.autoRotate = autoRotate.value;
    controls.autoRotateSpeed = 1.5;

    status.value = 'ready';

    let rafId = 0;
    const animate = () => {
      rafId = requestAnimationFrame(animate);
      controls.update();
      renderer.render(scene, camera);
    };
    animate();

    // 容器尺寸变化时同步渲染器与相机
    const ro = new ResizeObserver(() => {
      const w = container.clientWidth;
      const h = container.clientHeight;
      if (w > 0 && h > 0) {
        renderer.setSize(w, h);
        camera.aspect = w / h;
        camera.updateProjectionMatrix();
      }
    });
    ro.observe(container);

    // 记录清理句柄，卸载时释放 WebGL 资源
    cleanup = () => {
      cancelAnimationFrame(rafId);
      ro.disconnect();
      controls.dispose();
      model.traverse(obj => {
        const mesh = obj as Mesh;
        if (mesh.isMesh) {
          mesh.geometry.dispose();
          const mats = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
          mats.forEach((m: Material) => m.dispose());
        }
      });
      renderer.dispose();
      renderer.domElement.remove();
    };
  } catch (e) {
    console.error('[model3d] 3D 模型加载失败', e);
    status.value = 'error';
  }
}

let cleanup: (() => void) | null = null;
onBeforeUnmount(() => {
  stopObserve();
  cleanup?.();
});
</script>

<template>
  <div class="section-container py-[var(--sec-py,96px)]">
    <!-- 标题区 -->
    <div v-if="overline || title || subtitle" class="text-center max-w-640px mx-auto mb-56px">
      <p v-if="overline" class="text-14px font-semibold tracking-widest uppercase" :class="isDark ? 'text-primary-300' : 'text-primary'" :style="overlineStyle">
        {{ overline }}
      </p>
      <h2 class="mt-12px font-bold" :class="isDark ? 'text-white' : 'text-gray-900 dark:text-white'" :style="titleStyle">
        {{ title }}
      </h2>
      <p v-if="subtitle" class="mt-16px text-base" :class="isDark ? 'text-gray-400' : 'text-gray-500 dark:text-gray-400'" :style="bodyStyle">
        {{ subtitle }}
      </p>
    </div>

    <!-- 3D 展示区：透明背景跟随区块背景 -->
    <div
      ref="containerRef"
      class="relative w-full overflow-hidden rounded-16px touch-none select-none"
      :style="{ height: viewerHeight }"
    >
      <!-- 加载占位 -->
      <div v-if="status !== 'ready'" class="absolute inset-0 flex flex-col items-center justify-center gap-12px">
        <template v-if="status === 'error'">
          <span class="text-14px" :class="isDark ? 'text-gray-400' : 'text-gray-500'">3D 模型加载失败</span>
        </template>
        <template v-else-if="!modelUrl">
          <span class="text-14px" :class="isDark ? 'text-gray-400' : 'text-gray-500'">未配置 3D 模型</span>
        </template>
        <template v-else>
          <div class="h-24px w-24px animate-spin rounded-full border-2px border-gray-300 border-t-transparent" />
          <span class="text-14px" :class="isDark ? 'text-gray-400' : 'text-gray-500'">模型加载中…</span>
        </template>
      </div>

      <!-- 交互提示 -->
      <div
        v-if="status === 'ready'"
        class="absolute bottom-12px left-1/2 -translate-x-1/2 rounded-full bg-black/40 px-12px py-4px text-12px text-white backdrop-blur"
      >
        拖拽旋转 · 滚轮缩放
      </div>
    </div>
  </div>
</template>
