<template>
  <div
    :class="{
      'message-input-audio': true,
      'message-input-audio-open': isAudioTouchBarShow,
    }"
  >
    <Icon
      class="audio-message-icon"
      :file="audioIcon"
      :size="'23px'"
      :hotAreaSize="'3px'"
      @onClick="switchAudio"
    />
    <view
      v-if="props.isEnableAudio"
      class="audio-input-touch-bar"
      @touchstart="handleTouchStart"
      @longpress="handleLongPress"
      @touchmove="handleTouchMove"
      @touchend="handleTouchEnd"
    >
      <span>{{ TUITranslateService.t(`TUIChat.${touchBarText}`) }}</span>
      <view
        v-if="isRecording"
        class="record-modal"
      >
        <div class="red-mask" />
        <view class="float-element moving-slider" />
        <view class="float-element modal-title">
          {{ TUITranslateService.t(`TUIChat.${modalText}`) }}
        </view>
      </view>
    </view>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from '../../../adapter-vue';
import {
  TUIStore,
  StoreName,
  TUIChatService,
  SendMessageParams,
  IConversationModel,
  TUITranslateService,
} from '@tencentcloud/chat-uikit-engine';
import { TUIGlobal } from '@tencentcloud/universal-api';
import { throttle } from 'lodash';
import Icon from '../../common/Icon.vue';
import audioIcon from '../../../assets/icon/audio.svg';
import { Toast, TOAST_TYPE } from '../../common/Toast/index';
import { isEnabledMessageReadReceiptGlobal } from '../utils/utils';
import { InputDisplayType } from '../../../interface';

interface IProps {
  isEnableAudio: boolean;
}

interface IEmits {
  (e: 'changeDisplayType', type: InputDisplayType): void;
}

interface RecordResult {
  tempFilePath: string;
  duration?: number;
  fileSize?: number;
}

type TouchBarText = '按住说话' | '抬起发送' | '抬起取消';
type ModalText = '正在录音' | '继续上滑可取消' | '松开手指 取消发送';

const emits = defineEmits<IEmits>();
const props = withDefaults(defineProps<IProps>(), {
  isEnableAudio: false,
});

let recordTime: number = 0;
let isManualCancelBySlide = false;
let recordTimer: number | undefined;
let firstTouchPageY: number = -1;
let isFingerTouchingScreen = false;
let isFirstAuthrizedRecord = false;
const recorderManager = TUIGlobal?.getRecorderManager();

const isRecording = ref(false);
const touchBarText = ref<TouchBarText>('按住说话');
const modalText = ref<ModalText>('正在录音');
const isAudioTouchBarShow = ref<boolean>(false);
const currentConversation = ref<IConversationModel>();

const recordConfig = {
  // Duration of the recording, in ms, with a maximum value of 600000 (10 minutes)
  duration: 60000,
  // Sampling rate
  sampleRate: 44100,
  // Number of recording channels
  numberOfChannels: 1,
  // Encoding bit rate
  encodeBitRate: 192000,
  // Audio format
  // Select this format to create audio messages that can be interoperable across all chat platforms (Android, iOS, WeChat Mini Programs, and Web).
  format: 'mp3',
};

function switchAudio() {
  emits('changeDisplayType', props.isEnableAudio ? 'editor' : 'audio');
}

onMounted(() => {
  // Register events for the audio recording manager
  recorderManager.onStart(onRecorderStart);
  recorderManager.onStop(onRecorderStop);
  recorderManager.onError(onRecorderError);

  TUIStore.watch(StoreName.CONV, {
    currentConversation: onCurrentConverstaionUpdated,
  });
});

onUnmounted(() => {
  TUIStore.unwatch(StoreName.CONV, {
    currentConversation: onCurrentConverstaionUpdated,
  });
});

function onCurrentConverstaionUpdated(conversation: IConversationModel) {
  currentConversation.value = conversation;
}

function initRecorder() {
  initRecorderData();
  initRecorderView();
}

function initRecorderView() {
  isRecording.value = false;
  touchBarText.value = '按住说话';
  modalText.value = '正在录音';
}

function initRecorderData(options?: { hasError: boolean }) {
  clearInterval(recordTimer);
  recordTimer = undefined;
  recordTime = 0;
  firstTouchPageY = -1;
  isManualCancelBySlide = false;
  if (!options?.hasError) {
    recorderManager.stop();
  }
}

function handleTouchStart() {
  if (isFingerTouchingScreen) {
    // Compatibility: Ignore the recording generated by the user's first authorization on the APP.
    isFirstAuthrizedRecord = true;
  }
}

function handleLongPress() {
  isFingerTouchingScreen = true;
  recorderManager.start(recordConfig);
}

const handleTouchMove = throttle(function (e) {
  if (isRecording.value) {
    const pageY = e.changedTouches[e.changedTouches.length - 1].pageY;
    if (firstTouchPageY < 0) {
      firstTouchPageY = pageY;
    }
    const offset = (firstTouchPageY as number) - pageY;
    if (offset > 150) {
      touchBarText.value = '抬起取消';
      modalText.value = '松开手指 取消发送';
      isManualCancelBySlide = true;
    } else if (offset > 50) {
      touchBarText.value = '抬起发送';
      modalText.value = '继续上滑可取消';
      isManualCancelBySlide = false;
    } else {
      touchBarText.value = '抬起发送';
      modalText.value = '正在录音';
      isManualCancelBySlide = false;
    }
  }
}, 100);

function handleTouchEnd() {
  isFingerTouchingScreen = false;
  recorderManager.stop();
}

function onRecorderStart() {
  if (!isFingerTouchingScreen) {
    // If recording starts but the finger leaves the screen,
    // it means that the initial authorization popup interrupted the recording and it should be ignored.
    isFirstAuthrizedRecord = true;
    recorderManager.stop();
    return;
  }
  recordTimer = setInterval(() => {
    recordTime += 1;
  }, 1000);
  touchBarText.value = '抬起发送';
  isRecording.value = true;
}

function onRecorderStop(res: RecordResult) {
  if (isFirstAuthrizedRecord) {
    // Compatibility: Ignore the recording generated by the user's first authorization on WeChat. This is not applicable to the APP.
    isFirstAuthrizedRecord = false;
    initRecorder();
    return;
  }
  if (isManualCancelBySlide || !isRecording.value) {
    initRecorder();
    return;
  }
  clearInterval(recordTimer);
  /**
   * Compatible with uniapp for building apps
   * Compatible with uniapp voice messages without duration
   * Duration and fileSize need to be supplemented by the user
   * File size = (Audio bitrate) * Length of time (in seconds) / 8
   * res.tempFilePath stores the temporary path of the recorded audio file
  */
  const tempFilePath = res.tempFilePath;
  const duration = res.duration ? res.duration : recordTime * 1000;
  const fileSize = res.fileSize ? res.fileSize : ((48 * recordTime) / 8) * 1024;

  if (duration < 1000) {
    Toast({
      message: '录音时间太短',
      type: TOAST_TYPE.NORMAL,
      duration: 1500,
    });
  } else {
    const options = {
      to:
        currentConversation?.value?.groupProfile?.groupID
        || currentConversation?.value?.userProfile?.userID,
      conversationType: currentConversation?.value?.type,
      payload: { file: { duration, tempFilePath, fileSize } },
      needReadReceipt: isEnabledMessageReadReceiptGlobal(),
    } as SendMessageParams;
    TUIChatService?.sendAudioMessage(options);
  }
  initRecorder();
}

function onRecorderError() {
  initRecorderData({ hasError: true });
  initRecorderView();
}
</script>

<style lang="scss" scoped>
@import "../../../assets/styles/common";

.message-input-audio {
  display: flex;
  flex-direction: row;
  align-items: center;

  .audio-message-icon {
    margin-right: 3px;
  }

  .audio-input-touch-bar {
    height: 39px;
    flex: 1;
    border-radius: 10px;
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
    background-color: #fff;

    .record-modal {
      height: 300rpx;
      width: 60vw;
      background-color: rgba(0, 0, 0, 0.8);
      position: fixed;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      z-index: 9999;
      border-radius: 24rpx;
      display: flex;
      flex-direction: column;
      overflow: hidden;

      .red-mask {
        position: absolute;
        inset: 0;
        background-color: rgba(#ff3e48, 0.5);
        opacity: 0;
        transition: opacity 10ms linear;
        z-index: 1;
      }

      .moving-slider {
        margin: 10vw;
        width: 40rpx;
        height: 16rpx;
        border-radius: 4rpx;
        background-color: #006fff;
        animation: loading 1s ease-in-out infinite alternate;
        z-index: 2;
      }

      .float-element {
        position: relative;
        z-index: 2;
      }
    }

    @keyframes loading {
      0% {
        transform: translate(0, 0);
      }

      100% {
        transform: translate(30vw, 0);
        background-color: #f5634a;
        width: 40px;
      }
    }

    .modal-title {
      text-align: center;
      color: #fff;
    }
  }

  &-open {
    flex: 1;
  }
}
</style>
