<template>
  <div class="cameraGuide" v-if="visible">
    <!-- ===== 拍摄引导页 ===== -->
    <div class="cameraPage" v-show="step==='guide'">
      <div class="cameraNav">
        <van-icon name="arrow-left" class="backBtn" @click="handleClose" />
        <span class="navTitle">{{ navTitle }}</span>
      </div>

      <div class="guideLayer">
        <!-- 顶部主指令卡 -->
        <div class="guideHero">
          <div class="guideHeroIcon">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <rect x="3" y="5" width="18" height="14" rx="2"></rect>
              <path d="M3 9h18"></path>
              <circle cx="8" cy="13.5" r="1.6"></circle>
              <path d="M11 12.5h7M11 15h5"></path>
            </svg>
          </div>
          <div class="guideHeroText">
            <div class="guideHeroTitle">{{ heroTitle }}</div>
            <div class="guideHeroSub">从卡套内拿出，避免反光</div>
          </div>
        </div>

        <!-- 取景框 -->
        <div class="guideFrameOuter">
          <span class="corner tl"></span>
          <span class="corner tr"></span>
          <span class="corner bl"></span>
          <span class="corner br"></span>
          <div class="guideFrameBorder"></div>
          <div class="guideHole">
            <!-- 相机画面仅在取景框内显示 -->
            <video ref="video" class="cameraVideo" autoplay playsinline webkit-playsinline muted></video>
            <!-- 主页模板：标题 + 印章 -->
            <div v-if="type==='front'" class="licenseTemplate ltFrontPage">
              <div class="ltInner">
                <div class="ltTitle">
                  <div class="ltTitleLine">中华人民共和国机动车行驶证</div>
                </div>
                <div class="ltSeal">印章</div>
              </div>
            </div>
            <!-- 副页模板：条形码 + 编号 -->
            <div v-else-if="type==='back'" class="licenseTemplate ltBackPage">
              <div class="ltInner">
                <div class="ltBarcodeArea">
                  <div class="ltBarcodeStrip">
                    <div v-for="(bar, i) in barcodeBars" :key="i" class="ltBar" :class="bar"></div>
                  </div>
                  <div class="ltBarcodeNum">3701001234567</div>
                </div>
              </div>
            </div>
            <!-- 车辆照片模板：标题 + 车辆轮廓 -->
            <div v-else class="licenseTemplate ltCarPage">
              <div class="ltInner">
                <div class="ltTitle">
                  <div class="ltTitleLine">车辆照片</div>
                </div>
                <div class="ltCarOutline">
                </div>
              </div>
            </div>
          </div>
        </div>

        <!-- 底部取景要点 -->
        <div class="guideTips">
          <div class="tipItem ok">
            <span class="tipIco">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
                <path d="M4 8V4h4M16 4h4v4M20 16v4h-4M8 20H4v-4"></path>
              </svg>
            </span>
            <span class="tipLabel">四角入框</span>
          </div>
          <div class="tipItem ok">
            <span class="tipIco">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
                <path d="M4 12h16M7 7h10M7 17h10"></path>
              </svg>
            </span>
            <span class="tipLabel">平放正向</span>
          </div>
          <div class="tipItem warn">
            <span class="tipIco">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
                <circle cx="12" cy="12" r="4"></circle>
                <path d="M12 2v3M12 19v3M2 12h3M19 12h3M5 5l2 2M17 17l2 2M19 5l-2 2M5 19l2-2"></path>
              </svg>
            </span>
            <span class="tipLabel">避免反光</span>
          </div>
        </div>
      </div>

      <div class="shootBar">
        <div class="shootBtn" @click="takePhoto"></div>
        <div class="shootLabel">点击拍照</div>
      </div>
    </div>

    <!-- ===== 预览确认页 ===== -->
    <div class="previewPage" v-show="step==='preview'">
      <div class="cameraNav">
        <van-icon name="arrow-left" class="backBtn" @click="handlePreviewBack" />
        <span class="navTitle">照片预览</span>
      </div>
      <div class="previewImgWrap">
        <img class="previewImg" :src="previewDataURL" alt="预览" />
      </div>
      <div class="previewBtns">
        <button class="previewBtn btnRetake" @click="retake">重拍</button>
        <button class="previewBtn btnConfirm" @click="confirmPhoto">确认使用</button>
      </div>
    </div>
  </div>
</template>

<script>
// 拍摄引导组件：WebRTC 取景 + 引导框 + 分页面证件模板 + 预览确认
// props: type(front/back/car), visible
// emit: confirm({blob, dataURL})、cancel、fallback(权限拒绝/相机不可用，父组件降级系统选择器)
export default {
  name: 'CameraGuide',
  props: {
    type: {
      type: String,
      default: 'front'
    },
    visible: {
      type: Boolean,
      default: false
    },
    // 父组件前置探测时已拿到的相机流，传入则直接复用（iOS 上避免二次 getUserMedia 黑屏）
    stream: {
      type: Object,
      default: null
    }
  },
  data() {
    const heights = ['tall', 'short', 'medium wide', 'tall', 'short', 'medium', 'tall wide', 'short',
      'medium', 'tall', 'short wide', 'medium', 'tall', 'short', 'medium wide', 'tall',
      'short', 'medium', 'tall wide', 'short', 'medium', 'tall', 'short wide', 'medium',
      'tall', 'short', 'medium wide', 'tall', 'short', 'medium', 'tall wide', 'short',
      'medium', 'tall', 'short wide', 'medium', 'tall', 'short', 'medium wide', 'tall']
    return {
      step: 'guide', // guide 拍摄引导 | preview 预览确认
      activeStream: null,
      previewBlob: null,
      previewDataURL: '',
      barcodeBars: heights
    }
  },
  computed: {
    navTitle() {
      return { front: '拍摄行驶证主页', back: '拍摄行驶证副页', car: '拍摄行驶证车辆照片' }[this.type] || '拍摄行驶证'
    },
    heroTitle() {
      return { front: '请将主页对准取景框', back: '请将副页对准取景框', car: '请将车辆对准取景框' }[this.type] || '请将证件对准取景框'
    }
  },
  watch: {
    visible(val) {
      if (val) {
        this.step = 'guide'
        this.previewBlob = null
        this.previewDataURL = ''
        this.$nextTick(() => this.startCamera())
      } else {
        this.stopCamera()
      }
    }
  },
  beforeDestroy() {
    this.stopCamera()
  },
  methods: {
    async startCamera() {
      try {
        // 优先复用父组件探测时拿到的流，未传入才自行申请
        const stream = this.stream || await navigator.mediaDevices.getUserMedia({
          video: { facingMode: 'environment' },
          audio: false
        })
        // 等待期间组件已被关闭或销毁，立即释放
        if (!this.visible || this._isDestroyed || this._isBeingDestroyed) {
          stream.getTracks().forEach(t => t.stop())
          return
        }
        this.activeStream = stream
        console.log('[CameraGuide] stream acquired, 来源:', this.stream ? '父组件复用' : '组件自申请', 'active:', stream.active,
          'tracks:', stream.getTracks().map(t => `${t.kind}/${t.readyState}/enabled:${t.enabled}`))
        await this.$nextTick()
        const video = this.$refs.video
        if (video) {
          // iOS 自动播放加固：muted/playsinline 属性与特性同时设置，元数据就绪后再 play
          video.muted = true
          video.setAttribute('playsinline', 'true')
          video.setAttribute('webkit-playsinline', 'true')
          video.srcObject = stream
          const logVideoState = (tag) => {
            console.log(`[CameraGuide] ${tag}:`,
              'paused:', video.paused,
              'readyState:', video.readyState,
              'currentTime:', video.currentTime,
              'videoWidth:', video.videoWidth,
              'videoHeight:', video.videoHeight,
              'streamActive:', stream.active,
              'muted:', video.muted)
          }
          const playVideo = () => {
            video.play()
              .then(() => logVideoState('play resolved'))
              .catch(e => {
                console.warn('[CameraGuide] video play error:', e && (e.name || e.message || e))
                logVideoState('play rejected')
              })
          }
          video.onloadedmetadata = () => {
            logVideoState('loadedmetadata')
            playVideo()
          }
          video.onplaying = () => logVideoState('playing')
          video.onerror = () => {
            console.warn('[CameraGuide] video element error:', video.error && `${video.error.code}/${video.error.message}`)
          }
          if (video.readyState >= 1) {
            playVideo()
          }
          // 2 秒后再看一次：currentTime 是否增长（判断是真在播还是黑屏死流）
          setTimeout(() => logVideoState('after 2s'), 2000)
        }
      } catch (e) {
        // 权限拒绝或相机不可用 → 通知父组件降级为系统选择器
        console.warn('getUserMedia error:', e && (e.name || e.message || e))
        this.$emit('fallback', e)
      }
    },
    stopCamera() {
      if (this.activeStream) {
        this.activeStream.getTracks().forEach(t => t.stop())
        this.activeStream = null
      }
    },
    takePhoto() {
      const video = this.$refs.video
      if (!video || !video.videoWidth) {
        this.$toast('相机尚未就绪，请稍候')
        return
      }
      // 按取景框比例（约 1.43:1）从视频中心裁剪，与框内可视区域（object-fit: cover）保持一致
      const frameAspect = 1.43
      const vw = video.videoWidth
      const vh = video.videoHeight
      let sw, sh
      if (vw / vh > frameAspect) {
        sh = vh
        sw = Math.round(vh * frameAspect)
      } else {
        sw = vw
        sh = Math.round(vw / frameAspect)
      }
      const sx = Math.round((vw - sw) / 2)
      const sy = Math.round((vh - sh) / 2)
      const canvas = document.createElement('canvas')
      canvas.width = sw
      canvas.height = sh
      canvas.getContext('2d').drawImage(video, sx, sy, sw, sh, 0, 0, sw, sh)
      const dataURL = canvas.toDataURL('image/jpeg', 0.92)
      canvas.toBlob(blob => {
        if (!blob) {
          this.$toast('拍摄失败，请重试')
          return
        }
        this.previewBlob = blob
        this.previewDataURL = dataURL
        this.step = 'preview'
      }, 'image/jpeg', 0.92)
    },
    retake() {
      // 重拍回到引导页，相机流保持
      this.step = 'guide'
    },
    confirmPhoto() {
      this.$emit('confirm', { blob: this.previewBlob, dataURL: this.previewDataURL })
    },
    handleClose() {
      this.$emit('cancel')
    },
    handlePreviewBack() {
      // 预览页返回 = 放弃拍摄，关闭并释放相机流
      this.$emit('cancel')
    }
  }
}
</script>

<style scoped lang="scss">
.cameraGuide {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 3000;
  background: #F5F5F5;
}
.cameraPage {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(160deg, #F7F9FC 0%, #EEF1F6 50%, #E6EAF1 100%);
  overflow: hidden;
}
.cameraVideo {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 1;
}
.cameraNav {
  position: relative;
  z-index: 10;
  display: flex;
  align-items: center;
  height: 52px;
  padding-left: 14px;
  padding-top: env(safe-area-inset-top);
  background: #2C395F;
  color: #fff;
  .backBtn {
    font-size: 22px;
    width: 36px;
    flex-shrink: 0;
  }
  .navTitle {
    flex: 1;
    text-align: left;
    font-size: 18px;
    font-weight: bolder;
    letter-spacing: .5px;
    padding-left: 4px;
  }
}
.guideLayer {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding-top: 80px;
  /* 避让底部拍照栏（约 142px 高），防止小屏下提示被遮挡 */
  padding-bottom: 150px;
  padding-bottom: calc(150px + env(safe-area-inset-bottom));
  pointer-events: none;
}
.guideHero {
  display: flex;
  align-items: center;
  gap: 12px;
  background: #FFFFFF;
  border: 1px solid #E9E9EA;
  border-radius: 6px;
  padding: 12px 15px;
  margin-bottom: 24px;
  max-width: 322px;
  width: calc(100% - 48px);
  box-sizing: border-box;
  box-shadow: 0 2px 10px rgba(20, 30, 50, .08);
  .guideHeroIcon {
    width: 42px;
    height: 42px;
    flex-shrink: 0;
    border-radius: 6px;
    background: rgba(53, 77, 147, .10);
    display: flex;
    align-items: center;
    justify-content: center;
    color: #354D93;
    svg {
      width: 23px;
      height: 23px;
    }
  }
  .guideHeroTitle {
    color: #222;
    font-size: 16px;
    font-weight: 700;
    line-height: 1.3;
    letter-spacing: .3px;
  }
  .guideHeroSub {
    color: #5B606E;
    font-size: 12.5px;
    margin-top: 4px;
    line-height: 1.4;
  }
}
/* 取景框：宽约为屏宽 80%，宽高比约 1.43:1；小屏按可视高度自适应缩小，避免遮挡上下元素 */
.guideFrameOuter {
  position: relative;
  width: 80%;
  /* 纵向可用高度 ≈ 100vh - (导航80 + 拍照栏150 + 提示卡92 + 底部提示92) ≈ 100vh - 420px，
     换算成宽度（×1.43）并限制在 170~340px 之间，大屏表现与原 80%/340px 一致 */
  width: clamp(170px, min(80%, calc((100vh - 420px) * 1.43)), 340px);
  width: clamp(170px, min(80%, calc((100dvh - 420px) * 1.43)), 340px);
  /* 宽高比约 1.43:1（与拍照裁剪比例一致）；不用 padding-top 百分比——它相对父级宽度，无法跟随自适应宽度 */
  aspect-ratio: 1.43 / 1;
  flex-shrink: 0;
  /* 不用 filter: drop-shadow，iOS 上父级 filter 会导致 video 合成层不绘制（黑屏） */
  box-shadow: 0 4px 16px rgba(20, 30, 50, .12);
}
/* 兼容不支持 aspect-ratio 的旧浏览器：padding-top 相对父级宽度，80% / 1.43 ≈ 55.94% */
@supports not (aspect-ratio: 1 / 1) {
  .guideFrameOuter {
    padding-top: 55.94%;
  }
}
.corner {
  position: absolute;
  width: 30px;
  height: 30px;
  border-color: #354D93;
  border-style: solid;
  z-index: 10;
  animation: cornerFadeIn .45s ease both;
  &.tl { top: -4px; left: -4px; border-width: 4px 0 0 4px; border-radius: 4px 0 0 0; }
  &.tr { top: -4px; right: -4px; border-width: 4px 4px 0 0; border-radius: 0 4px 0 0; }
  &.bl { bottom: -4px; left: -4px; border-width: 0 0 4px 4px; border-radius: 0 0 0 4px; }
  &.br { bottom: -4px; right: -4px; border-width: 0 4px 4px 0; border-radius: 0 0 4px 0; }
}
@keyframes cornerFadeIn {
  from { opacity: 0; transform: scale(1.15); }
  to { opacity: 1; transform: scale(1); }
}
.guideFrameBorder {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  border: 3px solid #C2C8D2;
  border-radius: 8px;
  pointer-events: none;
  z-index: 9;
}
.guideHole {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  border-radius: 8px;
  overflow: hidden;
}
/* 证件模板叠层（仅视觉辅助，不拦截事件） */
.licenseTemplate {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 6;
  border: 1.5px solid rgba(53, 77, 147, .55);
  border-radius: 4px;
  overflow: hidden;
  pointer-events: none;
  background: rgba(53, 77, 147, .06);
}
.ltInner {
  position: absolute;
  top: 5px;
  left: 5px;
  right: 5px;
  bottom: 5px;
  display: flex;
  flex-direction: column;
}
.ltTitle {
  text-align: center;
  margin-bottom: 4px;
}
.ltTitleLine {
  font-size: 10px;
  color: rgba(53, 77, 147, .9);
  font-weight: bold;
  letter-spacing: 1px;
  white-space: nowrap;
}
.ltSeal {
  position: absolute;
  left: 10px;
  bottom: 10px;
  width: 34px;
  height: 34px;
  border: 1.5px dashed rgba(255, 85, 59, .85);
  border-radius: 2px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 5.5px;
  color: rgba(255, 85, 59, .85);
}
.ltFrontPage .ltInner {
  justify-content: flex-start;
}
.ltBackPage .ltInner {
  justify-content: flex-end;
}
.ltBarcodeArea {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  padding-bottom: 6px;
  padding-right: 8px;
}
.ltBarcodeStrip {
  display: flex;
  align-items: flex-end;
  height: 28px;
  gap: 1px;
  .ltBar {
    background: rgba(53, 77, 147, .6);
    width: 1.5px;
    border-radius: 0.5px;
    &.wide { width: 2.8px; }
    &.short { height: 50%; }
    &.medium { height: 70%; }
    &.tall { height: 100%; }
  }
}
.ltBarcodeNum {
  font-size: 8px;
  color: rgba(53, 77, 147, .9);
  letter-spacing: 2px;
  font-family: 'Courier New', monospace;
}
.ltCarPage .ltCarOutline {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  svg {
    width: 60%;
    opacity: .6;
    color: #354D93;
  }
}
.guideTips {
  display: flex;
  gap: 10px;
  margin-top: 22px;
  padding: 0 14px;
  max-width: 340px;
  width: 100%;
  box-sizing: border-box;
}
.tipItem {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  background: #FFFFFF;
  border-radius: 6px;
  padding: 11px 4px 9px;
  border: 1px solid #E9E9EA;
  box-shadow: 0 2px 8px rgba(20, 30, 50, .06);
  .tipIco {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    svg {
      width: 17px;
      height: 17px;
    }
  }
  &.ok .tipIco {
    background: rgba(53, 77, 147, .10);
    color: #354D93;
  }
  &.warn .tipIco {
    background: rgba(255, 85, 59, .12);
    color: #FF553B;
  }
  .tipLabel {
    color: #5B606E;
    font-size: 11.5px;
    line-height: 1.3;
    text-align: center;
  }
}
.shootBar {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 12px;
  padding-bottom: calc(32px + env(safe-area-inset-bottom));
  background: #FFFFFF;
  border-top: 1px solid #E9E9EA;
}
.shootBtn {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  border: 4px solid #354D93;
  background: #FFFFFF;
  position: relative;
  box-shadow: 0 0 0 2px rgba(53, 77, 147, .22), 0 6px 16px rgba(20, 30, 50, .18);
  &:active {
    transform: scale(.92);
  }
  &::after {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 58px;
    height: 58px;
    border-radius: 50%;
    background: #354D93;
  }
}
.shootLabel {
  color: #5B606E;
  font-size: 12px;
  margin-top: 10px;
  letter-spacing: 1px;
}
/* ===== 预览确认页 ===== */
.previewPage {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: #F5F5F5;
  display: flex;
  flex-direction: column;
  z-index: 20;
}
.previewImgWrap {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  overflow: hidden;
}
.previewImg {
  max-width: 100%;
  max-height: 100%;
  border-radius: 8px;
  object-fit: contain;
}
.previewBtns {
  display: flex;
  gap: 16px;
  padding: 0 20px 32px;
  padding-bottom: calc(32px + env(safe-area-inset-bottom));
}
.previewBtn {
  flex: 1;
  height: 48px;
  border-radius: 24px;
  border: none;
  font-size: 16px;
  font-weight: 600;
  &:active {
    opacity: .8;
  }
}
.btnRetake {
  background: #FFFFFF;
  color: #354D93;
  border: 1px solid #354D93;
}
.btnConfirm {
  background: #354D93;
  color: #fff;
}
</style>
