// ============================================================
// BROWSER WHISPER — client-side transcription, zero server calls
// Uses @xenova/transformers running Whisper in WebAssembly.
// Audio is processed entirely in-browser, never transmitted.
//
// Models (cached in IndexedDB after first download):
//   Xenova/whisper-tiny.en  — ~39MB  — fastest, ~2-3s/clip
//   Xenova/whisper-base.en  — ~74MB  — balanced, ~3-5s/clip
//   Xenova/whisper-small.en — ~244MB — best quality, ~6-10s/clip
// ============================================================

(function() {

  var STORAGE_ENABLED = 'ped_browser_whisper';
  var STORAGE_MODEL   = 'ped_whisper_model';
  var DEFAULT_MODEL   = 'Xenova/whisper-tiny.en';

  var _worker   = null;
  var _ready    = false;
  var _loading  = false;
  var _modelLoaded = null;
  var _pending  = null; // { resolve, reject }

  window.BrowserWhisper = {

    isSupported: function() {
      return typeof Worker !== 'undefined' &&
             typeof AudioContext !== 'undefined' &&
             typeof WebAssembly !== 'undefined';
    },

    isEnabled: function() {
      try { return localStorage.getItem(STORAGE_ENABLED) === '1'; } catch(e) { return false; }
    },

    setEnabled: function(val) {
      try { localStorage.setItem(STORAGE_ENABLED, val ? '1' : '0'); } catch(e) {}
    },

    getModel: function() {
      try { return localStorage.getItem(STORAGE_MODEL) || DEFAULT_MODEL; } catch(e) { return DEFAULT_MODEL; }
    },

    setModel: function(m) {
      try { localStorage.setItem(STORAGE_MODEL, m); } catch(e) {}
      // Force reload next time
      _ready = false;
      _modelLoaded = null;
    },

    // Pre-warm: load model in background before first recording
    preload: function(onProgress) {
      if (!this.isSupported() || !this.isEnabled()) return;
      _initWorker(this.getModel(), onProgress || function() {});
    },

    // Transcribe a Blob (WebM, WAV, etc.)
    transcribe: function(blob) {
      var self = this;
      if (!this.isSupported()) return Promise.reject(new Error('WebAssembly not supported'));
      if (!this.isEnabled()) return Promise.reject(new Error('Browser Whisper not enabled'));

      return _blobToFloat32(blob).then(function(float32) {
        return new Promise(function(resolve, reject) {
          var model = self.getModel();

          // If worker is ready with same model, send immediately
          if (_ready && _modelLoaded === model) {
            _pending = { resolve: resolve, reject: reject };
            _worker.postMessage({ type: 'transcribe', audio: float32, model: model }, [float32.buffer]);
            return;
          }

          // Need to (re)load model first
          _pending = { resolve: resolve, reject: reject };
          _initWorker(model, function() {}, function() {
            _worker.postMessage({ type: 'transcribe', audio: float32, model: model }, [float32.buffer]);
          });
        });
      });
    }
  };

  // ── Internal ──────────────────────────────────────────────

  function _initWorker(model, onProgress, onReady) {
    if (_loading && _modelLoaded === model) return; // already loading same model

    if (_worker) { _worker.terminate(); _worker = null; }
    _ready = false;
    _loading = true;
    _modelLoaded = model;

    _worker = new Worker('/js/whisperWorker.js');

    _worker.addEventListener('message', function(e) {
      var d = e.data;

      if (d.type === 'loading') {
        if (onProgress) onProgress('Downloading Whisper model (' + d.model.split('/').pop() + ')...', 0);
      }
      if (d.type === 'progress') {
        if (onProgress) onProgress(d.file.split('/').pop() || 'model', d.progress);
      }
      if (d.type === 'ready') {
        _ready = true;
        _loading = false;
        if (onProgress) onProgress('', 100);
        if (onReady) onReady();
      }
      if (d.type === 'result') {
        if (_pending) { _pending.resolve(d.text); _pending = null; }
      }
      if (d.type === 'error') {
        _loading = false;
        _ready = false;
        console.error('[BrowserWhisper] Worker error:', d.message);
        // Show user-friendly message
        if (typeof showToast === 'function') {
          showToast('Browser Whisper blocked by network/firewall. Using server transcription.', 'warning');
        }
        if (_pending) { _pending.reject(new Error(d.message)); _pending = null; }
        if (onProgress) onProgress('', 0);
      }
    });

    _worker.addEventListener('error', function(err) {
      _loading = false;
      _ready = false;
      console.error('[BrowserWhisper] Worker crashed:', err);
      if (typeof showToast === 'function') {
        showToast('Browser Whisper unavailable. Using server transcription.', 'warning');
      }
      if (_pending) { _pending.reject(err); _pending = null; }
      if (onProgress) onProgress('', 0);
    });

    _worker.postMessage({ type: 'load', model: model });
  }

  // Convert audio Blob → Float32Array at 16kHz mono (what Whisper expects)
  function _blobToFloat32(blob) {
    return blob.arrayBuffer().then(function(buf) {
      var ctx = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: 16000 });
      return ctx.decodeAudioData(buf).then(function(audioBuffer) {
        ctx.close();
        return audioBuffer.getChannelData(0); // mono
      }).catch(function(err) {
        ctx.close();
        throw new Error('Audio decode failed: ' + err.message);
      });
    });
  }

})();
