<template>
  <view
    @click="playAudio"
    class="audio_message_container bg_container"
    :class="[isSender ? 'audio_message_container_self' : '']"
	style="position: relative; overflow: visible;">
	<view class="msg_arrow" :class="isSender ? 'msg_arrow_right' : 'msg_arrow_left'"></view>
    <view class="cricleplay" :class="[isSender ? 'cricleplay_self' : '']">
      <view class="play-icon-small" :class="[isSender ? 'play-icon-small-self' : '']"></view>
      <view class="play-icon-middle" :class="[isSender ? 'play-icon-middle-self' : '']" :style="{ opacity: (playing && animateStep < 2) ? 0 : 1 }"></view>
      <view class="play-icon-large" :class="[isSender ? 'play-icon-large-self' : '']" :style="{ opacity: (playing && animateStep < 3) ? 0 : 1 }"></view>
    </view>
    <text class="audio_duration">{{ message.soundElem.duration }}''</text>
    <view v-if="showUnreadIndicator" class="audio_unread_dot"></view>
  </view>
</template>

<script>
// 音频播放单例管理
let _audioContext = null;
let _currentVM = null;

function getAudioContext() {
  if (!_audioContext) {
    _audioContext = uni.createInnerAudioContext();
    // #ifdef APP-PLUS
    // 强制使用扬声器播放，忽略静音开关
    if (typeof uni.setInnerAudioOption === 'function') {
        uni.setInnerAudioOption({
            obeyMuteSwitch: false,
            speakerOn: true
        });
    }
    // #endif

    _audioContext.onPlay(() => {
      if (_currentVM) {
        _currentVM.playing = true;
        _currentVM.paused = false;
        // Animation is started immediately on click to reduce perceived latency
        if (_currentVM.animateStep === 3) { // Only start if not already running
             _currentVM.startAnimate();
        }
        _currentVM.startSafetyTimer();
      }
    });
    
    _audioContext.onPause(() => {
      if (_currentVM) {
        _currentVM.playing = false;
        _currentVM.paused = true;
        _currentVM.stopAnimate();
        _currentVM.stopSafetyTimer();
      }
    });
    
    _audioContext.onStop(() => {
      if (_currentVM) {
        _currentVM.playing = false;
        _currentVM.paused = false;
        _currentVM.stopAnimate();
        _currentVM.stopSafetyTimer();
      }
    });
    
    _audioContext.onEnded(() => {
      if (_currentVM) {
        _currentVM.playing = false;
        _currentVM.paused = false;
        _currentVM.stopAnimate();
        _currentVM.stopSafetyTimer();
      }
    });

    _audioContext.onTimeUpdate(() => {
      if (_currentVM && _currentVM.playing) {
        _currentVM.handleTimeUpdate();
      }
    });
    
    _audioContext.onError((err) => {
      console.error('Audio play error:', err);
      // 添加错误提示，方便用户感知
      uni.showToast({
          title: '播放失败',
          icon: 'none'
      });
      if (_currentVM) {
        _currentVM.playing = false;
        _currentVM.paused = false;
        _currentVM.stopAnimate();
        _currentVM.stopSafetyTimer();
      }
    });
  }
  return _audioContext;
}

export default {
  name: "AudioMessageRender",
  props: {
    isSender: Boolean,
    message: Object,
  },
  data() {
    return {
      playing: false,
      paused: false,
      hasRead: false,
      animateStep: 3,
      animateTimer: null,
      safetyTimer: null,
    };
  },
  mounted() {
    this.checkReadState();
  },
  watch: {
    message: {
      handler() {
        this.checkReadState();
      },
    },
    playing(val) {
      if (val) {
        this.startAnimate();
        this.startSafetyTimer();
      } else {
        this.stopAnimate();
        this.stopSafetyTimer();
      }
    },
  },
  beforeDestroy() {
    this.stopAnimate();
    this.stopSafetyTimer();
    // 如果当前组件正在播放，销毁前停止播放
    if (_currentVM === this) {
      const ctx = getAudioContext();
      if (ctx) {
        ctx.stop();
      }
      _currentVM = null;
    }
  },
  computed: {
    showUnreadIndicator() {
      return !this.isSender && !this.hasRead && Boolean(this.message?.clientMsgID);
    },
  },
  methods: {
    getStorageKey() {
      const userID = this.$store?.getters?.storeCurrentUserID || "";
      return `${userID || "GLOBAL"}_AudioMessageReadIds`;
    },
    loadReadIds() {
      const key = this.getStorageKey();
      const stored = uni.getStorageSync(key);
      const readIds = Array.isArray(stored) ? stored : [];
      return { key, readIds };
    },
    checkReadState() {
      const clientMsgID = this.message?.clientMsgID;
      if (!clientMsgID) {
        this.hasRead = true;
        return;
      }
      const { readIds } = this.loadReadIds();
      this.hasRead = readIds.includes(clientMsgID);
    },
    markAsRead() {
      const clientMsgID = this.message?.clientMsgID;
      if (this.hasRead || !clientMsgID) {
        return;
      }
      const { key, readIds } = this.loadReadIds();
      if (readIds.includes(clientMsgID)) {
        this.hasRead = true;
        return;
      }
      uni.setStorageSync(key, [...readIds, clientMsgID]);
      this.hasRead = true;
    },
    // 异步检查文件是否存在
    checkFileExist(path) {
      return new Promise((resolve) => {
        if (!path) {
            resolve(false);
            return;
        }

        // #ifdef APP-PLUS
        // 增加平台路径格式校验，防止跨平台同步的数据（如 Android 路径同步到 iOS）导致误判
        const platform = uni.getSystemInfoSync().platform;
        if (platform === 'ios') {
            // Android 特有路径特征
            if (path.includes('/storage/') || path.includes('/sdcard/') || path.includes('/Android/data/')) {
                console.log('Detected Android path on iOS, ignoring:', path);
                resolve(false);
                return;
            }
        }
        
        plus.io.resolveLocalFileSystemURL(path, () => {
          resolve(true);
        }, () => {
          // 尝试加上 file:// 前缀再试一次
          if (!path.startsWith('file://') && path.startsWith('/')) {
             plus.io.resolveLocalFileSystemURL('file://' + path, () => {
                resolve(true);
             }, () => {
                resolve(false);
             });
          } else {
             resolve(false);
          }
        });
        // #endif
        
        // #ifndef APP-PLUS
        // H5 或其他平台暂不支持本地文件检查，直接返回 true 尝试播放，或 false
        resolve(true);
        // #endif
      });
    },
    
    async playAudio() {
      const ctx = getAudioContext();
      
      // 如果点击的是当前正在播放/暂停的音频
      if (_currentVM === this) {
        if (this.playing) {
          ctx.pause();
          this.playing = false;
          this.paused = true;
        } else {
          ctx.play();
          this.playing = true;
          this.paused = false;
          this.markAsRead();
        }
        return;
      }

      // 如果有其他音频正在播放，先停止它
      if (_currentVM) {
        _currentVM.playing = false;
        _currentVM.paused = false;
        _currentVM.stopAnimate();
      }
      
      // 切换到当前音频
      _currentVM = this;
      
      const sourceUrl = this.message.soundElem.sourceUrl;
      const soundPath = this.message.soundElem.soundPath;
      console.log("qxj sourceUrl:"+sourceUrl+" soundPath:"+soundPath);
      // 1. 尝试播放本地文件 (通常是自己发送的消息)
      if (soundPath) {
        const exists = await this.checkFileExist(soundPath);
        if (exists) {
            this.playLocalFile(ctx, soundPath);
            return;
        }
      }

      // 2. 尝试播放缓存文件
      const cachedKey = 'AUDIO_CACHE_' + sourceUrl;
      const cachedPath = uni.getStorageSync(cachedKey);
      if (cachedPath) {
        const exists = await this.checkFileExist(cachedPath);
        if (exists) {
          this.playLocalFile(ctx, cachedPath);
          return;
        } else {
          // 缓存文件不存在（可能被系统清理），清除缓存记录
          uni.removeStorageSync(cachedKey);
        }
      }

      // 3. 优先尝试流式播放（减少等待时间），同时后台下载缓存
      if (sourceUrl) {
        // 立即播放远程地址
        ctx.src = sourceUrl;
        ctx.play();
        this.playing = true;
        this.paused = false;
        this.startAnimate(); // 立即开始动画，减少用户感知的延迟
        this.markAsRead();
        
        // 后台静默下载缓存
        this.downloadFileSilently(sourceUrl);
      } else {
        uni.showToast({ title: '无效的音频地址', icon: 'none' });
        this.playing = false;
        _currentVM = null;
      }
    },
    
    // 修复路径问题：确保本地路径有正确的前缀
    fixPath(path) {
        if (!path) return '';
        if (path.startsWith('http')) return path;
        // #ifdef APP-PLUS
        let fixedPath = path;
        // 处理 _doc/ 开头的相对路径
        if (fixedPath.startsWith('_doc/')) {
             fixedPath = plus.io.convertLocalFileSystemURL(fixedPath);
        }
        // 处理绝对路径
        if (fixedPath.startsWith('/')) {
             if (!fixedPath.startsWith('file://')) {
                 fixedPath = 'file://' + fixedPath;
             }
        }
        return fixedPath;
        // #endif
        return path;
    },
    
    playLocalFile(ctx, path) {
        const fixedPath = this.fixPath(path);
        // 避免重复赋值 src 导致重新加载
        if (ctx.src !== fixedPath) {
             ctx.src = fixedPath;
        }
        ctx.play();
        // 立即更新状态，确保UI响应
        this.playing = true;
        this.paused = false;
        this.startAnimate(); // 立即开始动画
        this.markAsRead();
    },

    downloadFileSilently(url) {
        // 检查是否已有缓存或正在下载
        if (uni.getStorageSync('AUDIO_CACHE_' + url)) return;
        
        uni.downloadFile({
            url: url,
            success: (res) => {
                if (res.statusCode === 200) {
                    uni.setStorageSync('AUDIO_CACHE_' + url, res.tempFilePath);
                }
            },
            fail: (err) => {
                console.error('Silent audio download failed:', err);
            }
        });
    },

    // 保留旧方法作为备用（如果需要）
    downloadAndPlay(ctx, url) {
        uni.downloadFile({
            url: url,
            success: (res) => {
                // 如果用户已经切换到其他音频，丢弃本次下载结果
                if (_currentVM !== this) return;

                if (res.statusCode === 200) {
                    const tempFilePath = res.tempFilePath;
                    // 缓存路径
                    uni.setStorageSync('AUDIO_CACHE_' + url, tempFilePath);
                    // 播放
                    ctx.src = tempFilePath;
                    ctx.play();
                    
                    // 立即更新状态
                    this.playing = true;
                    this.paused = false;
                    this.markAsRead();
                } else {
                    console.error('Audio download failed:', res);
                    uni.showToast({ title: '音频加载失败', icon: 'none' });
                    this.playing = false;
                    _currentVM = null;
                }
            },
            fail: (err) => {
                // 如果用户已经切换到其他音频，忽略错误
                if (_currentVM !== this) return;

                console.error('Audio download error:', err);
                uni.showToast({ title: '音频下载出错', icon: 'none' });
                this.playing = false;
                _currentVM = null;
            }
        });
    },

    startAnimate() {
      this.stopAnimate();
      this.animateStep = 1;
      this.animateTimer = setInterval(() => {
        this.animateStep++;
        if (this.animateStep > 3) {
          this.animateStep = 1;
        }
      }, 500);
    },

    stopAnimate() {
      if (this.animateTimer) {
        clearInterval(this.animateTimer);
        this.animateTimer = null;
      }
      this.animateStep = 3;
      // Force update by using $nextTick if available or simple assignment
      // In nvue/weex, sometimes direct assignment doesn't trigger UI update if value is same type but not detected change?
      // But here 3 should trigger opacity 1. 
      // Let's verify template logic: :style="{ opacity: animateStep >= 2 ? 1 : 0 }"
      // If animateStep is 3, opacity is 1. 
    },

    handleTimeUpdate() {
      const ctx = getAudioContext();
      if (!ctx) return;
      
      const duration = this.message.soundElem.duration;
      // Check if playback exceeded duration (with small tolerance)
      if (duration && ctx.currentTime >= duration + 0.5) {
        this.forceStop();
        return;
      }
      
      // Reset safety timer based on remaining time + buffer
      this.resetSafetyTimer(duration - ctx.currentTime);
    },
    
    startSafetyTimer() {
      const duration = this.message.soundElem.duration || 60;
      // Initial safe buffer: duration + 5s (to account for buffering)
      this.resetSafetyTimer(duration + 5);
    },
    
    stopSafetyTimer() {
      if (this.safetyTimer) {
        clearTimeout(this.safetyTimer);
        this.safetyTimer = null;
      }
    },
    
    resetSafetyTimer(seconds) {
      this.stopSafetyTimer();
      const ms = Math.max(1000, seconds * 1000 + 1000); // Minimum 1s, Buffer 1s
      this.safetyTimer = setTimeout(() => {
        this.forceStop();
      }, ms);
    },
    
    forceStop() {
      this.playing = false;
      this.paused = false;
      this.stopAnimate();
      this.stopSafetyTimer();
      
      if (_currentVM === this) {
        const ctx = getAudioContext();
        if (ctx) ctx.stop();
        _currentVM = null;
      }
    },
  },
};
</script>

<style lang="scss" scoped>
.bg_container {
  padding: 16rpx 24rpx;
  border-radius: 0rpx 12rpx 12rpx 12rpx;
  background-color: #ffffff;
}

.audio_wrapper {
  flex-direction: row;
  align-items: center;
}

.audio_wrapper_self {
  flex-direction: row-reverse;
}

.audio_message_container {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: center;
  padding: 16rpx 24rpx;
  border-radius: 0rpx 12rpx 12rpx 12rpx;
  border-width: 1px;
  border-style: solid;
  border-color: #E8EAEF;
  min-width: 140rpx;
  background-color: #ffffff;
  /* z-index: 1; */
}

.audio_message_container_self {
  flex-direction: row-reverse;
  border-radius: 12rpx 0 12rpx 12rpx;
  background-color: #95EC69 !important;
  border-color: #95EC69;
}

.audio_duration {
  font-size: 18px;
  color: #333333;
}
.msg_arrow {
	width: 0;
	height: 0;
	border-top: 12rpx solid transparent;
	border-bottom: 12rpx solid transparent;
	position: absolute;
	top: 22rpx;
}
.msg_arrow_left {
	left: -14rpx;
	border-right: 16rpx solid #ffffff;
}
.msg_arrow_right {
	right: -14rpx;
	border-left: 16rpx solid #95EC69;
}
.cricleplay {
  display: flex;
  flex-direction: row;
  align-items: center;
  margin-right: 12rpx;
  flex-shrink: 0;
}

.cricleplay_self {
  margin-right: 0;
  margin-left: 12rpx;
  flex-direction: row-reverse;
}

.play-icon-small {
  width: 7px;
  height: 5px;
  flex-shrink: 0;
  border-style: solid;
  border-width: 1px;
  border-radius: 50%;
  border-color: transparent;
  border-right-color: #3870e4;
}

.play-icon-small-self {
  border-color: transparent;
  border-left-color: #000000;
  border-right-color: transparent;
}

.play-icon-middle {
  width: 10px;
  height: 10px;
  flex-shrink: 0;
  border-style: solid;
  border-width: 1px;
  border-radius: 50%;
  border-color: transparent;
  border-right-color: #3870e4;
  margin-left: -5px;
  opacity: 1;
}

.play-icon-middle-self {
  border-color: transparent;
  border-left-color: #000000;
  border-right-color: transparent;
  margin-left: 0;
  margin-right: -5px;
}

.play-icon-large {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  border-style: solid;
  border-width: 1px;
  border-radius: 50%;
  border-color: transparent;
  border-right-color: #3870e4;
  margin-left: -15px;
  opacity: 1;
}

.play-icon-large-self {
  border-color: transparent;
  border-left-color: #000000;
  border-right-color: transparent;
  margin-left: 0;
  margin-right: -15px;
}

/* Animations in NVUE are limited, using opacity might work but @keyframes are not fully supported in all nvue modes. 
   Ideally should use bindingx or weex animation module. 
   For now, we remove complex animation or keep it simple. */



.audio_unread_dot {
  width: 14rpx;
  height: 14rpx;
  border-radius: 50%;
  background-color: #FF4D4F;
  margin-left: 10rpx;
}

</style>
