<template>
  <div class="model-viewer-page">
    <!-- Header -->
    <div class="viewer-header">
      <div class="header-left">
        <a-button type="text" @click="handleBack">
          <template #icon><ArrowLeftOutlined /></template>
          返回
        </a-button>
        <span class="title">3D 模型预览</span>
      </div>
      <div class="header-right">
        <a-button type="text" @click="resetCamera">
          <template #icon><AimOutlined /></template>
          重置视角
        </a-button>
        <a-button type="text" @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">
        <a-spin size="large" tip="正在加载模型..." />
      </div>

      <!-- Error -->
      <div v-if="error" class="error-overlay">
        <a-result status="error" title="模型加载失败" :sub-title="error">
          <template #extra>
            <a-button type="primary" @click="handleRetry">重试</a-button>
            <a-button @click="handleBack">返回</a-button>
          </template>
        </a-result>
      </div>

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

      <!-- Model Info -->
      <div v-if="!loading && !error && modelInfo" class="model-info">
        <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">{{ modelInfo.vertices }}</span>
        </div>
        <div class="info-item">
          <span class="label">面数：</span>
          <span class="value">{{ modelInfo.faces }}</span>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue"
import { useRouter, useRoute } from "vue-router"
import {
  ArrowLeftOutlined,
  AimOutlined,
  FullscreenOutlined,
  FullscreenExitOutlined,
  DragOutlined
} 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 { 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)

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

// 获取模型 URL
// Vue Router 会自动解码 query 参数，所以直接使用即可
const modelUrl = ref((route.query.url as string) || "")
console.log("模型查看器 - URL:", modelUrl.value)

// 返回上一页
const handleBack = () => {
  // 如果是新标签页打开（没有历史记录），则关闭窗口
  if (window.history.length <= 1) {
    window.close()
  } else {
    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 initScene = () => {
  if (!containerRef.value) return

  // 创建场景
  scene = new THREE.Scene()
  scene.background = new THREE.Color(0x1a1a2e)

  // 创建相机
  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 = 1.2
  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)

  // 添加环境光
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.4)
  scene.add(ambientLight)

  // 添加主方向光 - 增强亮度
  const mainLight = new THREE.DirectionalLight(0xffffff, 1.5)
  mainLight.position.set(10, 20, 10)
  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)

  // 添加补光 - 从侧面
  const fillLight = new THREE.DirectionalLight(0xffffff, 0.8)
  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)

  // 添加网格
  const gridHelper = new THREE.GridHelper(100, 100, 0x444444, 0x333333)
  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
  }

  // 检查文件扩展名
  const supportedExtensions = ['.glb', '.gltf']
  const urlLower = modelUrl.value.toLowerCase()
  const isSupported = supportedExtensions.some(ext => urlLower.includes(ext))
  if (!isSupported) {
    error.value = `不支持的文件格式，目前仅支持 GLB/GLTF 格式`
    loading.value = false
    console.error("不支持的文件格式:", modelUrl.value)
    return
  }

  loading.value = true
  error.value = null

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

    // 验证 URL 是否可访问
    try {
      const response = await fetch(modelUrl.value, { method: "HEAD" })
      if (!response.ok) {
        throw new Error(`文件不存在或无法访问 (HTTP ${response.status})`)
      }
      console.log("文件验证通过，开始加载...")
    } catch (fetchErr: any) {
      console.error("文件访问验证失败:", fetchErr)
      throw new Error(`无法访问文件: ${fetchErr.message}`)
    }

    const loader = new GLTFLoader()

    // 配置 DRACO 解码器
    if (!dracoLoader) {
      dracoLoader = new DRACOLoader()
      dracoLoader.setDecoderPath("https://www.gstatic.com/draco/versioned/decoders/1.5.6/")
    }
    loader.setDRACOLoader(dracoLoader)

    const gltf = await loader.loadAsync(modelUrl.value)

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

    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)

      // 取最大距离，并添加足够的边距（3倍）
      const baseDistance = Math.max(distanceForHeight, distanceForWidth, distanceForDepth)
      const cameraDistance = Math.max(baseDistance * 3, maxDim * 3, 10)

      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)
    error.value = err.message || "无法加载模型文件"
    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 (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
}

// 重试
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">
.model-viewer-page {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  background: #1a1a2e;
  z-index: 1000;
}

.viewer-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 20px;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);

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

    .title {
      color: #fff;
      font-size: 16px;
      font-weight: 500;
    }
  }

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

  :deep(.ant-btn-text) {
    color: rgba(255, 255, 255, 0.85);

    &:hover {
      color: #fff;
      background: rgba(255, 255, 255, 0.1);
    }
  }
}

.viewer-content {
  flex: 1;
  position: relative;
  overflow: hidden;
}

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

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

.loading-overlay,
.error-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(26, 26, 46, 0.95);
  z-index: 10;

  :deep(.ant-spin-text) {
    color: #fff;
  }

  :deep(.ant-result-title) {
    color: #fff;
  }

  :deep(.ant-result-subtitle) {
    color: rgba(255, 255, 255, 0.65);
  }
}

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

.control-hint {
  display: flex;
  gap: 24px;
  padding: 10px 20px;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(10px);
  border-radius: 8px;
  color: rgba(255, 255, 255, 0.85);
  font-size: 13px;

  span {
    display: flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
  }
}

.model-info {
  position: absolute;
  top: 20px;
  right: 20px;
  padding: 16px;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(10px);
  border-radius: 8px;
  z-index: 5;

  .info-item {
    display: flex;
    gap: 8px;
    margin-bottom: 8px;
    font-size: 13px;

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

    .label {
      color: rgba(255, 255, 255, 0.65);
    }

    .value {
      color: #fff;
      font-weight: 500;
    }
  }
}
</style>
