<template>
  <div class="model-viewer-page">
    <!-- Animated Background -->
    <div class="bg-animation">
      <div class="bg-gradient bg-gradient-1"></div>
      <div class="bg-gradient bg-gradient-2"></div>
      <div class="bg-gradient bg-gradient-3"></div>
    </div>

    <!-- Header -->
    <div class="page-header">
      <div class="header-left">
        <a-button type="text" class="back-btn" @click="handleBack">
          <template #icon><ArrowLeftOutlined /></template>
        </a-button>
        <span class="title">3D 模型预览</span>
        <span class="live-badge">
          <span class="pulse-dot"></span>
          LIVE
        </span>
      </div>
      <div class="header-right">
        <a-button type="text" class="action-btn" @click="resetCamera">
          <template #icon><AimOutlined /></template>
          重置视角
        </a-button>
        <a-button type="text" class="action-btn" @click="toggleFullscreen">
          <template #icon
            ><FullscreenOutlined v-if="!isFullscreen" /><FullscreenExitOutlined
              v-else
          /></template>
          {{ isFullscreen ? "退出全屏" : "全屏" }}
        </a-button>
      </div>
    </div>

    <!-- 3D Viewer -->
    <div class="viewer-content">
      <div ref="containerRef" class="model-canvas"></div>

      <!-- Loading -->
      <div v-if="loading" class="loading-overlay">
        <div class="loader">
          <div class="loader-ring"></div>
          <div class="loader-ring"></div>
          <div class="loader-ring"></div>
          <span class="loader-text">正在加载模型...</span>
        </div>
      </div>

      <!-- Error -->
      <div v-if="error" class="error-overlay">
        <div class="error-card">
          <div class="error-icon">!</div>
          <h3>模型加载失败</h3>
          <p>{{ error }}</p>
          <div class="error-actions">
            <a-button type="primary" class="gradient-btn" @click="handleRetry"
              >重试</a-button
            >
            <a-button class="outline-btn" @click="handleBack">返回</a-button>
          </div>
        </div>
      </div>

      <!-- Model Info (左侧) -->
      <div v-if="!loading && !error && modelInfo" class="model-info">
        <div class="info-header">
          <span class="info-icon"><AppstoreOutlined /></span>
          <span class="info-title">模型信息</span>
        </div>
        <div class="info-body">
          <div class="info-item">
            <span class="label">尺寸</span>
            <span class="value">{{ modelInfo.size }}</span>
          </div>
          <div class="info-item">
            <span class="label">顶点数</span>
            <span class="value highlight">{{ modelInfo.vertices }}</span>
          </div>
          <div class="info-item">
            <span class="label">面数</span>
            <span class="value highlight">{{ modelInfo.faces }}</span>
          </div>
        </div>
      </div>

      <!-- Scene Settings Panel (右侧) -->
      <div v-if="!loading && !error" class="scene-settings">
        <div
          class="settings-header"
          @click="settingsPanelOpen = !settingsPanelOpen"
        >
          <span class="settings-title"> <SettingOutlined /> 场景设置 </span>
          <span class="collapse-icon" :class="{ 'is-open': settingsPanelOpen }">
            <RightOutlined />
          </span>
        </div>

        <div v-show="settingsPanelOpen" class="settings-body">
          <!-- 背景设置 -->
          <div class="settings-group">
            <div class="group-title">背景</div>
            <div class="setting-item">
              <span class="setting-label">背景色</span>
              <input
                type="color"
                v-model="sceneSettings.backgroundColor"
                @input="updateBackgroundColor"
                class="color-picker"
              />
            </div>
            <div class="setting-item">
              <span class="setting-label">显示网格</span>
              <a-switch
                v-model:checked="sceneSettings.showGrid"
                size="small"
                @change="updateGridVisibility"
              />
            </div>
          </div>

          <!-- 环境光设置 -->
          <div class="settings-group">
            <div class="group-title">环境光</div>
            <div class="setting-item">
              <span class="setting-label">强度</span>
              <div class="slider-wrapper">
                <a-slider
                  v-model:value="sceneSettings.ambientLight.intensity"
                  :min="0"
                  :max="2"
                  :step="0.1"
                  @change="updateAmbientLight"
                />
                <span class="slider-value">{{
                  sceneSettings.ambientLight.intensity.toFixed(1)
                }}</span>
              </div>
            </div>
            <div class="setting-item">
              <span class="setting-label">颜色</span>
              <input
                type="color"
                v-model="sceneSettings.ambientLight.color"
                @input="updateAmbientLight"
                class="color-picker"
              />
            </div>
          </div>

          <!-- 主光源设置 -->
          <div class="settings-group">
            <div class="group-title">主光源</div>
            <div class="setting-item">
              <span class="setting-label">强度</span>
              <div class="slider-wrapper">
                <a-slider
                  v-model:value="sceneSettings.mainLight.intensity"
                  :min="0"
                  :max="3"
                  :step="0.1"
                  @change="updateMainLight"
                />
                <span class="slider-value">{{
                  sceneSettings.mainLight.intensity.toFixed(1)
                }}</span>
              </div>
            </div>
            <div class="setting-item">
              <span class="setting-label">颜色</span>
              <input
                type="color"
                v-model="sceneSettings.mainLight.color"
                @input="updateMainLight"
                class="color-picker"
              />
            </div>
            <div class="setting-item">
              <span class="setting-label">水平角度</span>
              <div class="slider-wrapper">
                <a-slider
                  v-model:value="sceneSettings.mainLight.horizontalAngle"
                  :min="0"
                  :max="360"
                  :step="5"
                  @change="updateMainLightPosition"
                />
                <span class="slider-value"
                  >{{ sceneSettings.mainLight.horizontalAngle }}°</span
                >
              </div>
            </div>
            <div class="setting-item">
              <span class="setting-label">垂直角度</span>
              <div class="slider-wrapper">
                <a-slider
                  v-model:value="sceneSettings.mainLight.verticalAngle"
                  :min="0"
                  :max="90"
                  :step="5"
                  @change="updateMainLightPosition"
                />
                <span class="slider-value"
                  >{{ sceneSettings.mainLight.verticalAngle }}°</span
                >
              </div>
            </div>
          </div>

          <!-- 补光设置 -->
          <div class="settings-group">
            <div class="group-title">补光</div>
            <div class="setting-item">
              <span class="setting-label">强度</span>
              <div class="slider-wrapper">
                <a-slider
                  v-model:value="sceneSettings.fillLight.intensity"
                  :min="0"
                  :max="2"
                  :step="0.1"
                  @change="updateFillLight"
                />
                <span class="slider-value">{{
                  sceneSettings.fillLight.intensity.toFixed(1)
                }}</span>
              </div>
            </div>
            <div class="setting-item">
              <span class="setting-label">颜色</span>
              <input
                type="color"
                v-model="sceneSettings.fillLight.color"
                @input="updateFillLight"
                class="color-picker"
              />
            </div>
          </div>

          <!-- 聚光灯设置 -->
          <div class="settings-group">
            <div class="group-title">聚光灯</div>
            <div class="setting-item">
              <span class="setting-label">启用</span>
              <a-switch
                v-model:checked="sceneSettings.spotLight.enabled"
                size="small"
                @change="updateSpotLight"
              />
            </div>
            <template v-if="sceneSettings.spotLight.enabled">
              <div class="setting-item">
                <span class="setting-label">强度</span>
                <div class="slider-wrapper">
                  <a-slider
                    v-model:value="sceneSettings.spotLight.intensity"
                    :min="0"
                    :max="3"
                    :step="0.1"
                    @change="updateSpotLight"
                  />
                  <span class="slider-value">{{
                    sceneSettings.spotLight.intensity.toFixed(1)
                  }}</span>
                </div>
              </div>
              <div class="setting-item">
                <span class="setting-label">颜色</span>
                <input
                  type="color"
                  v-model="sceneSettings.spotLight.color"
                  @input="updateSpotLight"
                  class="color-picker"
                />
              </div>
              <div class="setting-item">
                <span class="setting-label">光束角度</span>
                <div class="slider-wrapper">
                  <a-slider
                    v-model:value="sceneSettings.spotLight.angle"
                    :min="10"
                    :max="90"
                    :step="5"
                    @change="updateSpotLight"
                  />
                  <span class="slider-value"
                    >{{ sceneSettings.spotLight.angle }}°</span
                  >
                </div>
              </div>
            </template>
          </div>

          <!-- 渲染设置 -->
          <div class="settings-group">
            <div class="group-title">渲染</div>
            <div class="setting-item">
              <span class="setting-label">曝光度</span>
              <div class="slider-wrapper">
                <a-slider
                  v-model:value="sceneSettings.render.exposure"
                  :min="0.1"
                  :max="3"
                  :step="0.1"
                  @change="updateRenderSettings"
                />
                <span class="slider-value">{{
                  sceneSettings.render.exposure.toFixed(1)
                }}</span>
              </div>
            </div>
            <div class="setting-item">
              <span class="setting-label">色调映射</span>
              <a-select
                v-model:value="sceneSettings.render.toneMapping"
                size="small"
                style="width: 120px"
                @change="updateRenderSettings"
              >
                <a-select-option value="ACES">ACES</a-select-option>
                <a-select-option value="Linear">线性</a-select-option>
                <a-select-option value="Reinhard">Reinhard</a-select-option>
                <a-select-option value="Cineon">Cineon</a-select-option>
              </a-select>
            </div>
          </div>
        </div>

        <!-- 重置按钮 -->
        <div v-show="settingsPanelOpen" class="settings-footer">
          <a-button size="small" class="reset-btn" @click="resetSettings"
            >重置默认</a-button
          >
        </div>
      </div>

      <!-- Controls Info -->
      <div v-if="!loading && !error" class="controls-info">
        <div class="control-hint">
          <span class="hint-item"><span class="key">左键</span>旋转</span>
          <span class="hint-item"><span class="key">右键</span>平移</span>
          <span class="hint-item"><span class="key">滚轮</span>缩放</span>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, onMounted, onUnmounted } from "vue"
import { useRouter, useRoute } from "vue-router"
import {
  ArrowLeftOutlined,
  AimOutlined,
  FullscreenOutlined,
  FullscreenExitOutlined,
  SettingOutlined,
  RightOutlined,
  AppstoreOutlined,
} from "@ant-design/icons-vue"

// @ts-ignore
import * as THREE from "three"
// @ts-ignore
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"
// @ts-ignore
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js"
// @ts-ignore
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader.js"
// @ts-ignore
import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader.js"
// @ts-ignore
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"

const router = useRouter()
const route = useRoute()

const containerRef = ref<HTMLDivElement | null>(null)
const loading = ref(true)
const error = ref<string | null>(null)
const isFullscreen = ref(false)
const modelInfo = ref<{ size: string; vertices: string; faces: string } | null>(
  null
)
const settingsPanelOpen = ref(true)

// 场景设置默认值
const defaultSettings = {
  backgroundColor: "#f5f5f5",
  showGrid: false,
  ambientLight: {
    intensity: 0.4,
    color: "#ffffff",
  },
  mainLight: {
    intensity: 1.5,
    color: "#ffffff",
    horizontalAngle: 45,
    verticalAngle: 60,
  },
  fillLight: {
    intensity: 0.8,
    color: "#ffffff",
  },
  spotLight: {
    enabled: false,
    intensity: 1.0,
    color: "#ffffff",
    angle: 30,
  },
  render: {
    exposure: 1.2,
    toneMapping: "ACES",
  },
}

// 场景设置
const sceneSettings = reactive(JSON.parse(JSON.stringify(defaultSettings)))

let scene: THREE.Scene | null = null
let camera: THREE.PerspectiveCamera | null = null
let renderer: THREE.WebGLRenderer | null = null
let controls: OrbitControls | null = null
let model: THREE.Group | null = null
let animationId: number | null = null
let dracoLoader: DRACOLoader | null = null
let initialCameraPosition: THREE.Vector3 | null = null

// 灯光引用
let ambientLight: THREE.AmbientLight | null = null
let mainLight: THREE.DirectionalLight | null = null
let fillLight: THREE.DirectionalLight | null = null
let spotLight: THREE.SpotLight | null = null
let gridHelper: THREE.GridHelper | null = null

// 获取模型 URL
const modelUrl = ref((route.query.url as string) || "")
console.log("模型查看器 - URL:", modelUrl.value)

// 返回上一页
const handleBack = () => {
  router.back()
}

// 重置相机视角
const resetCamera = () => {
  if (camera && initialCameraPosition && controls) {
    camera.position.copy(initialCameraPosition)
    controls.target.set(0, 0, 0)
    controls.update()
  }
}

// 切换全屏
const toggleFullscreen = () => {
  if (!document.fullscreenElement) {
    document.documentElement.requestFullscreen()
    isFullscreen.value = true
  } else {
    document.exitFullscreen()
    isFullscreen.value = false
  }
}

// 更新背景色
const updateBackgroundColor = () => {
  if (scene) {
    scene.background = new THREE.Color(sceneSettings.backgroundColor)
  }
}

// 更新网格显示
const updateGridVisibility = () => {
  if (gridHelper) {
    gridHelper.visible = sceneSettings.showGrid
  }
}

// 更新环境光
const updateAmbientLight = () => {
  if (ambientLight) {
    ambientLight.intensity = sceneSettings.ambientLight.intensity
    ambientLight.color.set(sceneSettings.ambientLight.color)
  }
}

// 更新主光源
const updateMainLight = () => {
  if (mainLight) {
    mainLight.intensity = sceneSettings.mainLight.intensity
    mainLight.color.set(sceneSettings.mainLight.color)
  }
}

// 更新主光源位置
const updateMainLightPosition = () => {
  if (mainLight) {
    const distance = 30
    const hAngle = (sceneSettings.mainLight.horizontalAngle * Math.PI) / 180
    const vAngle = (sceneSettings.mainLight.verticalAngle * Math.PI) / 180

    mainLight.position.set(
      distance * Math.cos(vAngle) * Math.cos(hAngle),
      distance * Math.sin(vAngle),
      distance * Math.cos(vAngle) * Math.sin(hAngle)
    )
  }
}

// 更新补光
const updateFillLight = () => {
  if (fillLight) {
    fillLight.intensity = sceneSettings.fillLight.intensity
    fillLight.color.set(sceneSettings.fillLight.color)
  }
}

// 更新聚光灯
const updateSpotLight = () => {
  if (!scene) return

  if (sceneSettings.spotLight.enabled) {
    if (!spotLight) {
      spotLight = new THREE.SpotLight(
        sceneSettings.spotLight.color,
        sceneSettings.spotLight.intensity
      )
      spotLight.position.set(0, 30, 0)
      spotLight.angle = (sceneSettings.spotLight.angle * Math.PI) / 180
      spotLight.penumbra = 0.3
      spotLight.castShadow = true
      scene.add(spotLight)

      // 添加聚光灯目标
      spotLight.target.position.set(0, 0, 0)
      scene.add(spotLight.target)
    } else {
      spotLight.intensity = sceneSettings.spotLight.intensity
      spotLight.color.set(sceneSettings.spotLight.color)
      spotLight.angle = (sceneSettings.spotLight.angle * Math.PI) / 180
    }
  } else {
    if (spotLight) {
      scene.remove(spotLight)
      scene.remove(spotLight.target)
      spotLight.dispose()
      spotLight = null
    }
  }
}

// 更新渲染设置
const updateRenderSettings = () => {
  if (renderer) {
    renderer.toneMappingExposure = sceneSettings.render.exposure

    const toneMappingMap: Record<string, THREE.ToneMapping> = {
      ACES: THREE.ACESFilmicToneMapping,
      Linear: THREE.LinearToneMapping,
      Reinhard: THREE.ReinhardToneMapping,
      Cineon: THREE.CineonToneMapping,
    }
    renderer.toneMapping =
      toneMappingMap[sceneSettings.render.toneMapping] ||
      THREE.ACESFilmicToneMapping
  }
}

// 重置设置
const resetSettings = () => {
  Object.assign(sceneSettings, JSON.parse(JSON.stringify(defaultSettings)))

  updateBackgroundColor()
  updateGridVisibility()
  updateAmbientLight()
  updateMainLight()
  updateMainLightPosition()
  updateFillLight()
  updateSpotLight()
  updateRenderSettings()
}

// 初始化场景
const initScene = () => {
  if (!containerRef.value) return

  // 创建场景
  scene = new THREE.Scene()
  scene.background = new THREE.Color(sceneSettings.backgroundColor)

  // 创建相机
  const width = containerRef.value.clientWidth
  const height = containerRef.value.clientHeight
  camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 10000)
  camera.position.set(10, 10, 10)
  camera.lookAt(0, 0, 0)

  // 创建渲染器 - 高质量设置
  renderer = new THREE.WebGLRenderer({
    antialias: true,
    alpha: true,
    powerPreference: "high-performance",
    precision: "highp",
  })
  renderer.setSize(width, height)
  renderer.setPixelRatio(window.devicePixelRatio)
  renderer.shadowMap.enabled = true
  renderer.shadowMap.type = THREE.PCFSoftShadowMap
  renderer.toneMapping = THREE.ACESFilmicToneMapping
  renderer.toneMappingExposure = sceneSettings.render.exposure
  renderer.outputColorSpace = THREE.SRGBColorSpace
  containerRef.value.appendChild(renderer.domElement)

  // 创建控制器
  controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true
  controls.dampingFactor = 0.05
  controls.minDistance = 0.1
  controls.maxDistance = 1000
  controls.enablePan = true
  controls.panSpeed = 1
  controls.rotateSpeed = 1

  // 添加半球光 - 更自然的环境光
  const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.6)
  hemiLight.position.set(0, 100, 0)
  scene.add(hemiLight)

  // 添加环境光
  ambientLight = new THREE.AmbientLight(
    sceneSettings.ambientLight.color,
    sceneSettings.ambientLight.intensity
  )
  scene.add(ambientLight)

  // 添加主方向光
  mainLight = new THREE.DirectionalLight(
    sceneSettings.mainLight.color,
    sceneSettings.mainLight.intensity
  )
  updateMainLightPosition()
  mainLight.castShadow = true
  mainLight.shadow.mapSize.width = 4096
  mainLight.shadow.mapSize.height = 4096
  mainLight.shadow.camera.near = 0.1
  mainLight.shadow.camera.far = 500
  mainLight.shadow.bias = -0.0001
  scene.add(mainLight)

  // 添加补光 - 从侧面
  fillLight = new THREE.DirectionalLight(
    sceneSettings.fillLight.color,
    sceneSettings.fillLight.intensity
  )
  fillLight.position.set(-10, 10, -10)
  scene.add(fillLight)

  // 添加背光 - 增加立体感
  const backLight = new THREE.DirectionalLight(0xffffff, 0.5)
  backLight.position.set(0, 10, -20)
  scene.add(backLight)

  // 添加底部补光
  const bottomLight = new THREE.DirectionalLight(0xffffff, 0.2)
  bottomLight.position.set(0, -10, 0)
  scene.add(bottomLight)

  // 添加网格 - 使用蓝色调
  gridHelper = new THREE.GridHelper(100, 100, 0x0958d9, 0x1677ff)
  gridHelper.visible = sceneSettings.showGrid
  scene.add(gridHelper)

  // 开始渲染
  animate()
}

// 加载模型
const loadModel = async () => {
  if (!scene || !modelUrl.value) {
    error.value = "模型 URL 不存在"
    loading.value = false
    console.error("模型加载失败: URL为空", {
      scene: !!scene,
      url: modelUrl.value,
    })
    return
  }

  // 检查文件扩展名（支持GLB、GLTF和ZIP格式，ZIP会在后端解压）
  // 从URL中提取文件扩展名（忽略查询参数）
  const urlWithoutQuery = modelUrl.value.split("?")[0].toLowerCase()
  const supportedExtensions = [".glb", ".gltf", ".zip", ".obj"]
  const isSupported = supportedExtensions.some((ext) =>
    urlWithoutQuery.endsWith(ext)
  )
  if (!isSupported) {
    error.value = `不支持的文件格式，目前仅支持 GLB/GLTF/ZIP/OBJ 格式`
    loading.value = false
    console.error("不支持的文件格式:", modelUrl.value)
    return
  }

  loading.value = true
  error.value = null

  try {
    console.log("开始加载模型，URL:", modelUrl.value)

    // 判断是本地URL还是外部URL
    // 本地URL: /api/uploads/... 或相对路径
    // 外部URL: http:// 或 https:// 开头的腾讯云COS链接
    const isLocalUrl =
      modelUrl.value.startsWith("/api/") ||
      modelUrl.value.startsWith("/uploads/") ||
      (!modelUrl.value.startsWith("http://") &&
        !modelUrl.value.startsWith("https://"))

    let loadUrl: string
    if (isLocalUrl) {
      // 本地URL直接使用，不需要代理
      loadUrl = modelUrl.value
      console.log("使用本地URL:", loadUrl)
    } else {
      // 外部URL使用后端代理解决CORS问题
      loadUrl = `/api/ai-3d/proxy-model?url=${encodeURIComponent(modelUrl.value)}`
      console.log("使用代理URL:", loadUrl)
    }

    // 移除旧模型
    if (model && scene) {
      scene.remove(model)
      disposeModel(model)
    }

    // 判断模型类型
    let modelType: "glb" | "gltf" | "obj" = "glb" // 默认类型

    // 先根据 URL 扩展名判断
    if (urlWithoutQuery.endsWith(".obj")) {
      modelType = "obj"
    } else if (urlWithoutQuery.endsWith(".gltf")) {
      modelType = "gltf"
    }

    // 如果是 ZIP 或代理请求，需要获取实际类型
    const needsTypeDetection =
      urlWithoutQuery.endsWith(".zip") || loadUrl.includes("proxy-model")

    if (needsTypeDetection) {
      // 对于 ZIP 或代理请求，先获取数据并检查类型头
      console.log("获取模型数据以检测类型...")
      const response = await fetch(loadUrl)

      if (!response.ok) {
        throw new Error(`模型加载失败: ${response.status} ${response.statusText}`)
      }

      // 检查 X-Model-Type 头部
      const xModelType = response.headers.get("X-Model-Type")
      if (xModelType === "obj") {
        modelType = "obj"
      } else if (xModelType === "gltf") {
        modelType = "gltf"
      }

      console.log("检测到模型类型:", modelType)

      // 获取数据
      const arrayBuffer = await response.arrayBuffer()

      if (modelType === "obj") {
        // OBJ 是文本格式
        const decoder = new TextDecoder()
        const objText = decoder.decode(arrayBuffer)
        const objLoader = new OBJLoader()
        model = objLoader.parse(objText)

        // 为 OBJ 模型添加默认材质
        model.traverse((child: THREE.Object3D) => {
          if (child instanceof THREE.Mesh) {
            if (
              !child.material ||
              (child.material as THREE.Material).type === "MeshBasicMaterial"
            ) {
              child.material = new THREE.MeshStandardMaterial({
                color: 0x888888,
                metalness: 0.3,
                roughness: 0.7,
              })
            }
          }
        })
      } else {
        // GLB/GLTF 格式
        const gltfLoader = new GLTFLoader()

        if (!dracoLoader) {
          dracoLoader = new DRACOLoader()
          dracoLoader.setDecoderPath(
            "https://www.gstatic.com/draco/versioned/decoders/1.5.6/"
          )
        }
        gltfLoader.setDRACOLoader(dracoLoader)

        // 使用 parse 方法解析已获取的数据
        const gltf = await new Promise<any>((resolve, reject) => {
          gltfLoader.parse(arrayBuffer, "", resolve, reject)
        })
        model = gltf.scene
      }
    } else {
      // 非 ZIP 且非代理，直接使用 loader 加载
      console.log("检测到模型类型:", modelType)

      if (modelType === "obj") {
        const objLoader = new OBJLoader()
        model = await objLoader.loadAsync(loadUrl)

        model.traverse((child: THREE.Object3D) => {
          if (child instanceof THREE.Mesh) {
            if (
              !child.material ||
              (child.material as THREE.Material).type === "MeshBasicMaterial"
            ) {
              child.material = new THREE.MeshStandardMaterial({
                color: 0x888888,
                metalness: 0.3,
                roughness: 0.7,
              })
            }
          }
        })
      } else {
        const gltfLoader = new GLTFLoader()

        if (!dracoLoader) {
          dracoLoader = new DRACOLoader()
          dracoLoader.setDecoderPath(
            "https://www.gstatic.com/draco/versioned/decoders/1.5.6/"
          )
        }
        gltfLoader.setDRACOLoader(dracoLoader)

        const gltf = await gltfLoader.loadAsync(loadUrl)
        model = gltf.scene
      }
    }

    // 计算模型信息
    let vertexCount = 0
    let faceCount = 0
    model.traverse((child: THREE.Object3D) => {
      if (child instanceof THREE.Mesh) {
        const geometry = child.geometry
        if (geometry.attributes.position) {
          vertexCount += geometry.attributes.position.count
        }
        if (geometry.index) {
          faceCount += geometry.index.count / 3
        } else if (geometry.attributes.position) {
          faceCount += geometry.attributes.position.count / 3
        }
      }
    })

    // 计算边界框
    const box = new THREE.Box3().setFromObject(model)
    const center = box.getCenter(new THREE.Vector3())
    const size = box.getSize(new THREE.Vector3())

    // 设置模型信息
    modelInfo.value = {
      size: `${size.x.toFixed(2)} x ${size.y.toFixed(2)} x ${size.z.toFixed(2)}`,
      vertices: vertexCount.toLocaleString(),
      faces: Math.round(faceCount).toLocaleString(),
    }

    // 居中模型
    model.position.sub(center)

    // 计算相机距离，确保模型完整显示
    if (camera && containerRef.value) {
      const maxDim = Math.max(size.x, size.y, size.z)

      // 根据容器宽高比调整
      const containerWidth = containerRef.value.clientWidth
      const containerHeight = containerRef.value.clientHeight
      const aspect = containerWidth / containerHeight

      // 使用 FOV 计算理想距离
      const fovRad = (camera.fov * Math.PI) / 180

      // 计算需要的距离：确保模型在垂直和水平方向都能完整显示
      const distanceForHeight = size.y / 2 / Math.tan(fovRad / 2)
      const distanceForWidth = size.x / 2 / Math.tan(fovRad / 2) / aspect
      const distanceForDepth = size.z / 2 / Math.tan(fovRad / 2)

      // 取最大距离，并添加适当的边距（1.8倍，让模型显示更大）
      const baseDistance = Math.max(
        distanceForHeight,
        distanceForWidth,
        distanceForDepth
      )
      const cameraDistance = Math.max(baseDistance * 1.8, maxDim * 1.8, 5)

      console.log("模型尺寸:", size)
      console.log("计算的相机距离:", cameraDistance)

      // 设置相机位置（从斜上方45度角观看）
      const angle = Math.PI / 4
      camera.position.set(
        cameraDistance * Math.cos(angle),
        cameraDistance * 0.5,
        cameraDistance * Math.sin(angle)
      )
      camera.lookAt(0, 0, 0)

      // 保存初始位置
      initialCameraPosition = camera.position.clone()

      // 更新控制器
      if (controls) {
        controls.target.set(0, 0, 0)
        controls.update()
      }
    }

    scene.add(model)
    loading.value = false
    console.log("模型加载成功")
  } catch (err: any) {
    console.error("模型加载失败:", err)

    // 提取更详细的错误信息
    let errorMessage = "无法加载模型文件"

    if (err.message) {
      errorMessage = err.message
    } else if (err.response?.data?.message) {
      errorMessage = err.response.data.message
    } else if (typeof err === "string") {
      errorMessage = err
    }

    // 如果是网络错误或代理错误，提供更友好的提示
    if (errorMessage.includes("代理请求失败") || errorMessage.includes("ZIP")) {
      errorMessage = `模型文件处理失败: ${errorMessage}`
    } else if (
      errorMessage.includes("timeout") ||
      errorMessage.includes("超时")
    ) {
      errorMessage = "模型文件下载超时，请检查网络连接或稍后重试"
    } else if (
      errorMessage.includes("404") ||
      errorMessage.includes("不存在")
    ) {
      errorMessage = "模型文件不存在或已被删除"
    }

    error.value = errorMessage
    loading.value = false
  }
}

// 释放模型资源
const disposeModel = (object: THREE.Object3D) => {
  object.traverse((child: THREE.Object3D) => {
    if (child instanceof THREE.Mesh) {
      child.geometry.dispose()
      if (Array.isArray(child.material)) {
        child.material.forEach((mat: THREE.Material) => mat.dispose())
      } else {
        child.material.dispose()
      }
    }
  })
}

// 动画循环
const animate = () => {
  const render = () => {
    if (controls && camera && renderer && scene) {
      controls.update()
      renderer.render(scene, camera)
      animationId = requestAnimationFrame(render)
    }
  }
  render()
}

// 窗口大小变化
const handleResize = () => {
  if (!containerRef.value || !camera || !renderer) return

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

  camera.aspect = width / height
  camera.updateProjectionMatrix()
  renderer.setSize(width, height)
}

// 全屏变化
const handleFullscreenChange = () => {
  isFullscreen.value = !!document.fullscreenElement
  setTimeout(handleResize, 100)
}

// 释放场景
const disposeScene = () => {
  if (animationId) {
    cancelAnimationFrame(animationId)
    animationId = null
  }

  if (model && scene) {
    disposeModel(model)
    scene.remove(model)
    model = null
  }

  if (spotLight && scene) {
    scene.remove(spotLight)
    scene.remove(spotLight.target)
    spotLight.dispose()
    spotLight = null
  }

  if (controls) {
    controls.dispose()
    controls = null
  }

  if (renderer && containerRef.value) {
    renderer.dispose()
    if (renderer.domElement.parentNode) {
      containerRef.value.removeChild(renderer.domElement)
    }
    renderer = null
  }

  if (dracoLoader) {
    dracoLoader.dispose()
    dracoLoader = null
  }

  scene = null
  camera = null
  ambientLight = null
  mainLight = null
  fillLight = null
  gridHelper = null
}

// 重试
const handleRetry = () => {
  error.value = null
  loadModel()
}

onMounted(() => {
  window.addEventListener("resize", handleResize)
  document.addEventListener("fullscreenchange", handleFullscreenChange)

  initScene()
  loadModel()
})

onUnmounted(() => {
  window.removeEventListener("resize", handleResize)
  document.removeEventListener("fullscreenchange", handleFullscreenChange)
  disposeScene()
})
</script>

<style scoped lang="scss">
// ==========================================
// 蓝色主题色彩方案 - 与系统统一
// ==========================================
$primary: #0958d9;
$primary-light: #1677ff;
$primary-dark: #003eb3;
$secondary: #4096ff;
$accent: #1677ff;
$success: #52c41a;
$warning: #faad14;
$error: #ff4d4f;

// 背景色 - 模型查看器保持深色以更好展示3D模型
$background: #f5f5f5;
$surface: #ffffff;
$surface-dark: #1a1a2e;
$text: rgba(0, 0, 0, 0.85);
$text-secondary: rgba(0, 0, 0, 0.65);
$text-muted: rgba(0, 0, 0, 0.45);
$text-light: #ffffff;

// 渐变
$gradient-primary: linear-gradient(135deg, $primary 0%, $primary-light 100%);

.model-viewer-page {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  background: $background;
  z-index: 1000;
  overflow: hidden;
}

// ==========================================
// Animated Background
// ==========================================
.bg-animation {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

.bg-gradient {
  position: absolute;
  border-radius: 50%;
  filter: blur(100px);
  opacity: 0.15;
  animation: float 30s ease-in-out infinite;

  &.bg-gradient-1 {
    width: 600px;
    height: 600px;
    background: $primary;
    top: -200px;
    left: -100px;
  }

  &.bg-gradient-2 {
    width: 500px;
    height: 500px;
    background: $primary-light;
    bottom: -150px;
    right: -100px;
    animation-delay: -10s;
  }

  &.bg-gradient-3 {
    width: 400px;
    height: 400px;
    background: $secondary;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    animation-delay: -20s;
    opacity: 0.1;
  }
}

@keyframes float {
  0%,
  100% {
    transform: translate(0, 0) scale(1);
  }
  33% {
    transform: translate(30px, -30px) scale(1.05);
  }
  66% {
    transform: translate(-20px, 20px) scale(0.95);
  }
}

// ==========================================
// Header
// ==========================================
.page-header {
  height: 64px;
  padding: 0 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
  position: relative;
  z-index: 10;
}

.header-left {
  display: flex;
  align-items: center;
  gap: 16px;

  .back-btn {
    color: $text !important;
    width: 40px;
    height: 40px;
    border-radius: 10px !important;
    border: 1px solid rgba($primary, 0.3) !important;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.3s !important;
    flex-shrink: 0;

    &:hover {
      background: rgba($primary, 0.2) !important;
      border-color: $primary !important;
      transform: translateY(-1px);
    }
  }

  .title {
    font-size: 20px;
    font-weight: 600;
    background: $gradient-primary;
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
  }

  .live-badge {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    background: rgba($success, 0.1);
    border: 1px solid rgba($success, 0.3);
    border-radius: 20px;
    color: $success;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.5px;

    .pulse-dot {
      width: 6px;
      height: 6px;
      background: $success;
      border-radius: 50%;
      animation: pulse 1.5s ease-in-out infinite;
    }
  }
}

.header-right {
  display: flex;
  gap: 8px;
}

@keyframes pulse {
  0%,
  100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.5;
    transform: scale(0.8);
  }
}

.action-btn {
  color: $text !important;
  border: 1px solid rgba($primary, 0.2) !important;
  border-radius: 8px !important;
  transition: all 0.3s ease !important;

  &:hover {
    color: $primary !important;
    background: rgba($primary, 0.1) !important;
    border-color: $primary !important;
    transform: translateY(-1px);
  }
}

// ==========================================
// Content Area
// ==========================================
.viewer-content {
  flex: 1;
  position: relative;
  overflow: hidden;
  z-index: 1;
  background: rgba($surface, 0.3);
  backdrop-filter: blur(10px);
}

.model-canvas {
  width: 100%;
  height: 100%;

  :deep(canvas) {
    display: block;
    width: 100% !important;
    height: 100% !important;
  }
}

// ==========================================
// Loading State
// ==========================================
.loading-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: $background;
  z-index: 10;
}

.loader {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
}

.loader-ring {
  position: absolute;
  width: 80px;
  height: 80px;
  border: 3px solid transparent;
  border-radius: 50%;
  animation: spin 1.5s linear infinite;

  &:nth-child(1) {
    border-top-color: $primary;
    animation-delay: 0s;
  }

  &:nth-child(2) {
    width: 60px;
    height: 60px;
    border-right-color: $primary-light;
    animation-delay: 0.2s;
    animation-direction: reverse;
  }

  &:nth-child(3) {
    width: 40px;
    height: 40px;
    border-bottom-color: $secondary;
    animation-delay: 0.4s;
  }
}

.loader-text {
  margin-top: 100px;
  color: $text-muted;
  font-size: 14px;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

// ==========================================
// Error State
// ==========================================
.error-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: $background;
  z-index: 10;
}

.error-card {
  text-align: center;
  padding: 40px;
  background: $surface;
  border: 1px solid #e8e8e8;
  border-radius: 16px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);

  .error-icon {
    width: 60px;
    height: 60px;
    margin: 0 auto 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 32px;
    font-weight: bold;
    color: #fff;
    background: $error;
    border-radius: 50%;
  }

  h3 {
    color: $text;
    font-size: 20px;
    margin-bottom: 8px;
  }

  p {
    color: $text-muted;
    font-size: 14px;
    margin-bottom: 24px;
  }
}

.error-actions {
  display: flex;
  gap: 12px;
  justify-content: center;
}

.gradient-btn {
  background: $gradient-primary !important;
  border: none !important;
  color: #fff !important;
  font-weight: 500 !important;

  &:hover {
    filter: brightness(1.1);
    transform: translateY(-1px);
  }
}

.outline-btn {
  background: transparent !important;
  border: 1px solid #e8e8e8 !important;
  color: $text !important;

  &:hover {
    border-color: $primary !important;
    color: $primary-light !important;
  }
}

// ==========================================
// Model Info (Left Side)
// ==========================================
.model-info {
  position: absolute;
  top: 20px;
  left: 20px;
  background: rgba($surface, 0.8);
  backdrop-filter: blur(20px);
  border: 1px solid rgba($primary, 0.3);
  border-radius: 12px;
  overflow: hidden;
  z-index: 5;
  min-width: 200px;

  .info-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 16px;
    background: rgba($primary, 0.1);
    border-bottom: 1px solid rgba($primary, 0.2);

    .info-icon {
      color: $primary-light;
      font-size: 16px;
    }

    .info-title {
      color: $text;
      font-size: 14px;
      font-weight: 600;
    }
  }

  .info-body {
    padding: 12px 16px;
  }

  .info-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 8px 0;

    &:not(:last-child) {
      border-bottom: 1px solid rgba($primary, 0.1);
    }

    .label {
      color: $text-muted;
      font-size: 13px;
    }

    .value {
      color: $text;
      font-size: 13px;
      font-weight: 500;

      &.highlight {
        color: $secondary;
      }
    }
  }
}

// ==========================================
// Scene Settings (Right Side)
// ==========================================
.scene-settings {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 280px;
  max-height: calc(100% - 40px);
  background: rgba($surface, 0.85);
  backdrop-filter: blur(20px);
  border: 1px solid rgba($primary, 0.3);
  border-radius: 12px;
  z-index: 10;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.settings-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 14px 16px;
  cursor: pointer;
  flex-shrink: 0;
  background: rgba($primary, 0.1);

  &:hover {
    background: rgba($primary, 0.15);
  }

  .settings-title {
    display: flex;
    align-items: center;
    gap: 10px;
    color: $text;
    font-size: 14px;
    font-weight: 600;
  }

  .collapse-icon {
    color: $text-muted;
    font-size: 12px;
    transition: transform 0.3s;

    &.is-open {
      transform: rotate(90deg);
    }
  }
}

.settings-body {
  flex: 1;
  overflow-y: auto;
  padding: 8px 0;

  &::-webkit-scrollbar {
    width: 4px;
  }

  &::-webkit-scrollbar-track {
    background: transparent;
  }

  &::-webkit-scrollbar-thumb {
    background: rgba($primary, 0.3);
    border-radius: 2px;

    &:hover {
      background: rgba($primary, 0.5);
    }
  }
}

.settings-group {
  padding: 12px 16px;
  border-bottom: 1px solid rgba($primary, 0.1);

  &:last-of-type {
    border-bottom: none;
  }

  .group-title {
    color: $primary-light;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1px;
    margin-bottom: 12px;
  }
}

.setting-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;

  &:last-child {
    margin-bottom: 0;
  }

  .setting-label {
    color: $text;
    font-size: 13px;
    flex-shrink: 0;
  }
}

.slider-wrapper {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  margin-left: 12px;

  :deep(.ant-slider) {
    flex: 1;
    margin: 0;

    .ant-slider-rail {
      background: rgba($primary, 0.2);
    }

    .ant-slider-track {
      background: $gradient-primary;
    }

    .ant-slider-handle {
      border-color: $primary;
      background: $primary;

      &:hover,
      &:focus {
        border-color: $primary-light;
        box-shadow: 0 0 0 4px rgba($primary, 0.2);
      }
    }
  }

  .slider-value {
    color: $text-muted;
    font-size: 12px;
    min-width: 36px;
    text-align: right;
  }
}

.color-picker {
  width: 36px;
  height: 28px;
  padding: 0;
  border: 2px solid rgba($primary, 0.3);
  border-radius: 6px;
  cursor: pointer;
  background: transparent;
  transition: border-color 0.3s;

  &:hover {
    border-color: $primary;
  }

  &::-webkit-color-swatch-wrapper {
    padding: 2px;
  }

  &::-webkit-color-swatch {
    border: none;
    border-radius: 3px;
  }
}

.settings-footer {
  padding: 12px 16px;
  border-top: 1px solid rgba($primary, 0.2);
  flex-shrink: 0;
  background: rgba($primary, 0.05);
}

.reset-btn {
  width: 100%;
  background: transparent !important;
  border: 1px solid rgba($primary, 0.5) !important;
  color: $primary-light !important;
  font-weight: 500 !important;
  transition: all 0.3s !important;

  &:hover {
    background: rgba($primary, 0.2) !important;
    border-color: $primary !important;
    color: #fff !important;
  }
}

// Select 样式
:deep(.ant-select) {
  .ant-select-selector {
    background: rgba($primary, 0.1) !important;
    border-color: rgba($primary, 0.3) !important;
    color: $text !important;
    border-radius: 6px !important;
  }

  .ant-select-arrow {
    color: $text-muted;
  }

  &:hover .ant-select-selector {
    border-color: $primary !important;
  }

  &.ant-select-focused .ant-select-selector {
    border-color: $primary !important;
    box-shadow: 0 0 0 2px rgba($primary, 0.2) !important;
  }
}

// Switch 样式
:deep(.ant-switch) {
  background: rgba($text-muted, 0.3);

  &.ant-switch-checked {
    background: $gradient-primary;
  }
}

// ==========================================
// Controls Info
// ==========================================
.controls-info {
  position: absolute;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 5;
}

.control-hint {
  display: flex;
  gap: 20px;
  padding: 12px 24px;
  background: rgba($surface, 0.8);
  backdrop-filter: blur(20px);
  border: 1px solid rgba($primary, 0.3);
  border-radius: 50px;

  .hint-item {
    display: flex;
    align-items: center;
    gap: 8px;
    color: $text-muted;
    font-size: 13px;

    .key {
      padding: 2px 8px;
      background: rgba($primary, 0.2);
      border: 1px solid rgba($primary, 0.3);
      border-radius: 4px;
      color: $primary-light;
      font-size: 11px;
      font-weight: 600;
    }
  }
}
</style>
