<template>
  <view class="highlight-card">
    <view class="highlight-head">
      <text class="highlight-title">服务回顾短片</text>
      <text class="highlight-badge">智能剪辑</text>
    </view>
    <text class="highlight-desc">
      将服务前、过程、服务后素材自动剪成竖屏 9:16 短片。<text class="highlight-em">成片时长随素材量变化</text>，并受门店配置的总时长上限约束，<text class="highlight-em">不提供「固定 15 秒 / 30 秒」选项</text>。请先在下方选择编排方式，再点「生成短片」。
    </text>
    <text v-if="canTriggerHighlight" class="highlight-desc-ops">
      「编排」指素材拼接顺序：预设顺序含对比封面与收尾；穿插成片在阶段间均匀穿插照片与小视频。可选配乐由门店在服务端配置。
    </text>
    <view v-if="highlightStatus === 'processing'" class="highlight-status highlight-status--busy">
      <text>短片生成中，请稍候…</text>
      <text class="highlight-processing-hint">可离开本页，稍后重新进入即可查看结果。</text>
    </view>
    <view v-else-if="highlightStatus === 'failed'" class="highlight-status highlight-status--err">
      <text v-if="modelValue.highlightFailReasonLabel" class="highlight-err-type">{{
        modelValue.highlightFailReasonLabel
      }}</text>
      <text class="highlight-err-msg">{{ highlightErrorUserText }}</text>
      <text v-if="canTriggerHighlight" class="highlight-fail-hint">可调整编排方式后重试；若仍失败请联系门店或管理员。</text>
    </view>
    <video
      v-if="highlightReady"
      class="highlight-player"
      :src="imgUrl(modelValue.highlightVideoUrl)"
      controls
      :show-center-play-btn="true"
      objectFit="contain"
    />
    <text v-if="highlightReady && modelValue.highlightDurationSec" class="highlight-meta">
      约 {{ modelValue.highlightDurationSec }} 秒（实际时长随素材变化）
      <text v-if="highlightComposeLabel"> · {{ highlightComposeLabel }}</text>
    </text>
    <!-- #ifdef MP-WEIXIN -->
    <view v-if="highlightReady" class="highlight-mp-bar">
      <button
        class="highlight-btn highlight-btn--album highlight-btn--block"
        :disabled="savingHighlightVideo"
        @click="saveHighlightVideoToAlbum"
      >
        {{ savingHighlightVideo ? '保存中…' : '保存视频' }}
      </button>
      <text class="highlight-mp-hint">
        保存后可在系统相册中转发到微信好友或其它应用；分享本报告小程序卡片请用页面底部「转发给宠主」。
      </text>
      <text class="highlight-mp-tip">也可点右上角「···」将本页转发给好友。</text>
    </view>
    <!-- #endif -->
    <!-- #ifdef H5 -->
    <view v-if="highlightReady" class="highlight-h5-bar">
      <button
        class="highlight-btn highlight-btn--album highlight-btn--block"
        :disabled="savingHighlightVideo"
        @click="downloadHighlightVideoH5"
      >
        {{ savingHighlightVideo ? '处理中…' : '下载短片' }}
      </button>
      <text class="highlight-mp-hint">
        将回顾短片保存到本机下载目录；若浏览器拦截，将尝试复制链接。也可使用浏览器菜单分享本页。
      </text>
    </view>
    <!-- #endif -->
    <view v-if="canTriggerHighlight" class="highlight-actions">
      <view class="highlight-mode-head">
        <text class="highlight-mode-title">编排方式</text>
        <text class="highlight-mode-sub">（剪辑顺序，不是选时长）</text>
      </view>
      <view class="highlight-mode">
        <view
          :class="['highlight-mode-item', highlightComposeMode === 'preset' && 'is-on']"
          @click="highlightComposeMode = 'preset'"
        >
          预设顺序
        </view>
        <view
          :class="['highlight-mode-item', highlightComposeMode === 'interleave' && 'is-on']"
          @click="highlightComposeMode = 'interleave'"
        >
          穿插成片
        </view>
      </view>
      <button
        class="highlight-btn highlight-btn--block"
        :disabled="highlightStatus === 'processing'"
        @click="startHighlight"
      >
        生成短片
      </button>
    </view>
  </view>
</template>

<script setup>
import { ref, computed, watch, onUnmounted } from 'vue'
import { getReportByToken, imgUrl, postReportHighlightStart } from '../../api/index.js'
import { getUserSession } from '../../utils/session.js'

const props = defineProps({
  modelValue: { type: Object, required: true }
})
const emit = defineEmits(['update:modelValue'])

const savingHighlightVideo = ref(false)
const highlightComposeMode = ref('preset')

const highlightStatus = computed(() => props.modelValue?.highlightVideoStatus || '')

const highlightReady = computed(
  () => highlightStatus.value === 'done' && !!props.modelValue?.highlightVideoUrl
)

const highlightComposeLabel = computed(() => {
  const m = props.modelValue?.highlightComposeMode
  if (m === 'interleave') return '穿插成片'
  if (m === 'preset') return '预设顺序'
  return ''
})

const highlightErrorUserText = computed(() => {
  const raw = props.modelValue?.highlightVideoError
  const hasCode = !!props.modelValue?.highlightFailReason
  if (!raw || !String(raw).trim()) {
    return '生成失败，请稍后重试'
  }
  const s = String(raw).trim()
  if (hasCode) return s
  if (/ffmpeg|exit\s*code|IllegalState|Exception|stack|0x|at\s+com\./i.test(s)) {
    return '短片生成遇到问题，请稍后重试或联系门店。'
  }
  return s
})

const canTriggerHighlight = computed(() => {
  const u = getUserSession()
  const role = u?.role
  return !!(u?.id && (role === 'boss' || role === 'staff'))
})

let highlightPollTimer = null

const stopHighlightPoll = () => {
  if (highlightPollTimer != null) {
    clearInterval(highlightPollTimer)
    highlightPollTimer = null
  }
}

const resolveToken = () => {
  let t = props.modelValue?.reportToken || ''
  // #ifdef H5
  if (!t && typeof window !== 'undefined') {
    t = new URLSearchParams(window.location.search).get('token') || ''
  }
  // #endif
  return t || ''
}

const maybeStartHighlightPoll = () => {
  stopHighlightPoll()
  if (props.modelValue?.highlightVideoStatus !== 'processing') return
  highlightPollTimer = setInterval(async () => {
    const t = resolveToken()
    if (!t) {
      stopHighlightPoll()
      return
    }
    const res = await getReportByToken(t)
    if (res.code === 200 && res.data) {
      emit('update:modelValue', res.data)
      const m = res.data.highlightComposeMode
      if (m === 'preset' || m === 'interleave') {
        highlightComposeMode.value = m
      }
      if (res.data.highlightVideoStatus !== 'processing') {
        stopHighlightPoll()
        if (res.data.highlightVideoStatus === 'done') {
          uni.showToast({ title: '短片已生成', icon: 'success' })
        }
      }
    }
  }, 2500)
}

watch(
  () => props.modelValue?.highlightVideoStatus,
  (s) => {
    if (s === 'processing') maybeStartHighlightPoll()
    else stopHighlightPoll()
  },
  { immediate: true }
)

watch(
  () => props.modelValue?.highlightComposeMode,
  (m) => {
    if (m === 'preset' || m === 'interleave') highlightComposeMode.value = m
  },
  { immediate: true }
)

const startHighlight = async () => {
  const u = getUserSession()
  if (!props.modelValue?.id || !u?.id) {
    uni.showToast({ title: '请先登录门店账号', icon: 'none' })
    return
  }
  uni.showLoading({ title: '提交中' })
  const res = await postReportHighlightStart({
    reportId: props.modelValue.id,
    composeMode: highlightComposeMode.value,
    operatorUserId: u.id,
    role: u.role || 'staff'
  })
  uni.hideLoading()
  if (res.code === 200) {
    uni.showToast({ title: res.message || '已开始生成', icon: 'none' })
    emit('update:modelValue', { ...props.modelValue, highlightVideoStatus: 'processing' })
    maybeStartHighlightPoll()
  } else if (res.code === 503) {
    uni.showModal({
      title: '暂无法生成',
      content:
        (res.message || '服务器未安装或未配置 ffmpeg') +
        '\n\n请在主机安装 ffmpeg（及 ffprobe），保证运行后端的用户能执行「ffmpeg -version」；Docker 需在镜像内安装或挂载二进制，必要时在配置中指定 ffmpeg 绝对路径并重启服务。',
      showCancel: false
    })
  } else {
    uni.showToast({ title: res.message || '发起失败', icon: 'none' })
  }
}

const saveHighlightVideoToAlbum = async () => {
  // #ifdef MP-WEIXIN
  const path = props.modelValue?.highlightVideoUrl
  if (!path) return
  const fullUrl = imgUrl(path)
  savingHighlightVideo.value = true
  uni.showLoading({ title: '下载中', mask: true })
  try {
    const tempPath = await new Promise((resolve, reject) => {
      uni.downloadFile({
        url: fullUrl,
        success: (res) => {
          if (res.statusCode === 200 && res.tempFilePath) {
            resolve(res.tempFilePath)
          } else {
            reject(new Error(res.errMsg || `下载失败(${res.statusCode})`))
          }
        },
        fail: (err) => reject(err || new Error('downloadFile fail'))
      })
    })
    await new Promise((resolve, reject) => {
      uni.saveVideoToPhotosAlbum({
        filePath: tempPath,
        success: () => resolve(),
        fail: (err) => reject(err || new Error('saveVideo fail'))
      })
    })
    uni.showToast({ title: '已保存到相册', icon: 'success' })
  } catch (e) {
    const msg = e && (e.errMsg || e.message) ? String(e.errMsg || e.message) : ''
    if (msg.includes('auth deny') || msg.includes('authorize') || msg.includes('auth')) {
      uni.showModal({
        title: '需要相册权限',
        content: '保存视频需授权写入系统相册（用于留存服务回顾短片），请在设置中开启。',
        confirmText: '去设置',
        success: (m) => {
          if (m.confirm) uni.openSetting({})
        }
      })
    } else if (
      msg.includes('downloadFile:fail') ||
      msg.includes('domain') ||
      msg.includes('不在以下 downloadFile')
    ) {
      uni.showModal({
        title: '无法下载短片',
        content:
          '请登录微信公众平台 → 小程序 → 开发管理 → 开发设置，将视频文件所在域名（与接口域名一致，如 https://api.xxx.com）加入「downloadFile 合法域名」。',
        showCancel: false
      })
    } else {
      uni.showToast({ title: msg.length > 0 && msg.length < 52 ? msg : '保存失败', icon: 'none' })
    }
  } finally {
    savingHighlightVideo.value = false
    uni.hideLoading()
  }
  // #endif
}

const downloadHighlightVideoH5 = async () => {
  // #ifdef H5
  const path = props.modelValue?.highlightVideoUrl
  if (!path) return
  const fullUrl = imgUrl(path)
  const rawName = (props.modelValue?.petName || '宠物').trim() || '宠物'
  const safeName = rawName.replace(/[\\/:*?"<>|]/g, '_').slice(0, 32)
  savingHighlightVideo.value = true
  uni.showLoading({ title: '准备下载', mask: true })
  try {
    const res = await fetch(fullUrl, { mode: 'cors', credentials: 'omit' })
    if (!res.ok) throw new Error(`HTTP ${res.status}`)
    const blob = await res.blob()
    const objectUrl = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = objectUrl
    a.download = `服务回顾_${safeName}.mp4`
    a.rel = 'noopener'
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    URL.revokeObjectURL(objectUrl)
    uni.showToast({ title: '已开始下载', icon: 'success' })
  } catch {
    try {
      await new Promise((resolve, reject) => {
        uni.setClipboardData({
          data: fullUrl,
          success: () => resolve(),
          fail: (err) => reject(err)
        })
      })
      uni.showModal({
        title: '无法直接下载',
        content:
          '可能受跨域或浏览器策略限制。已将视频链接复制到剪贴板，可粘贴到地址栏打开；或使用右上角菜单分享本页。',
        showCancel: false
      })
    } catch {
      uni.showModal({
        title: '下载失败',
        content: '请稍后重试，或复制报告链接在系统浏览器中打开。',
        showCancel: false
      })
    }
  } finally {
    savingHighlightVideo.value = false
    uni.hideLoading()
  }
  // #endif
}

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

<style scoped>
.highlight-card {
  margin: 4px 16px 16px;
  background: linear-gradient(140deg, #ecfdf5 0%, #ffffff 55%);
  border: 1px solid #bbf7d0;
  border-radius: 16px;
  padding: 18px 16px 16px;
  box-shadow: 0 10px 24px rgba(22, 163, 74, 0.1);
}
.highlight-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}
.highlight-title {
  font-size: 16px;
  font-weight: 700;
  color: #166534;
}
.highlight-badge {
  font-size: 11px;
  color: #15803d;
  background: rgba(45, 185, 109, 0.15);
  padding: 2px 8px;
  border-radius: 999px;
}
.highlight-desc {
  font-size: 12px;
  color: #475569;
  line-height: 1.55;
  display: block;
}
.highlight-em {
  color: #166534;
  font-weight: 600;
}
.highlight-desc-ops {
  margin-top: 8px;
  font-size: 11px;
  color: #64748b;
  line-height: 1.45;
  display: block;
}
.highlight-fail-hint {
  display: block;
  margin-top: 8px;
  font-size: 11px;
  color: #b45309;
  line-height: 1.45;
}
.highlight-status {
  margin: 12px 0;
  padding: 12px;
  border-radius: 10px;
  font-size: 13px;
  line-height: 1.5;
}
.highlight-status--busy {
  background: #f0fdf4;
  border: 1px solid #86efac;
  color: #166534;
}
.highlight-processing-hint {
  display: block;
  margin-top: 6px;
  font-size: 11px;
  color: #15803d;
  opacity: 0.9;
}
.highlight-status--err {
  background: #fff7ed;
  border: 1px solid #fdba74;
  color: #9a3412;
}
.highlight-err-type {
  display: block;
  font-weight: 700;
  margin-bottom: 4px;
}
.highlight-err-msg {
  display: block;
  font-size: 12px;
  line-height: 1.45;
}
.highlight-meta {
  display: block;
  margin-top: 8px;
  font-size: 12px;
  color: #64748b;
  text-align: center;
}
.highlight-mp-bar {
  margin-top: 12px;
}
.highlight-btn--album {
  background: linear-gradient(135deg, #22c55e, #16a34a);
  color: #fff;
}
.highlight-mp-hint {
  display: block;
  margin-top: 8px;
  font-size: 11px;
  color: #64748b;
  line-height: 1.45;
}
.highlight-mp-tip {
  display: block;
  margin-top: 4px;
  font-size: 11px;
  color: #94a3b8;
}
.highlight-h5-bar {
  margin-top: 12px;
}
.highlight-mode-head {
  margin-bottom: 8px;
}
.highlight-mode-title {
  font-size: 13px;
  font-weight: 700;
  color: #166534;
}
.highlight-mode-sub {
  font-size: 11px;
  color: #64748b;
  margin-left: 4px;
}
.highlight-mode {
  display: flex;
  gap: 10px;
  margin-bottom: 12px;
}
.highlight-mode-item {
  flex: 1;
  text-align: center;
  padding: 10px 8px;
  border-radius: 10px;
  border: 1.5px solid #cbd5e1;
  background: #fff;
  font-size: 13px;
  font-weight: 600;
  color: #475569;
}
.highlight-mode-item.is-on {
  border-color: #16a34a;
  background: #ecfdf5;
  color: #166534;
}
.highlight-player {
  width: 100%;
  max-height: 420px;
  border-radius: 12px;
  margin-top: 8px;
  background: #0f172a;
}
.highlight-actions {
  margin-top: 14px;
}
.highlight-btn--block {
  width: 100%;
}
.highlight-btn {
  border: none;
  border-radius: 12px;
  padding: 12px 16px;
  font-size: 15px;
  font-weight: 600;
}
.highlight-btn[disabled] {
  opacity: 0.55;
}
</style>
