import axios from 'axios'
import Base64 from 'crypto-js/enc-base64'
import HmacSHA1 from 'crypto-js/hmac-sha1'
import { v4 as uuidv4 } from 'uuid'
import { useWebSocket } from '@vueuse/core'

// https://help.aliyun.com/zh/isi/developer-reference/websocket
export function useSpeechTranscriber({ onSentenceEnd }: { onSentenceEnd?: (result: string, data: any) => void }) {
  let audioContext: AudioContext
  let audioInput: MediaStreamAudioSourceNode

  const token = ref('')
  const isTranscriptionStarted = ref(false)

  const wsUrl = computed(() => {
    return `wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1?token=${token.value}`
  })

  const header = {
    appkey: import.meta.env.VITE_APP_KEY,
    namespace: 'SpeechTranscriber',
    task_id: uuidv4().replace(/-/g, ''),
    message_id: uuidv4().replace(/-/g, ''),
  }

  const { send, status, open } = useWebSocket(wsUrl, {
    autoConnect: false,
    immediate: false,
    onConnected: () => {
      console.log('语音识别连接成功')
      const startTranscriptionMessage = {
        header: { ...header, name: 'StartTranscription' },
        payload: {
          format: 'pcm',
          sample_rate: 16000,
          enable_intermediate_result: true,
          enable_punctuation_prediction: true,
          enable_inverse_text_normalization: true,
        },
      }
      send(JSON.stringify(startTranscriptionMessage))
    },
    onMessage: (ws, event) => {
      try {
        const data = JSON.parse(event.data)
        if (data.header.name === 'TranscriptionStarted') {
          isTranscriptionStarted.value = true
        }
        if (data.header.name === 'SentenceEnd') {
          // 一句话结束
          console.log(data.payload.result)
          onSentenceEnd?.(data.payload.result, data)
        }
      } catch (e) {
        console.log('error', e, event.data)
      }
    },
    onError: (ws, event) => {
      console.log('语音识别连接失败', event)
    },
    onDisconnected: (ws, event) => {
      console.log('语音识别连接断开', event)
    },
  })

  const start = async (stream: MediaStream) => {
    console.log('开始录音', stream)
    token.value = await getToken()
    open()
    try {
      // 创建 AudioContext 并设定采样率
      audioContext = new AudioContext({ sampleRate: 16000 })
      audioInput = audioContext.createMediaStreamSource(stream)

      // 加载 AudioWorkletProcessor 模块
      await audioContext.audioWorklet.addModule('/processor.js')

      // 创建自定义处理器节点
      const audioWorkletNode = new AudioWorkletNode(audioContext, 'pcm-processor')

      // 监听自定义消息
      audioWorkletNode.port.onmessage = (event) => {
        const inputData16 = event.data as ArrayBuffer
        if (status.value === 'OPEN' && isTranscriptionStarted.value) {
          send(inputData16) // 发送到后端或处理函数
        }
      }

      // 连接音频流到处理器
      audioInput.connect(audioWorkletNode)
      // 如果你不需要播放，可以不连接 destination
      // audioWorkletNode.connect(audioContext.destination);
    } catch (e) {
      console.error('录音失败: ' + e)
    }
  }

  const stop = () => {
    if (audioInput) {
      audioInput.disconnect()
    }

    if (audioContext) {
      audioContext.close()
    }
    const stopTranscriptionMessage = { header: { ...header, name: 'StopTranscription' } }
    send(JSON.stringify(stopTranscriptionMessage))
  }

  return { start, stop }
}

async function getToken() {
  const parameters: Record<string, string> = {
    AccessKeyId: import.meta.env.VITE_ACCESS_KEY_ID,
    Action: 'CreateToken',
    Format: 'JSON',
    RegionId: 'cn-shanghai',
    SignatureMethod: 'HMAC-SHA1',
    SignatureNonce: crypto.randomUUID(),
    SignatureVersion: '1.0',
    Timestamp: new Date().toISOString().replace(/\.\d{3}/, ''),
    Version: '2019-02-28',
  }

  const specialUrlEncode = (text: string) => {
    return encodeURIComponent(text).replace(/\+/g, '%20').replace(/\*/g, '%2A').replace(/%7E/g, '~')
  }

  const encodeParameters = (params: Record<string, string>) => {
    return Object.keys(params)
      .sort()
      .map((key) => `${specialUrlEncode(key)}=${specialUrlEncode(params[key])}`)
      .join('&')
  }
  const canonicalizedQueryString = encodeParameters(parameters)

  const stringToSign = 'GET' + '&' + specialUrlEncode('/') + '&' + specialUrlEncode(canonicalizedQueryString)

  const signature = Base64.stringify(HmacSHA1(stringToSign, `${import.meta.env.VITE_ACCESS_KEY_SECRET}&`))

  const encodedSignature = specialUrlEncode(signature)

  const url = `https://nls-meta.cn-shanghai.aliyuncs.com/?Signature=${encodedSignature}&${canonicalizedQueryString}`

  const response = await axios.get(url)
  return response.data.Token.Id
}
