/**
 * VoiceInput - 独立语音转文字 SDK
 * 基于讯飞语音听写 WebAPI（流式版）
 * 
 * 零依赖，纯原生 JS，任何静态 HTML 页面都能用
 * 
 * 用法：
 *   const voice = new VoiceInput({
 *     onResult: (text) => { textarea.value = text; }
 *   });
 *   voice.start();  // 开始录音
 *   voice.stop();   // 停止录音
 */
(function (global) {
  'use strict';

  // ============ 默认配置 ============
  var DEFAULT_CONFIG = {
    APPID: '029f22a0',
    APISecret: 'Njk1Y2VlYjhhODhkNTYxM2Y3YTE4YzRl',
    APIKey: '60f2b651b26629030c861e5b2c96102d',
    url: 'wss://iat.xf-yun.com/v1',
    host: 'iat.xf-yun.com',
    language: 'zh_cn',
    accent: 'mandarin'
  };

  // ============ 主线程音频处理（避免 CSP 限制 blob Worker）============
  var _processorSampleRate = 44100;

  function to16kHz(data) {
    var fitCount = Math.round(data.length * (16000 / _processorSampleRate));
    if (fitCount <= 1) return new Float32Array(0);
    var newData = new Float32Array(fitCount);
    var springFactor = (data.length - 1) / (fitCount - 1);
    newData[0] = data[0];
    for (var i = 1; i < fitCount - 1; i++) {
      var tmp = i * springFactor;
      var before = Math.floor(tmp);
      var after = Math.ceil(tmp);
      newData[i] = data[before] + (data[after] - data[before]) * (tmp - before);
    }
    newData[fitCount - 1] = data[data.length - 1];
    return newData;
  }

  function to16BitPCM(input) {
    var buf = new ArrayBuffer(input.length * 2);
    var view = new DataView(buf);
    for (var i = 0; i < input.length; i++) {
      var s = Math.max(-1, Math.min(1, input[i]));
      view.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
    }
    return buf;
  }

  function processAudioData(float32Array) {
    var output = to16kHz(float32Array);
    if (output.length === 0) return null;
    var pcm = to16BitPCM(output);
    var arr = [];
    var bytes = new Uint8Array(pcm);
    for (var i = 0; i < bytes.length; i++) arr.push(bytes[i]);
    return arr;
  }

  // ============ 工具函数 ============

  // 使用浏览器原生 SubtleCrypto 做 HMAC-SHA256（无需 CryptoJS）
  function hmacSHA256(key, message) {
    var enc = new TextEncoder();
    return crypto.subtle.importKey(
      'raw', enc.encode(key), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']
    ).then(function (cryptoKey) {
      return crypto.subtle.sign('HMAC', cryptoKey, enc.encode(message));
    }).then(function (sig) {
      return arrayBufferToBase64(sig);
    });
  }

  function arrayBufferToBase64(buffer) {
    var binary = '';
    var bytes = new Uint8Array(buffer);
    for (var i = 0; i < bytes.length; i++) {
      binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
  }

  function toBase64(buffer) {
    var binary = '';
    var bytes = new Uint8Array(buffer);
    for (var i = 0; i < bytes.length; i++) {
      binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
  }

  function base64ToUtf8(base64Str) {
    try {
      var binaryStr = atob(base64Str);
      var bytes = new Uint8Array(binaryStr.length);
      for (var i = 0; i < binaryStr.length; i++) {
        bytes[i] = binaryStr.charCodeAt(i);
      }
      return new TextDecoder('utf-8').decode(bytes);
    } catch (e) {
      return '';
    }
  }

  // ============ VoiceInput 类 ============

  function VoiceInput(opts) {
    opts = opts || {};

    // 配置
    this.APPID = opts.APPID || DEFAULT_CONFIG.APPID;
    this.APISecret = opts.APISecret || DEFAULT_CONFIG.APISecret;
    this.APIKey = opts.APIKey || DEFAULT_CONFIG.APIKey;
    this.url = opts.url || DEFAULT_CONFIG.url;
    this.host = opts.host || DEFAULT_CONFIG.host;
    this.language = opts.language || DEFAULT_CONFIG.language;
    this.accent = opts.accent || DEFAULT_CONFIG.accent;

    // 回调
    this.onResult = opts.onResult || function () {};
    this.onStatusChange = opts.onStatusChange || function () {};
    this.onError = opts.onError || function () {};

    // 内部状态
    this.status = 'idle'; // idle | connecting | recording | stopping | stopped
    this.audioData = [];
    this.textSegments = [];
    this.webSocket = null;
    this.audioContext = null;
    this.scriptProcessor = null;
    this.mediaSource = null;
    this.streamRef = null;
    this.sendInterval = null;
    this.resultTimeout = null;
    this.worker = null;
  }

  VoiceInput.prototype._initProcessor = function () {
    // 主线程处理，无需 Worker
    this._processorReady = true;
  };

  VoiceInput.prototype._processAudio = function (float32Array) {
    var result = processAudioData(float32Array);
    if (result) {
      this.audioData.push.apply(this.audioData, result);
    }
  };

  VoiceInput.prototype._setStatus = function (status) {
    if (this.status !== status) {
      var old = this.status;
      this.status = status;
      this.onStatusChange(old, status);
    }
  };

  VoiceInput.prototype._getWebSocketUrl = function () {
    var self = this;
    var date = new Date().toUTCString();
    var signatureOrigin = 'host: ' + self.host + '\ndate: ' + date + '\nGET /v1 HTTP/1.1';

    return hmacSHA256(self.APISecret, signatureOrigin).then(function (signature) {
      var authOrigin = 'api_key="' + self.APIKey + '", algorithm="hmac-sha256", headers="host date request-line", signature="' + signature + '"';
      var authorization = btoa(authOrigin);
      return self.url + '?authorization=' + authorization + '&date=' + encodeURIComponent(date) + '&host=' + self.host;
    });
  };

  VoiceInput.prototype.start = function () {
    if (this.status === 'recording' || this.status === 'connecting') return;

    var self = this;
    this.audioData = [];
    this.textSegments = [];
    this._setStatus('connecting');

    // 获取麦克风
    if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
      this.onError('浏览器不支持录音');
      this._setStatus('idle');
      return;
    }

    navigator.mediaDevices.getUserMedia({ audio: true }).then(function (stream) {
      self.streamRef = stream;

      // 初始化音频上下文
      var AudioCtx = window.AudioContext || window.webkitAudioContext;
      self.audioContext = new AudioCtx();
      self.audioContext.resume();

      // 告诉处理器实际采样率
      _processorSampleRate = self.audioContext.sampleRate;

      // 设置音频处理
      self.scriptProcessor = self.audioContext.createScriptProcessor(0, 1, 1);
      self.scriptProcessor.onaudioprocess = function (event) {
        if (self.status === 'recording') {
          self._processAudio(event.inputBuffer.getChannelData(0));
        }
      };
      self.mediaSource = self.audioContext.createMediaStreamSource(stream);
      self.mediaSource.connect(self.scriptProcessor);
      self.scriptProcessor.connect(self.audioContext.destination);

      // 连接 WebSocket
      self._connectWebSocket();
    }).catch(function (err) {
      self.onError('麦克风权限获取失败: ' + err.message);
      self._setStatus('idle');
    });
  };

  VoiceInput.prototype._connectWebSocket = function () {
    var self = this;

    this._getWebSocketUrl().then(function (url) {
      var ws = new WebSocket(url);
      self.webSocket = ws;

      var timeout = setTimeout(function () {
        if (ws.readyState === WebSocket.CONNECTING) {
          ws.close();
          self.onError('WebSocket 连接超时');
          self._cleanup();
        }
      }, 5000);

      ws.onopen = function () {
        clearTimeout(timeout);
        self._setStatus('recording');
        setTimeout(function () { self._startSending(); }, 300);
      };

      ws.onmessage = function (event) {
        self._handleResult(event.data);
      };

      ws.onerror = function () {
        clearTimeout(timeout);
        self.onError('WebSocket 连接错误');
        self._cleanup();
      };

      ws.onclose = function () {
        clearTimeout(timeout);
        if (self.status !== 'idle') {
          self._setStatus('idle');
        }
      };
    }).catch(function (err) {
      self.onError('签名失败: ' + err);
      self._cleanup();
    });
  };

  VoiceInput.prototype._startSending = function () {
    var self = this;
    if (!this.webSocket || this.webSocket.readyState !== 1) return;

    // 发送首帧（带参数）
    var firstAudio = this.audioData.splice(0, 1280);
    if (firstAudio.length === 0) firstAudio = [0]; // 保底

    this.webSocket.send(JSON.stringify({
      header: { app_id: this.APPID, status: 0 },
      parameter: {
        iat: {
          domain: 'slm',
          language: this.language,
          accent: this.accent,
          eos: 6000,
          vinfo: 1,
          dwa: 'wpgs',
          result: { encoding: 'utf8', compress: 'raw', format: 'json' }
        }
      },
      payload: {
        audio: {
          encoding: 'raw', sample_rate: 16000, channels: 1, bit_depth: 16,
          seq: 1, status: 0, audio: toBase64(firstAudio)
        }
      }
    }));

    // 每 40ms 发送后续帧
    this.sendInterval = setInterval(function () {
      if (!self.webSocket || self.webSocket.readyState !== 1 || self.status === 'idle') {
        clearInterval(self.sendInterval);
        self.sendInterval = null;
        return;
      }
      // 防数据堆积
      if (self.audioData.length > 10000) {
        self.audioData = self.audioData.slice(-5000);
      }
      var chunk = self.audioData.splice(0, 1280);
      if (chunk.length === 0) return;

      self.webSocket.send(JSON.stringify({
        header: { app_id: self.APPID, status: 1 },
        payload: {
          audio: { status: 1, audio: toBase64(chunk) }
        }
      }));
    }, 40);
  };

  VoiceInput.prototype._handleResult = function (data) {
    try {
      var json = JSON.parse(data);
      if (json.payload && json.payload.result) {
        var text = base64ToUtf8(json.payload.result.text);
        var result = JSON.parse(text);
        if (result.ws) {
          var current = result.ws.map(function (ws) {
            return ws.cw.map(function (cw) { return cw.w; }).join('');
          }).join('');
          this.textSegments.push({ pgs: result.pgs, text: current });
          var finalText = this._concatText(this.textSegments);
          this.onResult(finalText);
        }
      }
      // status=2 表示服务端已返回全部结果
      if (json.header && json.header.status === 2) {
        this._cleanupWebSocket();
      }
    } catch (e) {
      // 解析失败忽略
    }
  };

  VoiceInput.prototype._concatText = function (data) {
    var result = '', currentSentence = '', lastRpl = '';
    data.forEach(function (item) {
      if (item.pgs === 'apd') {
        result += lastRpl || currentSentence;
        currentSentence = item.text;
        lastRpl = '';
      } else if (item.pgs === 'rpl') {
        lastRpl = item.text;
      }
    });
    return result + (lastRpl || currentSentence);
  };

  VoiceInput.prototype.stop = function () {
    if (this.status !== 'recording') return;

    this._setStatus('stopping');

    // 停止定时发送
    if (this.sendInterval) {
      clearInterval(this.sendInterval);
      this.sendInterval = null;
    }

    // 暂停音频采集
    if (this.audioContext) {
      this.audioContext.suspend();
    }

    // 发送尾帧
    if (this.webSocket && this.webSocket.readyState === WebSocket.OPEN) {
      this.webSocket.send(JSON.stringify({
        header: { app_id: this.APPID, status: 2 },
        payload: {
          audio: {
            encoding: 'raw', sample_rate: 16000, channels: 1, bit_depth: 16,
            seq: 999, status: 2, audio: ''
          }
        }
      }));

      // 3秒超时强制关闭
      var self = this;
      this.resultTimeout = setTimeout(function () {
        self._cleanupWebSocket();
      }, 3000);
    } else {
      this._cleanup();
    }
  };

  VoiceInput.prototype._cleanupWebSocket = function () {
    if (this.resultTimeout) {
      clearTimeout(this.resultTimeout);
      this.resultTimeout = null;
    }
    if (this.webSocket) {
      this.webSocket.close();
      this.webSocket = null;
    }
    this._stopAudioStream();
    this._setStatus('idle');
  };

  VoiceInput.prototype._stopAudioStream = function () {
    if (this.streamRef) {
      this.streamRef.getTracks().forEach(function (t) { t.stop(); });
      this.streamRef = null;
    }
    if (this.scriptProcessor) {
      this.scriptProcessor.disconnect();
      this.scriptProcessor = null;
    }
    if (this.mediaSource) {
      this.mediaSource.disconnect();
      this.mediaSource = null;
    }
    if (this.audioContext) {
      this.audioContext.close();
      this.audioContext = null;
    }
  };

  VoiceInput.prototype._cleanup = function () {
    if (this.sendInterval) {
      clearInterval(this.sendInterval);
      this.sendInterval = null;
    }
    this._cleanupWebSocket();
  };

  VoiceInput.prototype.destroy = function () {
    this._cleanup();
  };

  // 暴露到全局
  global.VoiceInput = VoiceInput;

})(typeof window !== 'undefined' ? window : this);
