<template>
  <div class="light-page p-40">
    <button class="open-btn px-24 py-10 color-fff border-none round-8 fs-14 pointer" @click="openModal">打开打光效果</button>

    <div v-if="visible" class="modal-mask fixed flex-center" @click.self="closeModal">
      <div class="modal round-12 hidden">
        <header class="modal-header flex-between px-16 py-14">
          <span class="fs-15 color-fff">打光效果</span>
          <span class="close-x fs-28 color-888 pointer" @click="closeModal">×</span>
        </header>

        <div class="modal-body flex gap-20 p-24">
          <!-- 左：3D 可视化 -->
          <div class="visual-col flex-col gap-8">
            <div class="view-toggle flex gap-8">
              <button
                class="view-btn py-4 px-12 border-none round-6 fs-12 pointer"
                :class="{ active: viewMode === 'perspective' }"
                @click="viewMode = 'perspective'"
              >透视</button>
              <button
                class="view-btn py-4 px-12 border-none round-6 fs-12 pointer"
                :class="{ active: viewMode === 'front' }"
                @click="viewMode = 'front'"
              >正面</button>
            </div>
            <div ref="containerRef" class="visualizer relative wh-300"></div>
          </div>

          <!-- 右：控制面板 -->
          <div class="flex-1 flex-col gap-20 justify-center">
            <!-- 全局 -->
            <div class="control-section flex-col gap-10">
              <div class="section-title fs-13 color-aaa">全局</div>
              <div class="control-row flex-items-center gap-12">
                <span class="ctrl-label">智能模式</span>
                <button
                  class="toggle w-40 h-20 round-10 border-none relative pointer"
                  :class="{ on: smartMode }"
                  @click="smartMode = !smartMode"
                >
                  <span class="toggle-knob absolute top-2 left-2 wh-16 round bg-fff"></span>
                </button>
              </div>
              <div class="control-row flex-items-center gap-12">
                <span class="ctrl-label">亮度</span>
                <input type="range" v-model.number="brightness" min="0" max="100" class="slider flex-1 h-4 round-2 pointer" />
                <span class="minw-40 text-right fs-13 color-fff">{{ brightness }}%</span>
              </div>
              <div class="control-row flex-items-center gap-12">
                <span class="ctrl-label">颜色</span>
                <input type="color" v-model="lightColor" class="color-picker w-32 h-24 border-none round-4 pointer" />
              </div>
            </div>

            <!-- 主光源 -->
            <div class="control-section flex-col gap-10">
              <div class="section-title fs-13 color-aaa">主光源</div>
              <div class="preset-grid grid gap-8 flex">
                <button
                  v-for="p in lightPresets"
                  :key="p.label"
                  class="preset-btn py-8 border-none round-6 fs-12 color-ccc pointer color-fff px-10"
                  :class="{ active: activeLightPreset === p.label }"
                  @click="setLightPreset(p)"
                >{{ p.label }}</button>
              </div>
            </div>

            <!-- 轮廓光 -->
            <div class="control-section flex-col gap-10">
              <div class="section-title fs-13 color-aaa">轮廓光</div>
              <div class="control-row flex-items-center gap-12">
                <button
                  class="toggle w-40 h-20 round-10 border-none relative pointer"
                  :class="{ on: rimEnabled }"
                  @click="toggleRim"
                >
                  <span class="toggle-knob absolute top-2 left-2 wh-16 round bg-fff"></span>
                </button>
                <span class="fs-12 color-888">开启后置光源</span>
              </div>
            </div>
          </div>
        </div>

        <footer class="modal-footer flex-between px-16 py-14">
          <button class="reset-btn flex-items-center gap-6 py-6 px-14 border-none round-6 fs-13 pointer" @click="resetParams">
            <span class="fs-14">↺</span> 重置参数
          </button>
          <div class="flex-items-center gap-12">
            <span class="cost fs-13">⚡14</span>
            <button class="generate-btn py-8 px-20 color-fff border-none round-6 fs-13 pointer" @click="generate">⬆ 生成</button>
          </div>
        </footer>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, watch, onBeforeUnmount, nextTick } from 'vue';
import * as THREE from 'three';
import meinvUrl from '@/static/image/meinv.png';

const visible = ref(false);
const containerRef = ref<HTMLDivElement | null>(null);

// ============ 控制状态 ============
const viewMode = ref<'perspective' | 'front'>('perspective');
const smartMode = ref(false);
const brightness = ref(50);
const lightColor = ref('#ffffff');
const rimEnabled = ref(false);
const activeLightPreset = ref('前方');

const lightPresets = [
  { label: '左侧', theta: Math.PI / 2, phi: 0 },
  { label: '顶部', theta: 0, phi: Math.PI / 2 },
  { label: '右侧', theta: -Math.PI / 2, phi: 0 },
  { label: '前方', theta: 0, phi: 0 },
  { label: '底部', theta: 0, phi: -Math.PI / 2 },
  { label: '后方', theta: Math.PI, phi: 0 },
];

const R = 1.5;

// ============ Three.js 场景 ============
let renderer: THREE.WebGLRenderer | null = null;
let scene: THREE.Scene | null = null;
let perspCam: THREE.PerspectiveCamera | null = null;
let frontCam: THREE.OrthographicCamera | null = null;
let spotLight: THREE.SpotLight | null = null;
let spotHelper: THREE.SpotLightHelper | null = null;
let rimLight: THREE.SpotLight | null = null;
let ambient: THREE.AmbientLight | null = null;
let handle: THREE.Mesh | null = null;
let coneMesh: THREE.Mesh | null = null;
let rafId = 0;

// 光源球面坐标：theta 绕 Y，phi 绕 X（从赤道起）
let lightTheta = 0;
let lightPhi = 0;

const lightPos = () =>
  new THREE.Vector3(
    R * Math.cos(lightPhi) * Math.sin(lightTheta),
    R * Math.sin(lightPhi),
    R * Math.cos(lightPhi) * Math.cos(lightTheta),
  );

const buildWireframeSphere = (): THREE.LineSegments => {
  const geo = new THREE.SphereGeometry(R, 24, 16);
  const edges = new THREE.EdgesGeometry(geo);
  const mat = new THREE.LineBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0.25 });
  return new THREE.LineSegments(edges, mat);
};

const buildCone = (): THREE.Mesh => {
  const geo = new THREE.ConeGeometry(0.35, R, 32, 1, true);
  const mat = new THREE.MeshBasicMaterial({
    color: 0xffffff,
    transparent: true,
    opacity: 0.18,
    side: THREE.DoubleSide,
    depthWrite: false,
  });
  return new THREE.Mesh(geo, mat);
};

const updateCone = () => {
  if (!coneMesh || !handle) return;
  const pos = lightPos();
  handle.position.copy(pos);
  // 锥体从光源指向中心：默认锥尖朝 +Y，需旋转使锥尖朝向中心
  const dir = pos.clone().negate().normalize();
  const quat = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), dir);
  coneMesh.quaternion.copy(quat);
  // 锥体中心在光源与中心之间（锥高 R，中心偏移 R/2）
  coneMesh.position.copy(pos.clone().multiplyScalar(0.5));
};

const initScene = () => {
  const container = containerRef.value;
  if (!container) return;
  const w = container.clientWidth || 300;
  const h = container.clientHeight || 300;

  scene = new THREE.Scene();
  perspCam = new THREE.PerspectiveCamera(45, w / h, 0.1, 100);
  perspCam.position.set(3, 2, 4);
  perspCam.lookAt(0, 0, 0);

  const s = 1.8;
  frontCam = new THREE.OrthographicCamera(-s, s, s, -s, 0.1, 100);
  frontCam.position.set(0, 0, 6);
  frontCam.lookAt(0, 0, 0);

  renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
  renderer.setSize(w, h);
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setClearColor(0x000000, 0);
  container.appendChild(renderer.domElement);

  // 线框球
  scene.add(buildWireframeSphere());

  // 中心图片平面
  const tex = new THREE.TextureLoader().load(meinvUrl);
  tex.colorSpace = THREE.SRGBColorSpace;
  const planeGeo = new THREE.PlaneGeometry(1.4, 1.4);
  const planeMat = new THREE.MeshStandardMaterial({ map: tex, roughness: 0.85, metalness: 0.05 });
  const plane = new THREE.Mesh(planeGeo, planeMat);
  scene.add(plane);

  // 环境光
  ambient = new THREE.AmbientLight(0xffffff, 0.4);
  scene.add(ambient);

  // 主聚光灯
  spotLight = new THREE.SpotLight(0xffffff, 2.5, 0, Math.PI / 5, 0.4, 1);
  spotLight.position.copy(lightPos());
  scene.add(spotLight);
  scene.add(spotLight.target);

  // 光锥可视化
  coneMesh = buildCone();
  scene.add(coneMesh);

  // 光源把手
  const handleGeo = new THREE.SphereGeometry(0.08, 16, 16);
  const handleMat = new THREE.MeshBasicMaterial({ color: 0x111111 });
  handle = new THREE.Mesh(handleGeo, handleMat);
  scene.add(handle);

  // 轮廓光（默认关闭）
  rimLight = new THREE.SpotLight(0xffffff, 0, 0, Math.PI / 5, 0.4, 1);
  rimLight.position.set(-2, 1, -2);
  scene.add(rimLight);
  scene.add(rimLight.target);

  updateCone();
  applyBrightness();
  applyColor();
  animate();
};

const animate = () => {
  rafId = requestAnimationFrame(animate);
  if (renderer && scene) {
    const cam = viewMode.value === 'perspective' ? perspCam : frontCam;
    if (cam) renderer.render(scene, cam);
  }
};

const applyBrightness = () => {
  if (!spotLight || !ambient) return;
  const b = brightness.value / 100;
  spotLight.intensity = 1 + b * 4;
  ambient.intensity = 0.2 + b * 0.6;
};

const applyColor = () => {
  if (!spotLight) return;
  spotLight.color.set(lightColor.value);
  if (coneMesh) (coneMesh.material as THREE.MeshBasicMaterial).color.set(lightColor.value);
};

const setLightPreset = (p: { label: string; theta: number; phi: number }) => {
  activeLightPreset.value = p.label;
  lightTheta = p.theta;
  lightPhi = p.phi;
  if (spotLight) spotLight.position.copy(lightPos());
  updateCone();
};

const toggleRim = () => {
  rimEnabled.value = !rimEnabled.value;
  if (rimLight) rimLight.intensity = rimEnabled.value ? 3 : 0;
};

// ============ 拖拽光源把手 ============
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();
let dragging = false;

const onPointerDown = (e: PointerEvent) => {
  if (!renderer || !handle) return;
  const rect = renderer.domElement.getBoundingClientRect();
  pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
  pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
  raycaster.setFromCamera(pointer, getActiveCam());
  const hits = raycaster.intersectObject(handle);
  if (hits.length > 0) {
    dragging = true;
    activeLightPreset.value = '';
    e.stopPropagation();
    window.addEventListener('pointermove', onPointerMove);
    window.addEventListener('pointerup', onPointerUp);
  }
};

const onPointerMove = (e: PointerEvent) => {
  if (!dragging || !renderer) return;
  const rect = renderer.domElement.getBoundingClientRect();
  pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
  pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
  raycaster.setFromCamera(pointer, getActiveCam());

  // 射线与球面求交，取最近交点作为新光源位置
  const sphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0), R);
  const target = new THREE.Vector3();
  if (raycaster.ray.intersectSphere(sphere, target)) {
    lightTheta = Math.atan2(target.x, target.z);
    lightPhi = Math.asin(Math.max(-1, Math.min(1, target.y / R)));
    if (spotLight) spotLight.position.copy(target);
    updateCone();
  }
};

const onPointerUp = () => {
  dragging = false;
  window.removeEventListener('pointermove', onPointerMove);
  window.removeEventListener('pointerup', onPointerUp);
};

const getActiveCam = () => (viewMode.value === 'perspective' ? perspCam! : frontCam!);

const onResize = () => {
  const container = containerRef.value;
  if (!container || !renderer || !perspCam || !frontCam) return;
  const w = container.clientWidth || 300;
  const h = container.clientHeight || 300;
  renderer.setSize(w, h);
  perspCam.aspect = w / h;
  perspCam.updateProjectionMatrix();
  const s = 1.8;
  frontCam.left = -s;
  frontCam.right = s;
  frontCam.top = s * (h / w) || s;
  frontCam.bottom = -s * (h / w) || -s;
  frontCam.updateProjectionMatrix();
};

// ============ 生命周期 ============
const openModal = () => {
  visible.value = true;
  nextTick(() => {
    initScene();
    const c = containerRef.value;
    if (c) c.addEventListener('pointerdown', onPointerDown);
    window.addEventListener('resize', onResize);
  });
};

const closeModal = () => {
  visible.value = false;
  cleanup();
};

const cleanup = () => {
  cancelAnimationFrame(rafId);
  window.removeEventListener('resize', onResize);
  window.removeEventListener('pointermove', onPointerMove);
  window.removeEventListener('pointerup', onPointerUp);
  const c = containerRef.value;
  if (c) c.removeEventListener('pointerdown', onPointerDown);
  if (renderer) {
    renderer.dispose();
    renderer.forceContextLoss();
    if (renderer.domElement.parentNode) {
      renderer.domElement.parentNode.removeChild(renderer.domElement);
    }
    renderer = null;
  }
  scene = null;
  perspCam = null;
  frontCam = null;
  spotLight = null;
  spotHelper = null;
  rimLight = null;
  ambient = null;
  handle = null;
  coneMesh = null;
};

watch(brightness, applyBrightness);
watch(lightColor, applyColor);

onBeforeUnmount(() => cleanup());

// ============ 操作 ============
const resetParams = () => {
  smartMode.value = false;
  brightness.value = 50;
  lightColor.value = '#ffffff';
  rimEnabled.value = false;
  setLightPreset(lightPresets[3]);
  if (rimLight) rimLight.intensity = 0;
};

const generate = () => {
  console.log('打光参数', {
    viewMode: viewMode.value,
    smartMode: smartMode.value,
    brightness: brightness.value,
    lightColor: lightColor.value,
    lightTheta,
    lightPhi,
    rimEnabled: rimEnabled.value,
  });
};
</script>

<style scoped lang="scss">
.open-btn {
  background: #409eff;
  &:hover {
    background: #5599ff;
  }
}

.modal-mask {
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  z-index: 9999;
}

.modal {
  width: 720px;
  background: #1a1a2e;
  border: 1px solid #333;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
}

.modal-header {
  border-bottom: 1px solid #2a2a3e;
}

.close-x {
  line-height: 1;
  &:hover {
    color: #fff;
  }
}

.modal-body {
  border-bottom: 1px solid #2a2a3e;
}

.visual-col {
  flex-shrink: 0;
}

.view-btn {
  background: #2a2a3e;
  color: #aaa;
  &.active {
    background: #409eff;
    color: #fff;
  }
}

.visualizer {
  cursor: default;
}

.section-title {
  border-left: 2px solid #409eff;
  padding-left: 8px;
}

.ctrl-label {
  font-size: 13px;
  color: #ccc;
  min-width: 56px;
}

.control-section {
  padding: 10px 12px;
  background: #20203a;
  border-radius: 8px;
}

.preset-grid {
  grid-template-columns: repeat(3, 1fr);
}

.preset-btn {
  background: #2a2a3e;
  &.active {
    background: #409eff;
    color: #fff;
  }
}

.slider {
  -webkit-appearance: none;
  appearance: none;
  background: #2a2a3e;
  outline: none;

  &::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #888;
    cursor: pointer;
  }
}

.color-picker {
  background: transparent;
  cursor: pointer;
}

.toggle {
  background: #3a3a4e;
  transition: background 0.2s;
  &.on {
    background: #409eff;
  }
}

.toggle-knob {
  transition: transform 0.2s;
}

.toggle.on .toggle-knob {
  transform: translateX(20px);
}

.modal-footer {
  border-top: 1px solid #2a2a3e;
}

.reset-btn {
  background: #2a2a3e;
  color: #ccc;
  &:hover {
    background: #3a3a4e;
    color: #fff;
  }
}

.cost {
  color: #ffa500;
}

.generate-btn {
  background: #409eff;
  &:hover {
    background: #5599ff;
  }
}
</style>
