import pako from 'pako';

export class AsrClient {
  constructor (url) {
    this.URL = url;

    // 柚子
    // this.appid = "4200510043";
    // this.token = "BWP-R4zL035PE0rtyzdzU1UPxjc-9VTJ";
    // 测试
    this.appid = "4200510043";
    this.token = "BWP-R4zL035PE0rtyzdzU1UPxjc-9VTJ";

    this.sk = "TIlmvF4EeKEEfFs74eyyZ8Wn-kXn-WwJ";
    this.cluster = "volcengine_input_common";
    this.workflow = "audio_in,resample,partition,vad,fe,decode,itn";
    this.uid = "user_id";
    this.nbest = 1;
    this.show_utterances = true;
    this.result_type = "full";
    this.format = "wav";
    this.codec = "raw";
    this.sample_rate = 16000;
    this.channels = 1;
    this.bits = 16;
    this.authType = "TOKEN";
    this.socket = null;
    this.recv_latch = null;
    this.recv_timeout = 5;
    this.recv_suc = true;
    this.asr_response = new ASRResponse();
  }

  asr_sync_connect ($form='') {
		console.log('初始化接受参数：',$form)
		if($form){
			uni.setStorageSync('asytip',$form)
		}else{
			$form = uni.getStorageSync('asytip')
		}
    const self = this;
    try {
      // 构造参数消息
      const params_msg = self.construct_param();

      // 创建 WebSocket 连接
      self.socket = uni.connectSocket({
        // url: 'wss://openspeech.bytedance.com/api/v2/asr',
        url: self.URL + "?api_jwt=" + self.token,
        header: {
          'Authorization': 'Bearer; ' + self.token
        },
        binaryType: 'arraybuffer',
        success: () => {
          console.log('WebSocket 连接成功');
        },
        fail: (err) => {
          console.error('WebSocket 连接失败', err);
        }
      });

      console.log("1------------------------", self.socket);
      console.log("2------------------------", self.socket.readyState);

      // 监听WebSocket接收到的消息事件
      self.socket.onMessage(function (res) {
        console.log('收到服务器消息', res,'  whywhy:',$form);
        if (res.data instanceof ArrayBuffer) {
          const bytes = new Uint8Array(res.data);
          self.parse_response(bytes,$form)
        }
        // 处理接收到的消息
      });

      // 监听WebSocket连接打开事件
      self.socket.onOpen(function () {
        console.log('WebSocket连接已打开！');
        console.log("3------------------------", self.socket.readyState);
        self.socket.send({
          data: params_msg.buffer,
          success: () => {
            console.log('请求头发送成功');
          },
          fail: (err) => {
            console.error('完整的客户端请求发送失败', err);
            uni.$emit("asrEvent_close", {
              data: err.errMsg
            });
          }
        });
        // 连接打开后的操作
      });

      // 监听WebSocket连接关闭事件
      self.socket.onClose(function (res) {
        console.log('WebSocket已关闭！');
        // 连接关闭后的操作
      });



    } catch (error) {
      console.error("Error in asr_sync_connect:", error);
      return false;
    }
  }

  async asr_send (audio, is_last) {
    try {
      const self = this;
      const payload = self.construct_audio_payload(audio, is_last);
      return new Promise((resolve) => {
        if (!self.socket || self.socket.readyState !== 1) {
          console.error("WebSocket is not open");
          resolve(null);
          return;
        }

        self.socket.send({
          data: payload.buffer,
          success: () => {
            // console.log('完整的客户端请求发送成功',connected);
          },
          fail: (err) => {
            console.error('完整的客户端请求发送失败', err);
          }
        });
      });
    } catch (error) {
      console.error("Error in asr_send:", error);
      return null;
    }
  }

  asr_close () {
      console.log('inner asr_close');
    uni.$emit("ws_send_ready", {
      data: false,
      event: '主动关闭'
    });
    if (this.socket && this.socket.readyState === 1) {
      console.log('inner asr_closeasr_close');
      this.socket.close();
    }
  }

  gzip_compress (content) {
    // 使用 pako 库进行 GZIP 压缩
    return pako.gzip(content);
  }

  gzip_decompress (content) {
    // 使用 pako 库进行 GZIP 解压缩
    return pako.ungzip(content);
  }

  construct_param () {
    try {
      // 构造请求 ID
      const reqid = this.generateUUID();
      // 构造 ASR 参数对象
      const app = new App(this.appid, this.cluster, this.token);
      const user = new User(this.uid);
      const request = new Request(reqid, this.workflow, this.nbest, this.show_utterances, this.result_type, 1);
      const audio = new Audio(this.format, this.sample_rate, this.bits, this.channels);
      const asr_params = new ASRParams(app, user, request, audio);

      // 将对象序列化为 JSON 字符串
      const json_data = JSON.stringify(asr_params);

      // 将字符串转换为字节数组
      const bytes = [];
      for (let i = 0; i < json_data.length; i++) {
        const charCode = json_data.charCodeAt(i);
        bytes.push(charCode & 0xFF); // 获取字符的字节值（低 8 位）
      }

      // 创建 Int8Array
      const int8Array = new Int8Array(bytes);

      // 对数据进行 GZIP 压缩
      // const compressed = this.gzip_compress(int8Array);
      const compressed = int8Array;

      // 构造消息头部
      const header = new Int8Array(4);
      header[0] = (0b0001 << 4) | (4 >> 2); // 协议版本和头部长度
      header[1] = (0b0001 << 4) | 0b0000;  // 消息类型和序列号标志
      // header[2] = (0b0001 << 4) | 0b0001;  // 序列化方式和压缩方式
      header[2] = (0b0001 << 4) | 0b0000;  // 序列化方式和压缩方式
      header[3] = 0;                      // 保留字节

      // 构造 payload 长度部分
      const payload_len = compressed.length;
      const pl_byte = new Int8Array(4);
      new DataView(pl_byte.buffer).setUint32(0, payload_len, false); // 大端模式

      // 拼接所有部分
      const header_part = new Int8Array(header.buffer);
      const pl_part = new Int8Array(pl_byte.buffer);
      const compressed_part = new Int8Array(compressed);

      return this.concat_byte(header_part, pl_part, compressed);
    } catch (error) {
      console.error("Error constructing param:", error);
      return new Int8Array();
    }
  }

  parse_response (bytes,$form='') {
    try {
      var self = this;
      // 解析头部
      const header_len = (bytes[0] & 0x0f) << 2;
      const message_type = (bytes[1] & 0xf0) >>> 4;
      const message_type_flag = bytes[1] & 0x0f;
      const message_serial = (bytes[2] & 0xf0) >>> 4;
      const message_compress = bytes[2] & 0x0f;

      let payload_offset = header_len;
      let payload_len = 0;
      let payload = null;

      if (message_type === 0b1001) { // FULL_SERVER_RESPONSE
        // 读取 payload 长度
        const len_bytes = bytes.subarray(payload_offset, payload_offset + 4);
        payload_len = new DataView(len_bytes.buffer).getUint32(0, false); // 大端模式
        payload_offset += 4;
      } else if (message_type === 0b1011) { // SERVER_ACK
        // 读取序列号
        const seq_bytes = bytes.subarray(payload_offset, payload_offset + 4);
        const seq = new DataView(seq_bytes.buffer).getUint32(0, false); // 大端模式
        payload_offset += 4;

        // 如果还有数据，读取 payload 长度
        if (bytes.length > payload_offset + 4) {
          const len_bytes = bytes.subarray(payload_offset, payload_offset + 4);
          payload_len = new DataView(len_bytes.buffer).getUint32(0, false); // 大端模式
          payload_offset += 4;
        }
      } else if (message_type === 0b1111) { // ERROR_MESSAGE_FROM_SERVER
        // 读取错误码
        const error_code_bytes = bytes.subarray(payload_offset, payload_offset + 4);
        const error_code = new DataView(error_code_bytes.buffer).getUint32(0, false); // 大端模式
        payload_offset += 4;

        // 读取 payload 长度
        const len_bytes = bytes.subarray(payload_offset, payload_offset + 4);
        payload_len = new DataView(len_bytes.buffer).getUint32(0, false); // 大端模式
        payload_offset += 4;
      } else {
        console.error("Unsupported message type:", message_type);
        self.asr_close();
        return -1;
      }

      // 提取 payload 数据
      payload = bytes.subarray(payload_offset, payload_offset + payload_len);

      // 解压缩 payload
      if (message_compress === 0b0001) { // GZIP
        try {
          payload = this.gzip_decompress(payload);
        } catch (error) {
          console.error("Failed to decompress payload:", error);
          self.asr_close();
          return -1;
        }
      }

      // 反序列化 JSON 数据
      if (message_serial === 0b0001) { // JSON
        try {
          // const payload_text = new TextDecoder().decode(payload);
          // 将 Uint8Array 转换为字符串
          let jsonString = '';
          for (let i = 0; i < payload.length; i++) {
            jsonString += String.fromCharCode(payload[i]);
          }
          jsonString = decodeURIComponent(escape(jsonString));
          this.asr_response = JSON.parse(jsonString);
          // this.asr_response = JSON.parse(payload_text);
        } catch (error) {
          console.error("Failed to parse JSON payload:", error);
          self.asr_close();
          return -1;
        }
      }

      // 检查响应码
      console.error("ASR response code:", '--code:', this.asr_response.code, '--websokect status:', this.socket.readyState);
      if (this.asr_response.code == 1000 && this.asr_response.sequence == 1) {
        uni.$emit("ws_send_ready", {
          data: true,
          event: '1000'
        });
      } else if (this.asr_response.code == 400) {
        uni.$emit("ws_send_ready", {
          data: false,
          event: '400'
        });
      } else if (this.asr_response.code !== 1000) {
        uni.$emit("ws_send_ready", {
          data: false,
          event: '!=1000'
        });
        console.error("ASR response error:", this.asr_response, '--code:', this.asr_response.code, '--websokect status:', this.socket.readyState);
        //连接数太多

        let codeGroup = [
          { code: 1000, message: '成功' },
          { code: 1001, message: '请求参数无效' },
          { code: 1002, message: '无访问权限' },
          { code: 1003, message: '访问超频' },
          { code: 1004, message: '访问超额' },
          { code: 1005, message: '服务器繁忙' },
          { code: 1008, message: '保留号段' },
          { code: 1009, message: '保留号段' },
          { code: 1010, message: '音频过长' },
          { code: 1011, message: '音频过大' },
          { code: 1012, message: '音频格式无效' },
          { code: 1013, message: '音频静音' },
          { code: 1014, message: '保留号段' },
          { code: 1015, message: '保留号段' },
          { code: 1016, message: '保留号段' },
          { code: 1017, message: '保留号段' },
          { code: 1018, message: '保留号段' },
          { code: 1019, message: '保留号段' },
          { code: 1020, message: '识别等待超时' },
          { code: 1021, message: '识别处理超时' },
          { code: 1022, message: '识别错误' },
          { code: 1023, message: '保留号段' },
          { code: 1024, message: '保留号段' },
          { code: 1025, message: '保留号段' },
          { code: 1026, message: '保留号段' },
          { code: 1027, message: '保留号段' },
          { code: 1028, message: '保留号段' },
          { code: 1029, message: '保留号段' },
          { code: 1030, message: '保留号段' },
          { code: 1031, message: '保留号段' },
          { code: 1032, message: '保留号段' },
          { code: 1033, message: '保留号段' },
          { code: 1034, message: '保留号段' },
          { code: 1035, message: '保留号段' },
          { code: 1036, message: '保留号段' },
          { code: 1037, message: '保留号段' },
          { code: 1038, message: '保留号段' },
          { code: 1039, message: '保留号段' },
          { code: 1099, message: '未知错误' },
          { code: 400, message: '识别中' }
        ];

        uni.$emit("asrEvent_close", {
          data: codeGroup.filter(item => { return item.code == self.asr_response.code })[0].message,
          code: self.asr_response.code
        });
        self.asr_close();
        return -1;
      }
      //最后一条消息，关闭ws
      if (this.asr_response['sequence'] < 0) {
        self.asr_close();
				console.log("ASR response:", this.asr_response,' asr来源：',$form);
				uni.$emit('asrEvent_message', {
				  data: JSON.stringify(self.asr_response)
				});
      }
      
      return 0;
    } catch (error) {
      console.error("Error parsing response:", error);
      self.asr_close();
      //开启webSocket
      return -1;
    }
  }
  generateHeader (options) {
    const header = new Int8Array(4);
    header[0] = (options.version << 4) | options.headerSize;
    header[1] = (options.messageType << 4) | options.flags;
    header[2] = (options.serialMethod << 4) | options.compression;
    header[3] = options.reserved;
    return header;
  }

  construct_audio_payload (audio, is_last) {

    try {
      // 构造消息头部
      const header = new Int8Array(4);
      header[0] = (0b0001 << 4) | (4 >> 2); // 协议版本和头部长度
      header[1] = is_last
        ? (0b0010 << 4) | 0b0010 // AUDIO_ONLY_CLIENT_REQUEST 和 NEGATIVE_SEQUENCE_SERVER_ASSGIN
        : (0b0010 << 4) | 0b0000; // AUDIO_ONLY_CLIENT_REQUEST 和 NO_SEQUENCE_NUMBER
      header[2] = (0b0001 << 4) | 0b0001;  // 序列化方式和压缩方式
      header[2] = (0b0001 << 4) | 0b0000;  // 序列化方式和压缩方式
      header[3] = 0;                      // 保留字节
      // 对音频数据进行 GZIP 压缩
      // const compressed_audio = this.gzip_compress(audio);
      const compressed_audio = audio;
      console.log(is_last, audio, compressed_audio)
      // 构造 payload 长度部分
      const payload_len = compressed_audio.length;
      const pl_byte = new ArrayBuffer(4);
      new DataView(pl_byte).setUint32(0, payload_len, false); // 大端模式

      // 拼接所有部分
      const header_part = new Int8Array(header.buffer);
      const pl_part = new Int8Array(pl_byte);
      const compressed_part = new Int8Array(compressed_audio);

      return this.concat_byte(header_part, pl_part, compressed_part);
    } catch (error) {
      console.error("Error constructing audio payload:", error);
      return new Int8Array();
    }
  }

  generateUUID () {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
      const r = Math.random() * 16 | 0;
      const v = c === 'x' ? r : (r & 0x3 | 0x8);
      return v.toString(16);
    });
  }

  concat_byte (...arrays) {
    const totalLength = arrays.reduce((acc, arr) => acc + arr.length, 0);
    const result = new Int8Array(totalLength);
    let offset = 0;
    for (const arr of arrays) {
      result.set(arr, offset);
      offset += arr.length;
    }
    return result;
  }

  // 设置各种参数的方法
  setAppid (appid) {
    this.appid = appid;
  }

  setToken (token) {
    this.token = token;
  }

  setSk (sk) {
    this.sk = sk;
  }

  setCluster (cluster) {
    this.cluster = cluster;
  }

  setWorkflow (workflow) {
    this.workflow = workflow;
  }

  setUid (uid) {
    this.uid = uid;
  }

  setShow_utterances (show_utterances) {
    this.show_utterances = show_utterances;
  }

  setResult_type (result_type) {
    this.result_type = result_type;
  }

  setFormat (format) {
    this.format = format;
  }




  setSample_rate (sample_rate) {
    this.sample_rate = sample_rate;
  }

  setChannels (channels) {
    this.channels = channels;
  }

  setBits (bits) {
    this.bits = bits;
  }

  setAuthType (authType) {
    this.authType = authType;
  }

  getAsrResponse () {
    return this.asr_response;
  }
}


export class ASRResponse {
  constructor () {
    this.reqid = "unknown";
    this.code = 0;
    this.message = "";
    this.sequence = 0;
    this.result = [];
    this.addition = new Addition();
  }
}

export class Result {
  constructor () {
    this.text = "";
    this.confidence = 0;
    this.language = "";
    this.utterances = [];
    this.global_confidence = 0;
  }
}

export class Utterances {
  constructor () {
    this.text = "";
    this.start_time = 0;
    this.end_time = 0;
    this.definite = false;
    this.language = "";
    this.words = [];
  }
}

export class Words {
  constructor () {
    this.text = "";
    this.start_time = 0;
    this.end_time = 0;
    this.blank_duration = 0;
  }
}

export class Addition {
  constructor () {
    this.duration = "";
  }
}

export class ASRParams {
  constructor (app, user, request, audio) {
    this.app = app;
    this.user = user;
    this.request = request;
    this.audio = audio;
  }
}

export class App {
  constructor (appid, cluster, token) {
    this.appid = appid;
    this.cluster = cluster;
    this.token = token;
  }
}

export class User {
  constructor (uid) {
    this.uid = uid;
  }
}

export class Request {
  constructor (reqid, workflow, nbest, show_utterances, result_type, sequence) {
    this.reqid = reqid;
    this.workflow = workflow;
    this.nbest = nbest;
    this.show_utterances = show_utterances;
    this.result_type = result_type;
    this.sequence = sequence;
  }
}

export class Audio {
  constructor (format, rate, bits, channels) {
    this.format = format;
    this.rate = rate;
    this.bits = bits;
    this.channels = channels;
  }
}
export default {
  AsrClient
}