<template>
  <div class="qr-scanner-container">
    <div id="qr-reader" class="scanner-box"></div>

    <div v-if="errorMsg" class="error-msg">{{ errorMsg }}</div>

    <div class="focus-tip" v-if="!errorMsg && !isPaused">
      <div class="scan-line"></div>
      <div class="scan-text">将条码置于镜头范围内即可</div>
    </div>

    <div class="focus-tip success" v-if="isPaused">
      <div class="scan-text-success">
        <el-icon><CircleCheckFilled /></el-icon>
        扫描成功，2秒后继续...
      </div>
    </div>

    <div v-if="hasZoom" class="zoom-control">
      <span class="zoom-icon">-</span>
      <input
          type="range"
          :min="zoomMin"
          :max="zoomMax"
          step="0.1"
          v-model="currentZoom"
          @input="handleZoom"
      />
      <span class="zoom-icon">+</span>
      <div class="zoom-value">{{ currentZoom }}x</div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue'
import { Html5Qrcode, Html5QrcodeSupportedFormats } from 'html5-qrcode'
import { CircleCheckFilled } from '@element-plus/icons-vue'

const emit = defineEmits(['decode', 'error'])

const errorMsg = ref('')
const isPaused = ref(false)
let html5QrCode: Html5Qrcode | null = null
const scannerElementId = "qr-reader"

// 变焦控制状态
const hasZoom = ref(false)
const zoomMin = ref(1)
const zoomMax = ref(5)
const currentZoom = ref(1)

// 音频上下文
let audioCtx: AudioContext | null = null;

// 提示音播放函数
const playBeep = () => {
  try {
    const AudioContext = window.AudioContext || (window as any).webkitAudioContext;
    if (!AudioContext) return;

    if (!audioCtx) {
      audioCtx = new AudioContext();
    }

    if (audioCtx.state === 'suspended') {
      audioCtx.resume();
    }

    const oscillator = audioCtx.createOscillator();
    const gainNode = audioCtx.createGain();

    oscillator.connect(gainNode);
    gainNode.connect(audioCtx.destination);

    // 三角波，清脆响亮
    oscillator.type = 'triangle';
    oscillator.frequency.value = 1500;

    gainNode.gain.setValueAtTime(1.0, audioCtx.currentTime);

    oscillator.start();
    oscillator.stop(audioCtx.currentTime + 0.1);

  } catch (e) {
    console.error("播放提示音失败:", e);
  }
};

const startScanning = async () => {
  try {
    html5QrCode = new Html5Qrcode(scannerElementId, {
      useBarCodeDetectorIfSupported: true,
      formatsToSupport: [
        Html5QrcodeSupportedFormats.CODE_128,
        Html5QrcodeSupportedFormats.QR_CODE
      ],
      verbose: false
    })

    const config = {
      fps: 20,
      disableFlip: false,
      videoConstraints: {
        facingMode: "environment",
        // ★★★ 核心修改：设置为 2K (QHD) 分辨率 ★★★
        // min: 1280x720 (保证低端机能启动)
        // ideal: 2560x1440 (2K QHD，清晰度与性能的平衡点)
        width: { min: 1280, ideal: 2560, max: 3840 },
        height: { min: 720, ideal: 1440, max: 2160 },
        // 16:9 的比例
        aspectRatio: { ideal: 1.7777777778 },
        focusMode: "continuous",
        advanced: [{ focusMode: "macro" }, { zoom: 2.0 }]
      }
    }

    await html5QrCode.start(
        { facingMode: "environment" },
        config,
        (decodedText) => {
          if (isPaused.value) return
          console.log(`Scan: ${decodedText}`)

          isPaused.value = true

          playBeep();

          emit('decode', decodedText)

          if (navigator.vibrate) navigator.vibrate(200);

          setTimeout(() => {
            isPaused.value = false
          }, 2000)
        },
        (errorMessage) => {
          // ignore
        }
    )

    checkZoomCapability()

  } catch (err: any) {
    let msg = '无法启动摄像头'
    console.error("Scanner Error:", err)
    if (err.name === 'OverconstrainedError') {
      msg = '摄像头不支持 2K 分辨率，请尝试降低配置'
    }
    errorMsg.value = msg
    emit('error', msg)
  }
}

const checkZoomCapability = () => {
  if (!html5QrCode) return

  try {
    const videoTrack = html5QrCode.getRunningTrackCameraCapabilities() as MediaTrackCapabilities;

    // @ts-ignore
    if (videoTrack && 'zoom' in videoTrack) {
      hasZoom.value = true
      // @ts-ignore
      zoomMin.value = videoTrack.zoom.min || 1
      // @ts-ignore
      zoomMax.value = videoTrack.zoom.max || 5
      // @ts-ignore
      currentZoom.value = videoTrack.zoom.min || 1
    }
  } catch (e) {
    console.warn("无法获取变焦能力", e)
  }
}

const handleZoom = () => {
  if (!html5QrCode) return

  try {
    html5QrCode.applyVideoConstraints({
      advanced: [{ zoom: Number(currentZoom.value) }]
    })
  } catch (e) {
    console.error("变焦失败", e)
  }
}

const stopScanning = async () => {
  if (html5QrCode) {
    try {
      if (html5QrCode.isScanning) {
        await html5QrCode.stop()
      }
      html5QrCode.clear()
    } catch (e) {
      console.error("Stop failed", e)
    }
  }

  if (audioCtx) {
    audioCtx.close();
    audioCtx = null;
  }
}

onMounted(() => {
  try {
    const AudioContext = window.AudioContext || (window as any).webkitAudioContext;
    if (AudioContext) audioCtx = new AudioContext();
  } catch(e) {}

  setTimeout(() => {
    startScanning()
  }, 500)
})

onUnmounted(() => {
  stopScanning()
})
</script>

<style scoped>
.qr-scanner-container {
  width: 100%;
  height: 100%;
  position: relative;
  background-color: #000;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  overflow: hidden;
  border-radius: 0;
}

.scanner-box {
  width: 100%;
  height: 100%;
}

:deep(#qr-reader) {
  width: 100%;
  height: 100%;
  border: none !important;
  padding: 0 !important;
}

:deep(#qr-reader video) {
  width: 100% !important;
  height: 100% !important;
  object-fit: cover !important;
  display: block !important;
}

.error-msg {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  text-align: center;
  color: #fff;
  background: rgba(245, 108, 108, 0.85);
  padding: 15px;
  transform: translateY(-50%);
  z-index: 20;
}

.focus-tip {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 10;
  display: flex;
  justify-content: center;
  align-items: center;
}

.focus-tip.success {
  background: rgba(103, 194, 58, 0.2);
}

.scan-line {
  width: 100%;
  height: 2px;
  background: rgba(255, 0, 0, 0.5);
  box-shadow: 0 0 4px rgba(255, 0, 0, 0.8);
  position: absolute;
  animation: scan-move 2.5s infinite linear;
}

.scan-text {
  position: absolute;
  bottom: 150px;
  color: rgba(255, 255, 255, 0.8);
  font-size: 14px;
  text-shadow: 0 1px 3px rgba(0,0,0,0.8);
  background: rgba(0,0,0,0.3);
  padding: 4px 10px;
  border-radius: 4px;
}

.scan-text-success {
  color: #fff;
  font-size: 20px;
  font-weight: bold;
  display: flex;
  align-items: center;
  gap: 10px;
  text-shadow: 0 2px 4px rgba(0,0,0,0.8);
  background: rgba(103, 194, 58, 0.9);
  padding: 15px 30px;
  border-radius: 50px;
}

@keyframes scan-move {
  0% { top: 0%; opacity: 0; }
  10% { opacity: 1; }
  90% { opacity: 1; }
  100% { top: 100%; opacity: 0; }
}

.zoom-control {
  position: absolute;
  bottom: 80px;
  left: 50%;
  transform: translateX(-50%);
  width: 80%;
  max-width: 300px;
  display: flex;
  align-items: center;
  gap: 10px;
  background: rgba(0, 0, 0, 0.5);
  padding: 10px 20px;
  border-radius: 30px;
  z-index: 50;
  color: white;
}

.zoom-control input[type=range] {
  flex: 1;
  height: 4px;
}

.zoom-icon {
  font-size: 20px;
  font-weight: bold;
}

.zoom-value {
  font-size: 14px;
  width: 30px;
  text-align: right;
}
</style>